Vårt delingshjørne
Lars Espen
Nordhus
18. desember 2024
Problemet med hvor man skal plassere connection strings, client secrets og lignende er et evig problem. Uansett hvordan man vrir og vender på det må det som regel lagres noe gull noe sted. Over tid har både min forståelse og verktøyene tilgjengelig for secrets i C#+Azure blitt mye bedre, noe som igjen har ført til en god del forbedringer. Den optimale løsningen dersom man har kontroll på alle komponenter og de alle kjører i Azure sammen, er managed identity. Dessverre er ikke infrastrukturen alltid slik. Av og til har man integrasjoner til tredjepart, noe kjører on-prem eller hemmelighetene skal brukes til noe annet enn direkte tilgang.
I denne bloggen skal jeg ta dere gjennom hvorfor noen løsninger er bedre enn andre og hvordan å sette dem opp. Ut ifra ditt oppsett kan det hende man stopper et sted på veien og sier seg fornøyd, eller finner en annen måte å komme seg rundt de oppgitte svakhetene. Her er det jeg har lært så langt (fra nivå1-dårlig til nivå8-ninja)
Nivå 1 → Passord rett i koden
Det er fort gjort å bare skulle fikse noe fort og glemmer å fjerne slike implementasjoner eller at man er ny i programmeringsverden, men denne typen kode er litt krise å finne i produksjon …
✅ God ytelse
❌ Unngå bygg ved endring av connection string
❌ Ingen passord på utviklermaskiner
❌ Ingen passord i GIT-repo
❌ Ingen passord åpent på server
❌ Unngå deploy ved endring av connection string
❌ Lett koordinering av oppdateringer på tvers av applikasjoner
Nivå 2 → Passord i app-settings-fil
Ved å legge connection string og annet i app-settings.json fila kan vi i hvert fall endre verdi uten å bygge hele prosjektet på ny. Man kan i tillegg lage en app-settings fil pr miljø og på den måten ha konfigurasjon og passord satt opp pr miljø. Dette er dessverre fortsatt en del av kildekoden og har fortsatt en del problemer:
✅ God ytelse
✅ Unngå bygg ved endring av connection string
❌ Ingen passord på utviklermaskiner
❌ Ingen passord i GIT-repo
❌ Ingen passord åpent på server
❌ Unngå deploy ved endring av connection string
❌ Lett koordinering av oppdateringer på tvers av applikasjoner
Nivå 3 → value-substitution i byggpipeline + user sercrets
Passord i app-settings-fil kan erstattes i byggsteget med secrets. Dette gjøres som en task i build pipleline med value-substitution og user secrets lokalt. Dette gir mulighet for secrets pr miljø og sikkerhet på at hemmeligheter ikke kommer seg til Github.
✅ God ytelse
✅ Unngå bygg ved endring av connection string
✅ Ingen passord på utviklermaskiner
✅ Ingen passord i GIT-repo
✅ Ingen passord åpent på server
❌ Unngå deploy ved endring av connection string
❌ Lett koordinering av oppdateringer på tvers av applikasjoner
Nivå 4 → Passord i env
Man kan for eksempel sette app-settings-env i Azure web apps eller lignende. På den måten har man secrets helt uavhengig av bygg og deploy av koden og pr miljø. Dette er en ganske ok løsning for prosjekter der man har god kontroll på tilganger og infrastruktur som kode satt opp. Dette er spesielt vanlig ved kubernetes oppsett og gir sikkerhet på at secrets ikke kommer seg til Github. Men problemet er som regel å finne en god måte å sette passordene for infrastruktur koden. Det fører også ofte til at man må kjøre infrastruktur pipeline for hver deploy av ny kode for å være sikker på at nye secrets blir med.
✅ God ytelse
✅ Unngå bygg ved endring av connection string
✅ Ingen passord på utviklermaskiner
✅ Ingen passord i GIT-repo
❌ Ingen passord åpent på server
✅ Unngå deploy ved endring av connection string
❌ Lett koordinering av oppdateringer på tvers av applikasjoner
Nivå 5 → Keyvault-oppslag i koden
Lesing av secrets ved behov fra keyvault ved hjelp av denne typen kode:
Dette fører til at man potensielt slår opp secrets mange ganger og problemer med utvikling lokalt. DefaultAzureCredentials gir deg mulighet til å bruke aktiv visual strudio bruker og managed idenity til å lese ut secrets uten å måtte ha client secret noe sted. En annen positiv ting med det å få secrets inn i keyvault er at flere språk kan lese herifra og man støtter både managed identity, roller og har audit logg innebygget.
❌ God ytelse
✅ Unngå bygg ved endring av connection string
✅ Ingen passord på utviklermaskiner
✅ Ingen passord i GIT-repo
✅ Ingen passord åpent på server
✅ Unngå deploy ved endring av connection string
❌ Lett koordinering av oppdateringer på tvers av applikasjoner
Nivå 6 → App-settings-env-kevault
Det er mulig å la azure lese secrets med managed identity dynamisk ved å gi appsetting verdien:
På denne måten kan man la Azure gjøre jobben med oppslag av secret og migrere dette med app settings hver gang man restarter serveren. Dette alternativet er en utvidelse av nivå 4.
✅ God ytelse
✅ Unngå bygg ved endring av connection string
✅ Ingen passord på utviklermaskiner
✅ Ingen passord i GIT-repo
✅ Ingen passord åpent på server
✅ Unngå deploy ved endring av connection string
❌ Lett koordinering av oppdateringer på tvers av applikasjoner
Nivå 7 → Secret ninja!
Konfigurere automatisk innlesing av secrets inn i IConfiguration som del av setup med koden under.
Program.cs
Hjelpeklasse
Dependencies
Dette gjør at man kan ha secrets i keyvault og oppdatere dem der løpende enten manuelt eller med IAC. Samtidig kan resten av prosjektet ditt bruke IConfiguration som vanlig og hente ut sammenstilling av appsettings.json, appsettings.dev.json, user secrets, key vault secrets og manuell environment override. Det å ha muligheten til å legge secrets og config i alle disse 5 config stedene på et kjørende prosjekt kan være litt forvirrende, men gir også en utrolig fleksibilitet. Ved å ha mulighet til å sette opp gode kjøreregler på hvordan man skal jobbe med disse i teamet, synes jeg dette er en utrolig bra løsning.
✅ God ytelse
✅ Unngå bygg ved endring av connection string
✅ Ingen passord på utviklermaskiner
✅ Ingen passord i GIT-repo
✅ Ingen passord åpent på server
✅ Unngå deploy ved endring av connection string
❌ Lett koordinering av oppdateringer på tvers av applikasjoner
Nivå 8 → Config & Secret ninja!
Det siste steget bruker Azure App Configuration sammen med keyvault for å også kunne konfigurere dette på tvers av app og slippe spessialkoden over. Konseptet her er at man får en sentral server med alle konfigurasjoner på tvers av applikasjon. Dette gjør at man kan se oversikt over alle secrets og konfigurasjoner uten å nødvendigvis har rettigheter til å se selve hemmeligheten siden du ikke har tilgang i keyvault-et disse ligger i. Det gir også muligheten til å håndtere «feature toggles» på tvers av applikasjon som er veldig kraftig.
✅ God ytelse
✅ Unngå bygg ved endring av connection string
✅ Ingen passord på utviklermaskiner
✅ Ingen passord i GIT-repo
✅ Ingen passord åpent på server
✅ Unngå deploy ved endring av connection string
✅ Lett koordinering av oppdateringer på tvers av applikasjoner
Oppsumering
Det er nok ikke alle som kommer til å være 100% enige med meg når det kommer til vektlegging av krav. Brukerbehovet og valg som hvor leverandør-uavhengig man har lyst å være, kan jo for eksempel avgjøre alt for noen. Dersom man kommer fra retningen plattformdrift eller bruker mange andre språk enn C#, skjønner jeg at alternativene som går rundt miljøvariable er en urokkelig sannhet, men da føler jeg man begrenser seg i en suboptimal løsning. Verktøyene som har blitt laget gjør at man kan få utrolig fleksible og gode løsninger som gjør at løsningen både er lett å drifte, videreutvikle og sikre. Det å kunne laste ned et prosjekt fra Git i din bedrift og bare kunne trykke play uten å måtte gjøre mer for å spinne opp en løsning som går mot et valgt miljø som dev eller test syntes jeg er magisk!