Dela via


Säkerhetsöverväganden för verksamhetskritiska arbetsbelastningar

Verksamhetskritiska arbetsbelastningar måste skyddas i sig. Om ett program eller dess infrastruktur komprometteras är tillgängligheten i fara. Fokus för den här arkitekturen är att maximera tillförlitligheten så att programmet förblir högpresterande och tillgängligt under alla omständigheter. Säkerhetskontroller tillämpas främst i syfte att minimera hot som påverkar tillgänglighet och tillförlitlighet.

Kommentar

Dina affärskrav kan behöva fler säkerhetsåtgärder. Vi rekommenderar starkt att du utökar kontrollerna i implementeringen enligt vägledningen i Säkerhetsöverväganden för Azure Well-Architected Framework för verksamhetskritiska arbetsbelastningar.

Identitets- och åtkomsthantering

På programnivå använder den här arkitekturen ett enkelt autentiseringsschema baserat på API-nycklar för vissa begränsade åtgärder, till exempel att skapa katalogobjekt eller ta bort kommentarer. Avancerade scenarier som användarautentisering och användarroller ligger utanför baslinjearkitekturens omfång.

Om ditt program kräver användarautentisering och kontohantering följer du rekommendationerna för identitets- och åtkomsthantering. Vissa strategier omfattar att använda hanterade identitetsprovidrar, undvika anpassad identitetshantering och använda lösenordslös autentisering när det är möjligt.

Åtkomst med minst behörighet

Konfigurera åtkomstprinciper så att användare och program får den minimala åtkomstnivå som de behöver för att uppfylla sin funktion. Utvecklare behöver vanligtvis inte åtkomst till produktionsinfrastrukturen, men distributionspipelinen behöver fullständig åtkomst. Kubernetes-kluster push-överför inte containeravbildningar till ett register, men GitHub-arbetsflöden kan göra det. Klientdels-API:er får vanligtvis inte meddelanden från meddelandekoordinatorn, och backend-arbetare skickar inte nödvändigtvis nya meddelanden till asynkron meddelandekö. Dessa beslut beror på arbetsbelastningen och den åtkomstnivå som du tilldelar bör återspegla varje komponents funktioner.

Exempel från den verksamhetskritiska referensimplementeringen i Azure är:

  • Varje programkomponent som fungerar med Azure Event Hubs använder en anslutningssträng med behörigheterna Lyssna (BackgroundProcessor) eller Skicka (CatalogService). Den åtkomstnivån säkerställer att varje podd endast har den minsta åtkomst som krävs för att uppfylla dess funktion.
  • Tjänstens huvudnamn för AKS-agentpoolen (Azure Kubernetes Service) har endast behörighet att hämta och lista hemligheter i Azure Key Vault.
  • AKS Kubelet-identiteten har bara AcrPull-behörighet att komma åt det globala containerregistret.

Hanterade identiteter

För att förbättra säkerheten för en verksamhetskritisk arbetsbelastning bör du undvika att använda tjänstbaserade hemligheter, till exempel anslutningssträng eller API-nycklar, när det är möjligt. Vi rekommenderar att du använder hanterade identiteter om Azure-tjänsten stöder den funktionen.

Referensimplementeringen använder en tjänsttilldelad hanterad identitet i AKS-agentpoolen ("Kubelet-identitet") för att få åtkomst till det globala Azure Container Registry och en stämpels nyckelvalv. Lämpliga inbyggda roller används för att begränsa åtkomsten. Den här Terraform-koden tilldelar till exempel bara AcrPull rollen till Kubelet-identiteten:

resource "azurerm_role_assignment" "acrpull_role" {
  scope                = data.azurerm_container_registry.global.id
  role_definition_name = "AcrPull"
  principal_id         = azurerm_kubernetes_cluster.stamp.kubelet_identity.0.object_id
}

Hemligheter

När det är möjligt använder du Microsoft Entra-autentisering i stället för nycklar när du kommer åt Azure-resurser. Många Azure-tjänster, till exempel Azure Cosmos DB och Azure Storage, stöder alternativet att helt inaktivera nyckelautentisering. AKS stöder Microsoft Entra-arbetsbelastnings-ID.

För scenarier där du inte kan använda Microsoft Entra-autentisering har varje distributionsstämpel en dedikerad instans av Key Vault för att lagra nycklar. Dessa nycklar skapas automatiskt under distributionen och lagras i Key Vault med Terraform. Ingen mänsklig operatör, förutom utvecklare i slutpunkt till slutpunkt-miljöer, kan interagera med hemligheter. Dessutom konfigureras Key Vault-åtkomstprinciper så att inga användarkonton tillåts komma åt hemligheter.

Kommentar

Den här arbetsbelastningen använder inte anpassade certifikat, men samma principer gäller.

I AKS-klustret gör Key Vault Provider for Secrets Store att programmet kan använda hemligheter. CSI-drivrutinen läser in nycklar från Key Vault och monterar dem som filer i enskilda poddar.

#
# /src/config/csi-secrets-driver/chart/csi-secrets-driver-config/templates/csi-secrets-driver.yaml
#
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: azure-kv
spec:
  provider: azure
  parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "true"
    userAssignedIdentityID: {{ .Values.azure.managedIdentityClientId | quote }}
    keyvaultName: {{ .Values.azure.keyVaultName | quote }}
    tenantId: {{ .Values.azure.tenantId | quote }}
    objects: |
      array:
        {{- range .Values.kvSecrets }}
        - |
          objectName: {{ . | quote }}
          objectAlias: {{ . | lower | replace "-" "_" | quote }}
          objectType: secret
        {{- end }}

Referensimplementeringen använder Helm med Azure Pipelines för att distribuera CSI-drivrutinen som innehåller alla nyckelnamn från Key Vault. Drivrutinen ansvarar också för att uppdatera monterade hemligheter om de ändras i Key Vault.

I konsumentdelen använder båda .NET-programmen den inbyggda funktionen för att läsa konfigurationen från filer (AddKeyPerFile):

//
// /src/app/AlwaysOn.BackgroundProcessor/Program.cs
// + using Microsoft.Extensions.Configuration;
//
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // Load values from Kubernetes CSI Key Vault driver mount point.
        config.AddKeyPerFile(directoryPath: "/mnt/secrets-store/", optional: true, reloadOnChange: true);
        
        // More configuration if needed...
    })
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>();
    });

Kombinationen av CSI-drivrutinens automatiska inläsning och reloadOnChange: true säkerställer att när nycklar ändras i Key Vault monteras de nya värdena i klustret. Den här processen garanterar inte hemlig rotation i programmet. Implementeringen använder en Singleton Azure Cosmos DB-klientinstans som kräver att podden startas om för att ändringen ska kunna tillämpas.

Anpassade domäner och TLS

Webbaserade arbetsbelastningar bör använda HTTPS för att förhindra man-in-the-middle-attacker på alla interaktionsnivåer, till exempel kommunikation från klienten till API:et eller från API till API. Se till att automatisera certifikatrotationen eftersom utgångna certifikat fortfarande är en vanlig orsak till avbrott och försämrade funktioner.

Referensimplementeringen har fullt stöd för HTTPS med anpassade domännamn, till exempel contoso.com. Den tillämpar också lämplig konfiguration på både miljöerna int och prod . Du kan också lägga till anpassade domäner för e2e miljöer. Den här referensimplementeringen använder dock inte anpassade domännamn på grund av den kortlivade typen av e2e och den ökade distributionstiden när du använder anpassade domäner med SSL-certifikat i Azure Front Door.

Om du vill aktivera fullständig automatisering av distributionen bör du hantera den anpassade domänen via en Azure DNS-zon. Infrastrukturdistributionspipelinen skapar dynamiskt CNAME-poster i Azure DNS-zonen och mappar dessa poster automatiskt till en Azure Front Door-instans.

Azure Front Door-hanterade SSL-certifikat är aktiverade, vilket tar bort kravet på manuellA SSL-certifikatförnyelser. TLS 1.2 har konfigurerats som lägsta version.

#
# /src/infra/workload/globalresources/frontdoor.tf
#
resource "azurerm_frontdoor_custom_https_configuration" "custom_domain_https" {
  count                             = var.custom_fqdn != "" ? 1 : 0
  frontend_endpoint_id              = "${azurerm_frontdoor.main.id}/frontendEndpoints/${local.frontdoor_custom_frontend_name}"
  custom_https_provisioning_enabled = true

  custom_https_configuration {
    certificate_source = "FrontDoor"
  }
}

Miljöer som inte etableras med anpassade domäner är tillgängliga via standardslutpunkten för Azure Front Door. Du kan till exempel nå dem på en adress som env123.azurefd.net.

Kommentar

På kluster-ingresskontrollanten används inte anpassade domäner i något av fallen. I stället används ett Dns-namn som tillhandahålls av Azure som [prefix]-cluster.[region].cloudapp.azure.com med Let's Encrypt, som kan utfärda kostnadsfria SSL-certifikat för dessa slutpunkter.

Referensimplementeringen använder Jetstacks cert-manager för att automatiskt etablera SSL/TLS-certifikat från Let's Encrypt för ingressregler. Fler konfigurationsinställningar, till exempel ClusterIssuer, som begär certifikat från Let's Encrypt, distribueras via ett separat cert-manager-config helm-diagram som lagras i src/config/cert-manager/chart.

Den här implementeringen använder ClusterIssuer i stället för Issuer att undvika att ha utfärdare för varje namnområde. Mer information finns i dokumentationen för cert-manager och viktig information för cert-manager.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:

Konfiguration

All programkörningskonfiguration lagras i Key Vault, inklusive hemligheter och meningslösa inställningar. Du kan använda ett konfigurationsarkiv, till exempel Azure App Configuration, för att lagra inställningarna. Att ha ett enda lager minskar dock antalet potentiella felpunkter för verksamhetskritiska program. Använd Key Vault för körningskonfiguration för att förenkla den övergripande implementeringen.

Nyckelvalv bör fyllas i av distributionspipelinen. I implementeringen kommer de obligatoriska värdena antingen direkt från Terraform, till exempel databas anslutningssträng eller skickas som Terraform-variabler från distributionspipelinen.

Infrastruktur- och distributionskonfiguration för enskilda miljöer, till exempel e2e, intoch prod, lagras i variabelfiler som ingår i källkodslagringsplatsen. Den här metoden har två fördelar:

  • Alla ändringar i en miljö spåras och går igenom distributionspipelines innan de tillämpas på miljön.
  • Enskilda e2e miljöer kan konfigureras på olika sätt eftersom distributionen baseras på kod i en gren.

Ett undantag är lagring av känsliga värden för pipelines. Dessa värden lagras som hemligheter i Azure DevOps-variabelgrupper.

Containersäkerhet

Det är nödvändigt att skydda containeravbildningar för alla containerbaserade arbetsbelastningar.

Den här referensimplementeringen använder Docker-containrar för arbetsbelastningar som baseras på körningsavbildningar, inte SDK, för att minimera fotavtrycket och den potentiella attackytan. Det finns inga andra verktyg, till exempel ping, wgeteller curl, installerade.

Programmet körs under en icke-privilegierad användare workload som skapades som en del av avbildningsprocessen:

RUN groupadd -r workload && useradd --no-log-init -r -g workload workload
USER workload

Referensimplementeringen använder Helm för att paketera YAML-manifesten som behövs för att distribuera enskilda komponenter. Den här processen omfattar deras Kubernetes-distribution, tjänster, horisontell autoskalningskonfiguration för poddar och säkerhetskontext. Alla Helm-diagram innehåller grundläggande säkerhetsåtgärder som följer Metodtips för Kubernetes.

Dessa säkerhetsåtgärder är:

  • readOnlyFilesystem: Rotfilsystemet / i varje container är skrivskyddat för att förhindra att containern skriver till värdfilsystemet. Den här begränsningen hindrar angripare från att ladda ned fler verktyg och spara kod i containern. Kataloger som kräver läs- och skrivåtkomst monteras som volymer.
  • privileged: Alla containrar är inställda på att köras som icke-privilegierade. Att köra en container som privilegierad ger alla funktioner till containern, och den lyfter även alla begränsningar som kontrollgruppskontrollanten tillämpar.
  • allowPrivilegeEscalation: Hindrar insidan av en container från att få fler privilegier än den överordnade processen.

Dessa säkerhetsåtgärder är också konfigurerade för containrar som inte kommer från Microsoft och Helm-diagram som cert-manager när det är möjligt. Du kan använda Azure Policy för att granska dessa säkerhetsåtgärder.

#
# Example:
# /src/app/charts/backgroundprocessor/values.yaml
#
containerSecurityContext:
  privileged: false
  readOnlyRootFilesystem: true
  allowPrivilegeEscalation: false

Varje miljö, inklusive prod, intoch varje e2e miljö, har en dedikerad instans av Container Registry som har global replikering till var och en av de regioner där stämplar distribueras.

Kommentar

Den här referensimplementeringen använder inte sårbarhetsgenomsökning av Docker-avbildningar. Vi rekommenderar att du använder Microsoft Defender för containerregister, eventuellt med GitHub Actions.

Inkommande trafik

Azure Front Door är den globala lastbalanseraren i den här arkitekturen. Alla webbbegäranden dirigeras via Azure Front Door, som väljer lämplig serverdel. Verksamhetskritiska program bör dra nytta av andra Azure Front Door-funktioner, till exempel brandväggar för webbprogram (WAFs).

Brandvägg för webbaserade program

En viktig Azure Front Door-funktion är WAF eftersom den gör det möjligt för Azure Front Door att inspektera trafik som passerar. I läget Förebyggande blockeras alla misstänkta begäranden. I implementeringen konfigureras två regeluppsättningar. Dessa regeluppsättningar är Microsoft_DefaultRuleSet och Microsoft_BotManagerRuleSet.

Dricks

När du distribuerar Azure Front Door med WAF rekommenderar vi att du börjar med identifieringsläget. Övervaka dess beteende med naturlig kundtrafik och finjustera identifieringsreglerna. När du har eliminerat falska positiva identifieringar, eller om falska positiva identifieringar är sällsynta, växlar du till förebyggande läge. Den här processen är nödvändig eftersom varje program skiljer sig åt och vissa nyttolaster kan betraktas som skadliga, även om de är legitima för den specifika arbetsbelastningen.

Routning

Endast de begäranden som kommer via Azure Front Door dirigeras till API-containrarna, till exempel CatalogService och HealthService. Använd en Nginx-ingresskonfiguration för att framtvinga det här beteendet. Den söker efter förekomsten av ett X-Azure-FDID huvud och om det är rätt för den globala Azure Front Door-instansen av en specifik miljö.

#
# /src/app/charts/catalogservice/templates/ingress.yaml
#
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  # ...
  annotations:
    # To restrict traffic coming only through our Azure Front Door instance, we use a header check on the X-Azure-FDID.
    # The pipeline injects the value. Therefore, it's important to treat this ID as a sensitive value.
    nginx.ingress.kubernetes.io/modsecurity-snippet: |
      SecRuleEngine On
      SecRule &REQUEST_HEADERS:X-Azure-FDID \"@eq 0\"  \"log,deny,id:106,status:403,msg:\'Front Door ID not present\'\"
      SecRule REQUEST_HEADERS:X-Azure-FDID \"@rx ^(?!{{ .Values.azure.frontdoorid }}).*$\"  \"log,deny,id:107,status:403,msg:\'Wrong Front Door ID\'\"
  # ...

Distributionspipelines hjälper till att säkerställa att rubriken är korrekt ifylld, men den måste också kringgå den här begränsningen för röktester eftersom de avsöker varje kluster direkt i stället för via Azure Front Door. Referensimplementeringen använder det faktum att röktester körs som en del av distributionen. Med den här designen kan huvudvärdet vara känt och läggas till i HTTP-begäranden för röktest.

#
# /.ado/pipelines/scripts/Run-SmokeTests.ps1
#
$header = @{
  "X-Azure-FDID" = "$frontdoorHeaderId"
  "TEST-DATA"  = "true" # Header to indicate that posted comments and ratings are for tests and can be deleted again by the app.
}

Säkra distributioner

Om du vill följa baslinjens väldefinierade principer för driftskvalitet automatiserar du alla distributioner fullständigt. De bör inte kräva några manuella steg förutom för att utlösa körningen eller godkänna en grind.

Du måste förhindra skadliga försök eller oavsiktliga felkonfigurationer som kan inaktivera säkerhetsåtgärder. Referensimplementeringen använder samma pipeline för både infrastruktur- och programdistribution, vilket tvingar fram en automatisk återställning av eventuell konfigurationsavvikelse. Den här återställningen hjälper till att upprätthålla infrastrukturens integritet och anpassningen till programkoden. Alla ändringar ignoreras vid nästa distribution.

Terraform genererar känsliga värden för distribution under pipelinekörningen, eller så tillhandahåller Azure DevOps dem som hemligheter. Dessa värden skyddas med rollbaserade åtkomstbegränsningar.

Kommentar

GitHub-arbetsflöden ger ett liknande koncept med separata lager för hemliga värden. Hemligheter är krypterade, miljövariabler som GitHub Actions kan använda.

Det är viktigt att vara uppmärksam på alla artefakter som pipelinen skapar eftersom dessa artefakter potentiellt kan innehålla hemliga värden eller information om programmets inre funktioner. Azure DevOps-distributionen av referensimplementeringen genererar två filer med Terraform-utdata. En fil är avsedd för stämplar och en fil är avsedd för global infrastruktur. De här filerna innehåller inte lösenord som kan äventyra infrastrukturen. Du bör dock betrakta dessa filer som känsliga eftersom de visar information om infrastrukturen, inklusive kluster-ID:t, IP-adresser, lagringskontonamn, Key Vault-namn, Azure Cosmos DB-databasnamn och Azure Front Door-huvud-ID:er.

För arbetsbelastningar som använder Terraform måste du lägga extra arbete på att skydda tillståndsfilen eftersom den innehåller fullständig distributionskontext, inklusive hemligheter. Tillståndsfilen lagras vanligtvis i ett lagringskonto som ska ha en separat livscykel från arbetsbelastningen och bör endast vara tillgänglig från en distributionspipeline. Du bör logga all annan åtkomst till den här filen och skicka aviseringar till lämplig säkerhetsgrupp.

Beroendeuppdateringar

Bibliotek, ramverk och verktyg som programmet använder uppdateras över tid. Det är viktigt att slutföra dessa uppdateringar regelbundet eftersom de ofta innehåller korrigeringar för säkerhetsproblem som kan ge angripare obehörig åtkomst till systemet.

Referensimplementeringen använder GitHubs beroendeuppdateringar för NuGet, Docker, npm, Terraform och GitHub Actions. Konfigurationsfilen dependabot.yml genereras automatiskt med ett PowerShell-skript på grund av komplexiteten i programmets olika delar. Till exempel behöver varje Terraform-modul en separat post.

#
# /.github/dependabot.yml
#
version: 2
updates:
- package-ecosystem: "nuget"
  directory: "/src/app/AlwaysOn.HealthService"
  schedule:
    interval: "monthly" 
  target-branch: "component-updates" 

- package-ecosystem: "docker"
  directory: "/src/app/AlwaysOn.HealthService"
  schedule:
    interval: "monthly" 
  target-branch: "component-updates" 

# ... the rest of the file...
  • Uppdateringar utlöses varje månad som en kompromiss mellan att ha de senaste biblioteken och att hålla omkostnaderna underhållbara. Dessutom övervakas viktiga verktyg som Terraform kontinuerligt och viktiga uppdateringar körs manuellt.
  • Pull-begäranden (PR) riktar sig mot grenen component-updates i stället mainför .
  • Npm-bibliotek är konfigurerade för att endast kontrollera beroenden som går till det kompilerade programmet i stället för att stödja verktyg som @vue-cli.

Dependabot skapar en separat PR för varje uppdatering, vilket kan överbelasta driftteamet. Referensimplementeringen samlar först in en batch uppdateringar i grenen component-updates och kör sedan tester i e2e miljön. Om dessa tester lyckas skapar den en annan PR som riktar sig till grenen main .

Defensiv kodning

API-anrop kan misslyckas på grund av olika orsaker, till exempel kodfel, felaktiga distributioner och infrastrukturfel. Om ett API-anrop misslyckas bör anroparen eller klientprogrammet inte få omfattande felsökningsinformation eftersom den informationen kan ge angripare användbara datapunkter om programmet.

Referensimplementeringen visar den här principen genom att endast returnera korrelations-ID:t i det misslyckade svaret. Den delar inte felorsaken, till exempel undantagsmeddelande eller stackspårning. Med hjälp av det här ID:t och med hjälp av Server-Location huvudet kan en operatör undersöka incidenten med hjälp av Application Insights.

//
// Example ASP.NET Core middleware, which adds the Correlation ID to every API response.
//
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
   // ...

    app.Use(async (context, next) =>
    {
        context.Response.OnStarting(o =>
        {
            if (o is HttpContext ctx)
            {
                context.Response.Headers.Add("Server-Name", Environment.MachineName);
                context.Response.Headers.Add("Server-Location", sysConfig.AzureRegion);
                context.Response.Headers.Add("Correlation-ID", Activity.Current?.RootId);
                context.Response.Headers.Add("Requested-Api-Version", ctx.GetRequestedApiVersion()?.ToString());
            }
            return Task.CompletedTask;
        }, context);
        await next();
    });
    
    // ...
}

Gå vidare

Distribuera referensimplementeringen för att få en fullständig förståelse för resurser och deras konfiguration.