Důležité informace o zabezpečení pro klíčové úlohy
Důležité úlohy musí být ze své podstaty zabezpečené. Pokud dojde k ohrožení aplikace nebo její infrastruktury, je ohrožena dostupnost. Cílem této architektury je maximalizovat spolehlivost, aby aplikace zůstala výkonná a dostupná za všech okolností. Kontrolní mechanismy zabezpečení se používají především s cílem zmírnit hrozby, které ovlivňují dostupnost a spolehlivost.
Poznámka:
Vaše obchodní požadavky můžou potřebovat více bezpečnostních opatření. Důrazně doporučujeme rozšířit ovládací prvky ve vaší implementaci podle pokynů v aspektech zabezpečení dobře navržená architektura Azure pro klíčové úlohy.
Správa identit a přístupu
Na úrovni aplikace tato architektura používá jednoduché schéma ověřování založené na klíčích rozhraní API pro některé omezené operace, například vytváření položek katalogu nebo odstraňování komentářů. Pokročilé scénáře, jako je ověřování uživatelů a role uživatelů, jsou nad rámec základní architektury.
Pokud vaše aplikace vyžaduje ověřování uživatelů a správu účtů, postupujte podle doporučení pro správu identit a přístupu. Mezi strategie patří použití zprostředkovatelů spravovaných identit, zabránění vlastní správě identit a použití ověřování bez hesla, pokud je to možné.
Přístup s nejnižšími oprávněními
Nakonfigurujte zásady přístupu tak, aby uživatelé a aplikace získali minimální úroveň přístupu, kterou potřebují ke splnění své funkce. Vývojáři obvykle nepotřebují přístup k produkční infrastruktuře, ale kanál nasazení potřebuje úplný přístup. Clustery Kubernetes nenasdílely image kontejnerů do registru, ale pracovní postupy GitHubu můžou. Rozhraní API front-endu obvykle nedostanou zprávy od zprostředkovatele zpráv a back-endoví pracovníci nemusí zprostředkovateli posílat nové zprávy. Tato rozhodnutí závisí na úloze a na úrovni přístupu, kterou přiřadíte, by měla odrážet funkčnost jednotlivých komponent.
Mezi příklady z klíčové referenční implementace Azure patří:
- Každá komponenta aplikace, která funguje se službou Azure Event Hubs, používá připojovací řetězec s oprávněními Listen (
BackgroundProcessor
) nebo Send (CatalogService
). Tato úroveň přístupu zajišťuje, že každý pod má pouze minimální přístup potřebný ke splnění své funkce. - Instanční objekt pro fond agentů Azure Kubernetes Service (AKS) má pouze oprávnění Get and List pro tajné kódy ve službě Azure Key Vault.
- Identita AKS Kubelet má pouze oprávnění AcrPull pro přístup k globálnímu registru kontejneru.
Spravované identity
Pokud chcete zlepšit zabezpečení klíčové úlohy, vyhněte se používání tajných kódů založených na službách, jako jsou připojovací řetězec nebo klíče rozhraní API, pokud je to možné. Pokud služba Azure tuto funkci podporuje, doporučujeme používat spravované identity.
Referenční implementace používá spravovanou identitu přiřazenou službou ve fondu agentů AKS ("identita Kubelet") pro přístup k globální službě Azure Container Registry a trezoru klíčů razítka. K omezení přístupu se používají vhodné předdefinované role. Tento kód Terraformu například přiřadí k identitě Kubelet pouze AcrPull
roli:
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
}
Tajné kódy
Pokud je to možné, při přístupu k prostředkům Azure místo klíčů použijte ověřování Microsoft Entra. Mnoho služeb Azure, jako je Azure Cosmos DB a Azure Storage, podporuje možnost úplné zakázání ověřování pomocí klíče. AKS podporuje ID úloh Microsoft Entra.
Ve scénářích, ve kterých nemůžete použít ověřování Microsoft Entra, má každé razítko nasazení vyhrazenou instanci služby Key Vault k ukládání klíčů. Tyto klíče se vytvářejí automaticky během nasazování a ukládají se ve službě Key Vault pomocí Terraformu. Žádný lidský operátor, s výjimkou vývojářů v kompletních prostředích, může komunikovat s tajnými kódy. Zásady přístupu ke službě Key Vault jsou navíc nakonfigurované tak, aby žádné uživatelské účty neměly povolený přístup k tajným kódům.
Poznámka:
Tato úloha nepoužívá vlastní certifikáty, ale platí stejné principy.
V clusteru AKS umožňuje poskytovatel služby Key Vault pro úložiště tajných kódů aplikaci využívat tajné kódy. Ovladač CSI načte klíče ze služby Key Vault a připojí je jako soubory do jednotlivých podů.
#
# /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 }}
Referenční implementace používá Helm se službou Azure Pipelines k nasazení ovladače CSI, který obsahuje všechny názvy klíčů ze služby Key Vault. Ovladač také zodpovídá za aktualizaci připojených tajných kódů, pokud se změní ve službě Key Vault.
Na straně příjemce obě aplikace .NET používají integrovanou funkci ke čtení konfigurace ze souborů (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>();
});
Kombinace automatického opětovného načítání ovladače CSI a reloadOnChange: true
pomáhá zajistit, aby se při změně klíčů ve službě Key Vault připojily nové hodnoty do clusteru. Tento proces nezaručuje obměnu tajných kódů v aplikaci. Implementace používá jednoúčelovou instanci klienta Azure Cosmos DB, která vyžaduje restartování podu, aby změnu použil.
Vlastní domény a tls
Webové úlohy by měly používat protokol HTTPS, aby se zabránilo útokům typu man-in-the-middle na všechny úrovně interakce, jako je komunikace z klienta do rozhraní API nebo z rozhraní API do rozhraní API. Nezapomeňte automatizovat obměnu certifikátů, protože prošlé certifikáty jsou stále běžnou příčinou výpadků a snížených výkonů.
Referenční implementace plně podporuje HTTPS s vlastními názvy domén, například contoso.com
. Použije také příslušnou konfiguraci pro int
prostředí i prod
prostředí. Můžete také přidat vlastní domény pro e2e
prostředí. Tato referenční implementace však nepoužívá vlastní názvy domén z důvodu krátkodobé povahy e2e
a zvýšené doby nasazení při použití vlastních domén s certifikáty SSL ve službě Azure Front Door.
Pokud chcete povolit úplnou automatizaci nasazení, měli byste vlastní doménu spravovat prostřednictvím zóny Azure DNS. Kanál nasazení infrastruktury dynamicky vytváří záznamy CNAME v zóně Azure DNS a mapuje tyto záznamy automaticky na instanci služby Azure Front Door.
Jsou povolené certifikáty SSL spravované službou Azure Front Door, které odeberou požadavek na ruční prodlužování platnosti certifikátů SSL. Protokol TLS 1.2 je nakonfigurovaný jako minimální verze.
#
# /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"
}
}
Prostředí, která nejsou zřízená pomocí vlastních domén, jsou přístupná prostřednictvím výchozího koncového bodu služby Azure Front Door. Můžete je například kontaktovat na adrese, například env123.azurefd.net
.
Poznámka:
V kontroleru příchozího přenosu dat clusteru se vlastní domény v obou případech nepoužívají. Místo toho se používá název DNS poskytovaný službou Azure, jako [prefix]-cluster.[region].cloudapp.azure.com
je let's Encrypt, který může pro tyto koncové body vydávat bezplatné certifikáty SSL.
Referenční implementace používá jetstack cert-manager
k automatickému zřizování certifikátů SSL/TLS z let's Encrypt pro pravidla příchozího přenosu dat. Další nastavení konfigurace, jako je ClusterIssuer
například , které požaduje certifikáty z Let's Encrypt, se nasazují prostřednictvím samostatného cert-manager-config
chartu Helm, který je uložený v src/config/cert-manager/chart.
Tato implementace se používá ClusterIssuer
místo Issuer
toho, aby se zabránilo tomu, že by vystavitely pro každý obor názvů. Další informace najdete v dokumentaci k nástroji cert-manager a poznámky k verzi nástroje cert-manager.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
Konfigurace
Veškerá konfigurace modulu runtime aplikace je uložená ve službě Key Vault, včetně tajných kódů a nesmyslných nastavení. K uložení nastavení můžete použít úložiště konfigurace, například Aplikace Azure Konfigurace. Když ale máte jeden obchod, sníží se počet potenciálních bodů selhání důležitých aplikací. Ke zjednodušení celkové implementace použijte Key Vault pro konfiguraci modulu runtime.
Trezory klíčů by měly být naplněny kanálem nasazení. V implementaci jsou požadované hodnoty buď zdrojové přímo z Terraformu, jako jsou databázové připojovací řetězec nebo předány jako proměnné Terraformu z kanálu nasazení.
Konfigurace infrastruktury a nasazení jednotlivých prostředí, jako e2e
je například , int
a prod
, jsou uloženy v souborech proměnných, které jsou součástí úložiště zdrojového kódu. Tento přístup má dvě výhody:
- Všechny změny v prostředí se sledují a procházejí kanály nasazení, než se použijí v prostředí.
- Jednotlivá
e2e
prostředí je možné nakonfigurovat jinak, protože nasazení je založené na kódu ve větvi.
Jednou z výjimek je ukládání citlivých hodnot pro kanály. Tyto hodnoty se ukládají jako tajné kódy ve skupinách proměnných Azure DevOps.
Zabezpečení kontejneru
Je nutné zabezpečit image kontejnerů pro všechny kontejnerizované úlohy.
Tato referenční implementace používá kontejnery Dockeru úloh založené na imagích modulu runtime, nikoli sadě SDK, aby se minimalizovala stopa a potenciální prostor pro útoky. Nejsou nainstalovány žádné další nástroje, například ping
, wget
nebo curl
.
Aplikace běží pod neprivilegovaným uživatelem workload
, který byl vytvořen jako součást procesu sestavení image:
RUN groupadd -r workload && useradd --no-log-init -r -g workload workload
USER workload
Referenční implementace používá Helm k zabalení manifestů YAML, které potřebuje k nasazení jednotlivých komponent. Tento proces zahrnuje nasazení Kubernetes, služby, konfiguraci horizontálního automatického škálování podů a kontext zabezpečení. Všechny grafy Helm obsahují základní bezpečnostní opatření, která dodržují osvědčené postupy Kubernetes.
Tato bezpečnostní opatření jsou:
readOnlyFilesystem
: Kořenový systém/
souborů v každém kontejneru je nastavený na jen pro čtení, aby se zabránilo zápisu kontejneru do systému souborů hostitele. Toto omezení brání útočníkům ve stahování dalších nástrojů a zachování kódu v kontejneru. Adresáře, které vyžadují přístup pro čtení i zápis, jsou připojené jako svazky.privileged
: Všechny kontejnery se nastaví tak, aby běžely jako neprivilegované. Spuštění kontejneru jako privilegovaného poskytuje všem možnostem kontejneru a také zvedne všechna omezení, která vynucuje kontroler skupiny řízení zařízení.allowPrivilegeEscalation
: Zabrání uvnitř kontejneru v získání více oprávnění než jeho nadřazený proces.
Tato bezpečnostní opatření jsou také nakonfigurovaná pro kontejnery jiných společností než Microsoft a grafy Helm, jako cert-manager
je v případě, kdy je to možné. K auditování těchto bezpečnostních opatření můžete použít Azure Policy.
#
# Example:
# /src/app/charts/backgroundprocessor/values.yaml
#
containerSecurityContext:
privileged: false
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
Každé prostředí, včetně prod
, int
a každého e2e
prostředí, má vyhrazenou instanci služby Container Registry, která má globální replikaci do každé oblasti, ve které se nasazují razítka.
Poznámka:
Tato referenční implementace nepoužívá kontrolu chyb zabezpečení imagí Dockeru. Doporučujeme používat Microsoft Defender pro registry kontejnerů, potenciálně s GitHub Actions.
Příchozí přenosy dat
Azure Front Door je globální nástroj pro vyrovnávání zatížení v této architektuře. Všechny webové požadavky se směrují přes Azure Front Door, který vybere příslušný back-end. Klíčové aplikace by měly využívat další funkce služby Azure Front Door, jako jsou firewally webových aplikací (WAF).
Firewall webových aplikací
Důležitou funkcí služby Azure Front Door je WAF, protože umožňuje službě Azure Front Door kontrolovat provoz, který prochází. V režimu prevence jsou všechny podezřelé požadavky zablokované. V implementaci jsou nakonfigurovány dvě sady pravidel. Tyto sady pravidel jsou Microsoft_DefaultRuleSet
a Microsoft_BotManagerRuleSet
.
Tip
Když nasadíte Azure Front Door s WAF, doporučujeme začít s režimem detekce . Pečlivě monitorujte své chování s přirozeným provozem zákazníků a dolaďte pravidla detekce. Po odstranění falešně pozitivních výsledků nebo pokud jsou falešně pozitivní výsledky vzácné, přepněte do režimu prevence . Tento proces je nezbytný, protože každá aplikace se liší a některé datové části se dají považovat za škodlivé, i když jsou pro danou úlohu legitimní.
Směrování
Do kontejnerů rozhraní API se směrují jenom ty požadavky, které přicházejí přes Azure Front Door.CatalogService
HealthService
K vynucování tohoto chování použijte konfiguraci příchozího přenosu dat Nginx. Kontroluje přítomnost hlavičky X-Azure-FDID
a jestli je to ta správná pro globální instanci služby Azure Front Door konkrétního prostředí.
#
# /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\'\"
# ...
Kanály nasazení pomáhají zajistit, aby se tato hlavička správně naplnila, ale zároveň musí toto omezení obejít pro orientační testy, protože testují jednotlivé clustery přímo místo služby Azure Front Door. Referenční implementace používá skutečnost, že orientační testy se spouští jako součást nasazení. Tento návrh umožňuje, aby byla hodnota hlavičky známá a přidána do požadavků HTTP orientačního testu.
#
# /.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.
}
Zabezpečená nasazení
Pokud chcete dodržovat základní dobře navržené principy pro efektivitu provozu, plně automatizujte všechna nasazení. Neměli by vyžadovat žádné ruční kroky s výjimkou aktivace spuštění nebo schválení brány.
Je nutné zabránit škodlivým pokusům nebo náhodným chybným konfiguracím, které můžou zakázat bezpečnostní opatření. Referenční implementace používá stejný kanál pro nasazení infrastruktury i aplikace, což vynutí automatické vrácení všech potenciálních odchylek konfigurace. Toto vrácení zpět pomáhá udržovat integritu infrastruktury a zajistit soulad s kódem aplikace. Jakákoli změna se při dalším nasazení zahodí.
Terraform generuje citlivé hodnoty pro nasazení během spuštění kanálu nebo Azure DevOps je poskytuje jako tajné kódy. Tyto hodnoty jsou chráněné pomocí omezení přístupu na základě role.
Poznámka:
Pracovní postupy GitHubu poskytují podobný koncept samostatných úložišť pro hodnoty tajných kódů. Tajné kódy jsou šifrované, proměnné prostředí, které může GitHub Actions používat.
Je důležité věnovat pozornost všem artefaktům, které kanál vytváří, protože tyto artefakty mohou potenciálně obsahovat tajné hodnoty nebo informace o vnitřních pracovních činnostech aplikace. Nasazení Azure DevOps referenční implementace generuje dva soubory s výstupy Terraformu. Jeden soubor je určený pro kolky a jeden soubor je určený pro globální infrastrukturu. Tyto soubory neobsahují hesla, která by mohla ohrozit infrastrukturu. Tyto soubory byste ale měli považovat za citlivé, protože odhalí informace o infrastruktuře, včetně ID clusteru, IP adres, názvů účtů úložiště, názvů služby Key Vault, názvů databází Azure Cosmos DB a ID hlaviček služby Azure Front Door.
U úloh, které používají Terraform, je potřeba věnovat větší úsilí ochraně souboru stavu, protože obsahuje úplný kontext nasazení, včetně tajných kódů. Soubor stavu je obvykle uložený v účtu úložiště, který by měl mít samostatný životní cyklus od úlohy a měl by být přístupný pouze z kanálu nasazení. Měli byste protokolovat jakýkoli jiný přístup k tomuto souboru a odesílat výstrahy příslušné skupině zabezpečení.
Aktualizace závislostí
Knihovny, architektury a nástroje, které aplikace používá, se v průběhu času aktualizují. Tyto aktualizace je důležité pravidelně dokončit, protože často obsahují opravy problémů se zabezpečením, které by mohly útočníkům poskytnout neoprávněný přístup k systému.
Referenční implementace používá GitHub Dependabot pro aktualizace závislostí NuGet, Dockeru, npm, Terraformu a GitHub Actions. Konfigurační dependabot.yml
soubor se automaticky vygeneruje pomocí skriptu PowerShellu z důvodu složitosti různých částí aplikace. Každý modul Terraformu například potřebuje samostatnou položku.
#
# /.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...
- Aktualizace se aktivují měsíčně jako kompromis mezi tím, že mají nejaktuálnější knihovny a udržují režijní náklady udržovatelné. Kromě toho se průběžně monitorují klíčové nástroje, jako je Terraform, a důležité aktualizace se spouští ručně.
- Žádosti o přijetí změn cílí na
component-updates
větev místomain
. - Knihovny Npm jsou nakonfigurované tak, aby kontrolovaly pouze závislosti, které přecházejí do kompilované aplikace, místo na podpůrné nástroje, jako je
@vue-cli
.
Dependabot vytvoří pro každou aktualizaci samostatnou žádost o přijetí změn, která může zahltit provozní tým. Referenční implementace nejprve shromažďuje dávku aktualizací ve component-updates
větvi a pak spouští testy v e2e
prostředí. Pokud jsou tyto testy úspěšné, vytvoří další žádost o přijetí změn, která cílí na main
větev.
Obranné kódování
Volání rozhraní API můžou selhat z různých důvodů, včetně chyb kódu, chybných nasazení a selhání infrastruktury. Pokud volání rozhraní API selže, volající nebo klientská aplikace by neměly dostávat rozsáhlé informace o ladění, protože tyto informace můžou nežádoucím uživatelům poskytnout užitečné datové body o aplikaci.
Referenční implementace demonstruje tento princip vrácením pouze ID korelace v neúspěšné odpovědi. Nesdílí důvod selhání, jako je zpráva o výjimce nebo trasování zásobníku. Pomocí tohoto ID a pomocí hlavičky Server-Location
může operátor incident prozkoumat pomocí 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();
});
// ...
}
Další krok
Nasaďte referenční implementaci, abyste získali úplný přehled o prostředcích a jejich konfiguraci.