Вопросы безопасности для критически важных рабочих нагрузок
Критически важные рабочие нагрузки должны быть изначально защищены. Если приложение или его инфраструктура скомпрометирована, доступность рискует. Основное внимание этой архитектуре уделяется максимальной надежности, чтобы приложение оставалось доступным и доступным в любых обстоятельствах. Средства управления безопасностью применяются в первую очередь с целью устранения угроз, влияющих на доступность и надежность.
Примечание.
Бизнес-требования могут потребовать дополнительных мер безопасности. Мы настоятельно рекомендуем расширить элементы управления в реализации в соответствии с рекомендациями по обеспечению безопасности Azure Well-Architected Framework для критически важных рабочих нагрузок.
Управление удостоверениями и доступом
На уровне приложения эта архитектура использует простую схему проверки подлинности на основе ключей API для некоторых ограниченных операций, таких как создание элементов каталога или удаление комментариев. Расширенные сценарии, такие как проверка подлинности пользователей и роли пользователей, выходят за рамки базовой архитектуры.
Если приложению требуется проверка подлинности пользователей и управление учетными записями, следуйте рекомендациям по управлению удостоверениями и доступом. Некоторые стратегии включают использование поставщиков управляемых удостоверений, предотвращение пользовательского управления удостоверениями и использование проверки подлинности без пароля, когда это возможно.
Доступ с минимальными привилегиями
Настройте политики доступа, чтобы пользователи и приложения получили минимальный уровень доступа, который им нужно выполнить. Разработчики обычно не нуждаются в доступе к рабочей инфраструктуре, но конвейер развертывания нуждается в полном доступе. Кластеры Kubernetes не помещают образы контейнеров в реестр, но рабочие процессы GitHub могут быть. Интерфейсные API обычно не получают сообщения от брокера сообщений, а внутренние работники не обязательно отправляют новые сообщения брокеру. Эти решения зависят от рабочей нагрузки, а уровень доступа, который назначается, должен отражать функциональные возможности каждого компонента.
Примеры реализации критически важных ссылок Azure:
- Каждый компонент приложения, работающий с Центры событий Azure, использует строка подключения с разрешениями прослушивания (
BackgroundProcessor
) или отправки (CatalogService
). Этот уровень доступа гарантирует, что каждый модуль pod имеет только минимальный доступ, необходимый для выполнения своей функции. - Субъект-служба для пула агентов Служба Azure Kubernetes (AKS) имеет разрешения только на получение и перечисление секретов в Azure Key Vault.
- Удостоверение Kubelet AKS имеет только разрешение AcrPull для доступа к глобальному реестру контейнеров.
Управляемые удостоверения
Чтобы повысить безопасность критически важной рабочей нагрузки, избегайте использования секретов на основе служб, таких как строка подключения или ключи API, когда это возможно. Рекомендуется использовать управляемые удостоверения, если служба Azure поддерживает эту возможность.
Эталонная реализация использует управляемое удостоверение, назначаемое службой, в пуле агентов AKS ("удостоверение Kubelet") для доступа к глобальному Реестр контейнеров Azure и хранилищу ключей метки. Для ограничения доступа используются соответствующие встроенные роли. Например, этот код Terraform назначает только AcrPull
роль удостоверению Kubelet:
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
}
Секреты
По возможности используйте проверку подлинности Microsoft Entra вместо ключей при доступе к ресурсам Azure. Многие службы Azure, такие как Azure Cosmos DB и служба хранилища Azure, поддерживают возможность полностью отключить проверку подлинности ключей. AKS поддерживает Идентификация рабочей нагрузки Microsoft Entra.
В сценариях, в которых невозможно использовать проверку подлинности Microsoft Entra, каждая метка развертывания имеет выделенный экземпляр Key Vault для хранения ключей. Эти ключи создаются автоматически во время развертывания и хранятся в Key Vault с помощью Terraform. Ни какой человеческий оператор, кроме разработчиков в комплексных средах, не может взаимодействовать с секретами. Кроме того, политики доступа Key Vault настроены таким образом, чтобы учетные записи пользователей не разрешались получать доступ к секретам.
Примечание.
Эта рабочая нагрузка не использует пользовательские сертификаты, но те же принципы применяются.
В кластере AKS поставщик Key Vault для хранилища секретов позволяет приложению использовать секреты. Драйвер CSI загружает ключи из Key Vault и подключает их в виде файлов в отдельные модули 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 }}
Эталонная реализация использует Helm с Azure Pipelines для развертывания драйвера CSI, содержащего все имена ключей из Key Vault. Драйвер также отвечает за обновление подключенных секретов, если они изменяются в Key Vault.
В конечном итоге оба приложения .NET используют встроенную возможность для чтения конфигурации из файлов (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>();
});
Сочетание автоматической перезагрузки драйвера CSI и reloadOnChange: true
помогает гарантировать, что при изменении ключей в Key Vault новые значения подключены к кластеру. Этот процесс не гарантирует смену секретов в приложении. Реализация использует одиночный экземпляр клиента Azure Cosmos DB, который требует перезапуска модуля pod для применения изменения.
Пользовательские домены и TLS
Рабочие нагрузки на основе веб-сайтов должны использовать ПРОТОКОЛ HTTPS, чтобы предотвратить атаки на уровне взаимодействия на всех уровнях взаимодействия, например обмен данными от клиента к API или из API в API. Не забудьте автоматизировать смену сертификатов, так как просроченные сертификаты по-прежнему являются распространенными причинами сбоя и ухудшения работы.
Эталонная реализация полностью поддерживает HTTPS с пользовательскими доменными именами, такими как contoso.com
. Она также применяет соответствующую конфигурацию как к средам, так и prod
к средамint
. Вы также можете добавить пользовательские домены для e2e
сред. Однако эта эталонная реализация не использует пользовательские доменные имена из-за кратковременной природы e2e
и увеличения времени развертывания при использовании пользовательских доменов с SSL-сертификатами в Azure Front Door.
Чтобы включить полную автоматизацию развертывания, необходимо управлять личным доменом с помощью зоны Azure DNS. Конвейер развертывания инфраструктуры динамически создает записи CNAME в зоне Azure DNS и автоматически сопоставляет эти записи с экземпляром Azure Front Door.
Ssl-сертификаты, управляемые Azure Front Door, включены, что удаляет требование для продления SSL-сертификата вручную. TLS 1.2 настраивается как минимальная версия.
#
# /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"
}
}
Среды, не подготовленные с помощью пользовательских доменов, доступны через конечную точку Azure Front Door по умолчанию. Например, вы можете связаться с ними по адресу, например env123.azurefd.net
.
Примечание.
В контроллере входящего трафика кластера пользовательские домены не используются в любом случае. Вместо этого dns-имя, предоставленное Azure, например [prefix]-cluster.[region].cloudapp.azure.com
используется с let's Encrypt, которое может выдавать бесплатные SSL-сертификаты для этих конечных точек.
Эталонная реализация использует Jetstack cert-manager
для автоматической подготовки SSL/TLS-сертификатов из правил входящего трафика. Дополнительные параметры конфигурации, такие как ClusterIssuer
запросы сертификатов из Let's Encrypt, развертываются с помощью отдельной cert-manager-config
диаграммы helm, которая хранится в src/config/cert-manager/chart.
Эта реализация используется ClusterIssuer
вместо того, Issuer
чтобы избежать наличия издателей для каждого пространства имен. Дополнительные сведения см. в документации по диспетчеру сертификатов и заметках о выпуске cert-manager.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
Настройка
Все конфигурации среды выполнения приложения хранятся в Key Vault, включая секреты и нечувствительные параметры. Для хранения параметров можно использовать хранилище конфигураций, например Конфигурация приложений Azure. Однако при наличии одного хранилища уменьшается количество потенциальных точек сбоя для критически важных приложений. Используйте Key Vault для настройки среды выполнения, чтобы упростить общую реализацию.
Хранилища ключей должны заполняться конвейером развертывания. В реализации необходимые значения либо создаются непосредственно из Terraform, например из строка подключения базы данных, либо передаются в виде переменных Terraform из конвейера развертывания.
Конфигурация инфраструктуры и развертывания отдельных сред, таких как e2e
, int
и prod
, хранится в файлах переменных, которые являются частью репозитория исходного кода. Этот подход имеет два преимущества:
- Все изменения в среде отслеживаются и проходят через конвейеры развертывания, прежде чем они будут применены к среде.
- Отдельные
e2e
среды можно настроить по-разному, так как развертывание основано на коде в ветви.
Одним из исключений является хранилище конфиденциальных значений для конвейеров. Эти значения хранятся в виде секретов в группах переменных Azure DevOps.
Безопасность контейнеров
Необходимо защитить образы контейнеров для всех контейнерных рабочих нагрузок.
Эта эталонная реализация использует контейнеры Docker рабочей нагрузки, основанные на образах среды выполнения, а не пакете SDK, чтобы свести к минимуму объем памяти и потенциальную область атаки. Нет других средств, таких как ping
, wget
или curl
, установленных.
Приложение выполняется под непривилегированного пользователя workload
, созданного в процессе сборки образа:
RUN groupadd -r workload && useradd --no-log-init -r -g workload workload
USER workload
Эталонная реализация использует Helm для упаковки манифестов YAML, необходимых для развертывания отдельных компонентов. Этот процесс включает в себя развертывание Kubernetes, службы, конфигурацию горизонтального автомасштабирования pod и контекст безопасности. Все диаграммы Helm содержат базовые меры безопасности, которые соответствуют рекомендациям Kubernetes.
Эти меры безопасности:
readOnlyFilesystem
: корневая файловая система/
в каждом контейнере устанавливается только для чтения, чтобы предотвратить запись контейнера в файловую систему узла. Это ограничение запрещает злоумышленникам загружать дополнительные средства и сохранять код в контейнере. Каталоги, для которых требуется доступ на чтение и запись, устанавливаются в виде томов.privileged
: все контейнеры настроены для запуска как не привилегированных. Запуск контейнера в качестве привилегированного предоставляет все возможности контейнера, а также отменяет все ограничения, которые применяет контроллер группы управления устройствами.allowPrivilegeEscalation
: предотвращает получение дополнительных привилегий внутри контейнера, чем родительский процесс.
Эти меры безопасности также настраиваются для контейнеров, отличных от Майкрософт, и диаграммы Helm, как cert-manager
по возможности. Вы можете использовать Политика Azure для аудита этих мер безопасности.
#
# Example:
# /src/app/charts/backgroundprocessor/values.yaml
#
containerSecurityContext:
privileged: false
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
Каждая среда, в том числе prod
int
, и каждая e2e
среда, имеет выделенный экземпляр Реестра контейнеров, имеющий глобальную репликацию в каждом из регионов, где развертываются метки.
Примечание.
Эта эталонная реализация не использует сканирование уязвимостей образов Docker. Рекомендуется использовать Microsoft Defender для реестров контейнеров, потенциально с помощью GitHub Actions.
Входящий трафик
Azure Front Door — это глобальная подсистема балансировки нагрузки в этой архитектуре. Все веб-запросы направляются через Azure Front Door, который выбирает соответствующую серверную часть. Критически важные приложения должны воспользоваться другими возможностями Azure Front Door, такими как брандмауэры веб-приложений (WAFs).
Брандмауэр веб-приложения
Важной функцией Azure Front Door является WAF, так как она позволяет Azure Front Door проверять трафик, проходящий через него. В режиме предотвращения все подозрительные запросы блокируются. В реализации настраиваются два набора правил. Эти наборы правил и Microsoft_DefaultRuleSet
Microsoft_BotManagerRuleSet
.
Совет
При развертывании Azure Front Door с WAF рекомендуется начать с режима обнаружения . Внимательно отслеживайте его поведение с естественным трафиком клиента и точно настраивайте правила обнаружения. После устранения ложных срабатываний или при редких ложных срабатываниях переключитесь в режим предотвращения . Этот процесс необходим, так как каждое приложение отличается, и некоторые полезные данные могут считаться вредоносными, даже если они являются законными для этой конкретной рабочей нагрузки.
Маршрутизация
Только те запросы, которые выполняются через Azure Front Door, направляются в контейнеры API, например CatalogService
и HealthService
. Используйте конфигурацию входящего трафика Nginx, чтобы обеспечить это поведение. Он проверяет наличие заголовка X-Azure-FDID
и является ли он правильным для глобального экземпляра Azure Front Door определенной среды.
#
# /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\'\"
# ...
Конвейеры развертывания помогают обеспечить правильное заполнение этого заголовка, но также необходимо обойти это ограничение для тестов дыма, так как они проверяют каждый кластер непосредственно вместо Azure Front Door. Эталонная реализация использует тот факт, что тесты дыма выполняются в рамках развертывания. Эта конструкция позволяет известному значению заголовка и добавлено в HTTP-запросы теста дыма.
#
# /.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.
}
Защищенные развертывания
Чтобы следовать базовым хорошо спроектированным принципам операционного превосходства, полностью автоматизируйте все развертывания. Они не должны требовать никаких ручных шагов, кроме активации запуска или утверждения шлюза.
Необходимо предотвратить вредоносные попытки или случайные неправильные настройки, которые могут отключить меры безопасности. Эталонная реализация использует один и тот же конвейер для развертывания инфраструктуры и приложений, что приводит к автоматическому откату любого потенциального смещения конфигурации. Этот откат помогает обеспечить целостность инфраструктуры и выравнивание с кодом приложения. Любые изменения удаляются при следующем развертывании.
Terraform создает конфиденциальные значения для развертывания во время выполнения конвейера, или Azure DevOps предоставляет их в качестве секретов. Эти значения защищены с помощью ограничений доступа на основе ролей.
Примечание.
Рабочие процессы GitHub предоставляют аналогичную концепцию отдельных хранилищ для значений секретов. Секреты шифруются, переменные среды, которые могут использовать GitHub Actions.
Важно обратить внимание на все артефакты, создаваемые конвейером, так как эти артефакты могут потенциально содержать секретные значения или сведения о внутренней работе приложения. Развертывание Azure DevOps эталонной реализации создает два файла с выходными данными Terraform. Один файл предназначен для меток, и один файл предназначен для глобальной инфраструктуры. Эти файлы не содержат пароли, которые могут компрометации инфраструктуры. Однако эти файлы следует учитывать, так как они показывают информацию о инфраструктуре, включая идентификаторы кластеров, IP-адреса, имена учетных записей хранения, имена key Vault, имена баз данных Azure Cosmos DB и идентификаторы заголовков Azure Front Door.
Для рабочих нагрузок, использующих Terraform, необходимо предпринять дополнительные усилия для защиты файла состояния, так как он содержит полный контекст развертывания, включая секреты. Файл состояния обычно хранится в учетной записи хранения, которая должна иметь отдельный жизненный цикл от рабочей нагрузки и должна быть доступна только из конвейера развертывания. Вы должны регистрировать любой другой доступ к этому файлу и отправлять оповещения в соответствующую группу безопасности.
Обновления зависимостей
Библиотеки, платформы и инструменты, которые используются приложением, обновляются со временем. Важно регулярно выполнять эти обновления, так как они часто содержат исправления для проблем безопасности, которые могут предоставить злоумышленникам несанкционированный доступ к системе.
Эталонная реализация использует зависимости GitHub для NuGet, Docker, npm, Terraform и GitHub Actions. dependabot.yml
Файл конфигурации автоматически создается с помощью скрипта PowerShell из-за сложности различных частей приложения. Например, каждому модулю Terraform требуется отдельная запись.
#
# /.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...
- Обновления активируются ежемесячно в качестве компрометации между наличием наиболее актуальных библиотек и сохранением накладных расходов. Кроме того, ключевые инструменты, такие как Terraform, отслеживаются непрерывно, и важные обновления выполняются вручную.
- Запросы на вытягивание (PR) нацелены на
component-updates
ветвь вместоmain
. - Библиотеки Npm настроены только для проверки зависимостей, которые отправляются в скомпилированное приложение, а не для поддержки таких
@vue-cli
инструментов.
Dependabot создает отдельный pr-запрос для каждого обновления, что может перегружать группу операций. Эталонная реализация сначала собирает пакет обновлений в component-updates
ветви, а затем выполняет тесты в e2e
среде. Если эти тесты успешны, он создает другой pr-код, предназначенный для main
ветви.
Оборонительное кодирование
Вызовы API могут завершиться сбоем из-за различных причин, включая ошибки кода, неисправные развертывания и сбои инфраструктуры. Если вызов API завершается ошибкой, вызывающий объект или клиентское приложение, не должен получать подробные сведения об отладке, так как эта информация может дать злоумышленникам полезные точки данных о приложении.
Эталонная реализация демонстрирует этот принцип, возвращая только идентификатор корреляции в неудачном ответе. Он не разделяет причину сбоя, например сообщение об исключении или трассировку стека. С помощью этого идентификатора и с помощью заголовка Server-Location
оператор может исследовать инцидент с помощью 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();
});
// ...
}
Следующий шаг
Разверните эталонную реализацию, чтобы получить полное представление о ресурсах и их конфигурации.