Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Key Vault secret provider for TLS certificates #292

Merged
merged 10 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 17 additions & 20 deletions deployment/bin/deploy
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#!/bin/bash

set -e

source bin/lib

if [[ "${CI}" ]]; then
Expand All @@ -25,16 +24,12 @@ Options:
--skip-tf-init: Skip running terrform init.
--skip-functions: Don't run function publish.
--skip-fetch-tf-vars: Skip fetching terraform variables.
--user-auth: Use mounted Azure user credentials
-y: Skip confirmation.

"
}

require_env "ARM_SUBSCRIPTION_ID"
require_env "ARM_TENANT_ID"
require_env "ARM_CLIENT_ID"
require_env "ARM_USE_OIDC"


###################
# Parse arguments #
Expand Down Expand Up @@ -79,6 +74,10 @@ while [[ "$#" -gt 0 ]]; do case $1 in
SKIP_FETCH_TF_VARS=1
shift
;;
--user-auth)
export USER_AUTH=1
shift
;;
--help)
usage
exit 0
Expand All @@ -100,6 +99,16 @@ if [[ -z ${TERRAFORM_DIR} ]]; then
exit 1
fi

if [[ -z $USER_AUTH ]]; then
require_env "ARM_SUBSCRIPTION_ID"
require_env "ARM_TENANT_ID"
require_env "ARM_CLIENT_ID"
require_env "ARM_USE_OIDC"

require_env "AZURE_TENANT_ID"
require_env "AZURE_CLIENT_ID"
fi

setup_env

# ---------------------------------------------------
Expand Down Expand Up @@ -204,19 +213,6 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then

setup_helm

# Install cert-manager

echo "==================="
echo "== cert-manager ==="
echo "==================="

helm upgrade --install \
cert-manager \
--namespace pc \
--create-namespace \
--version v1.6.0 \
--set installCRDs=true jetstack/cert-manager

echo "=================="
echo "===== Argo ======="
echo "=================="
Expand Down Expand Up @@ -297,7 +293,8 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
--set controller.service.loadBalancerIP="${INTERNAL_INGRESS_IP}" \
--set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-internal"=true \
--wait \
--timeout 2m0s
--timeout 2m0s \
-f bin/nginx-values.yaml


######################
Expand Down
6 changes: 6 additions & 0 deletions deployment/bin/lib
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ function gather_tf_output() {
export INGRESS_IP=$(tf_output ingress_ip)
export DNS_LABEL=$(tf_output dns_label)
export INTERNAL_INGRESS_IP=$(tf_output internal_ingress_ip)
export AZURE_TENANT=$(tf_output tenant_id)
export KEYVAULT_NAME=$(tf_output secret_provider_keyvault_name)
export SECRET_PROVIDER_MANAGED_IDENTITY_ID=$(tf_output secret_provider_managed_identity_id)
export SECRET_PROVIDER_KEYVAULT_SECRET=$(tf_output secret_provider_keyvault_secret)

if [ "${1}" ]; then
popd
Expand Down Expand Up @@ -85,6 +89,8 @@ function cluster_login() {
kubelogin convert-kubeconfig \
-l azurecli \
--kubeconfig=kubeconfig
# --client-id ${ARM_CLIENT_ID} \
# --client-secret ${ARM_CLIENT_SECRET} \
export KUBECONFIG=kubeconfig
}

Expand Down
16 changes: 16 additions & 0 deletions deployment/bin/nginx-values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
controller:
podLabels:
azure.workload.identity/use: "true"
extraVolumes:
- name: secrets-store-inline
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "keyvault"
extraVolumeMounts:
- name: secrets-store-inline
mountPath: "/mnt/secrets-store"
readOnly: true
extraArgs:
default-ssl-certificate: pc/planetarycomputer-test-certificate
22 changes: 11 additions & 11 deletions deployment/helm/deploy-values.template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -103,24 +103,24 @@ pcingress:
cert:
secretName: "pctasks-tls-secret"

certIssuer:
enabled: true
privateKeySecretRef: "{{ tf.cluster_cert_issuer }}"
server: "{{ tf.cluster_cert_server }}"
issuerEmail: "[email protected]"
secretName: "pctasks-tls-secret"

ingress:
enabled: true
tlsHost: "{{ tf.cloudapp_hostname }}"
tlsHost: "planetarycomputer-test.microsoft.com"
hosts:
- "{{ tf.cloudapp_hostname }}"
- "planetarycomputer-test.microsoft.com"
- "{{ tf.api_management_name }}.azure-api.net"
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: "{{ tf.cluster_cert_issuer }}-pcingress"
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
nginx.ingress.kubernetes.io/proxy-buffers-number: "8"

secretProvider:
create: true
namespace: "pc"
providerName: "keyvault"
userAssignedIdentityID: "{{ env.SECRET_PROVIDER_MANAGED_IDENTITY_ID }}"
tenantId: "{{ env.AZURE_TENANT }}"
keyvaultName: "{{ env.KEYVAULT_NAME }}"
keyvaultCertificateName: "{{ env.SECRET_PROVIDER_KEYVAULT_SECRET }}"
kubernetesCertificateSecretName: "{{ env.SECRET_PROVIDER_KEYVAULT_SECRET }}"
2 changes: 1 addition & 1 deletion deployment/helm/pc-tasks-ingress/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ Application information:
{{ include "pcingress.selectorLabels" . }}
Ingress host: {{ .Values.pcingress.ingress.host }}
Service Fullname: {{ include "pcingress.fullname" . }}
Cert enabled: {{ .Values.pcingress.certIssuer.enabled }}
KeyVault secret provider created: {{ .Values.secretProvider.create }}
20 changes: 0 additions & 20 deletions deployment/helm/pc-tasks-ingress/templates/cluster_issuer.yaml

This file was deleted.

2 changes: 1 addition & 1 deletion deployment/helm/pc-tasks-ingress/templates/ingress.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ spec:
tls:
- hosts:
- {{ .Values.pcingress.ingress.tlsHost }}
secretName: {{ .Values.pcingress.cert.secretName }}
secretName: {{ .Values.secretProvider.kubernetesCertificateSecretName }}
rules:
{{- range .Values.pcingress.ingress.hosts }}
- host: {{ . }}
Expand Down
28 changes: 28 additions & 0 deletions deployment/helm/pc-tasks-ingress/templates/secret-provider.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{{- if .Values.secretProvider.create -}}
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: {{ .Values.secretProvider.providerName }}
namespace: {{ .Values.secretProvider.namespace }}
spec:
provider: azure
secretObjects:
- secretName: {{ .Values.secretProvider.kubernetesCertificateSecretName }}
type: kubernetes.io/tls
data:
- objectName: {{ .Values.secretProvider.keyvaultCertificateName }}
key: tls.crt
- objectName: {{ .Values.secretProvider.keyvaultCertificateName }}
key: tls.key
parameters:
usePodIdentity: "false"
clientID: "{{ .Values.secretProvider.userAssignedIdentityID }}"
keyvaultName: "{{ .Values.secretProvider.keyvaultName }}"
tenantId: "{{ .Values.secretProvider.tenantId }}"
cloudName: ""
objects: |
array:
- |
objectName: {{ .Values.secretProvider.keyvaultCertificateName }}
objectType: secret
{{- end }}
16 changes: 10 additions & 6 deletions deployment/helm/pc-tasks-ingress/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,21 @@ pcingress:
cert:
secretName: ""

certIssuer:
enabled: false
privateKeySecretRef: "letsencrypt-staging"
server: "https://acme-staging-v02.api.letsencrypt.org/directory"
issuerEmail: ""

ingress:
enabled: false
tlsHost: ""
hosts: []
annotations: {}

secretProvider:
create: true
providerName: "keyvault"
namespace: ""
userAssignedIdentityID: ""
tenantId: ""
keyvaultName: ""
keyvaultCertificateName: ""
kubernetesCertificateSecretName: ""

nameOverride: ""
fullnameOverride: ""
2 changes: 1 addition & 1 deletion deployment/terraform/batch_pool/providers.tf
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "3.97.1"
version = "3.103.1"
}
}
}
32 changes: 32 additions & 0 deletions deployment/terraform/resources/aks.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ resource "azurerm_kubernetes_cluster" "pctasks" {
dns_prefix = "${local.prefix}-cluster"
kubernetes_version = var.k8s_version

key_vault_secrets_provider {
secret_rotation_enabled = true
}
oidc_issuer_enabled = true
workload_identity_enabled = true

oms_agent {
log_analytics_workspace_id = azurerm_log_analytics_workspace.pctasks.id
}
Expand Down Expand Up @@ -149,3 +155,29 @@ resource "azurerm_role_assignment" "network" {
role_definition_name = "Network Contributor"
principal_id = azurerm_kubernetes_cluster.pctasks.identity[0].principal_id
}

# When you enable the key vault secrets provider block in an AKS cluster,
# this identity is created in the node resource group. Altough it technically
# is a property of the cluster resource under addProfiles.azureKeyvaultSecretsProvider.identity.resourceId
# the terraform provider doesn't know about it so we need to manually tell terraform this thing exists
data "azurerm_user_assigned_identity" "key_vault_secrets_provider_identity" {
resource_group_name = azurerm_kubernetes_cluster.pctasks.node_resource_group
name = "azurekeyvaultsecretsprovider-${azurerm_kubernetes_cluster.pctasks.name}"
}

resource "azurerm_federated_identity_credential" "cluster" {
name = "federated-id-${local.prefix}-${var.environment}"
resource_group_name = azurerm_kubernetes_cluster.pctasks.node_resource_group
audience = ["api://AzureADTokenExchange"]
issuer = azurerm_kubernetes_cluster.pctasks.oidc_issuer_url
subject = "system:serviceaccount:pc:nginx-ingress-ingress-nginx"
parent_id = data.azurerm_user_assigned_identity.key_vault_secrets_provider_identity.id
timeouts {}
}

# Left here as an exercise for the reader in PIM elevation
# resource "azurerm_role_assignment" "certificateAccess" {
# scope = "/subscriptions/9da7523a-cb61-4c3e-b1d4-afa5fc6d2da9/resourceGroups/pc-manual-resources/providers/Microsoft.KeyVault/vaults/pc-deploy-secrets"
# role_definition_name = "Key Vault Secrets User"
# principal_id = azurerm_kubernetes_cluster.pctasks.key_vault_secrets_provider[0].secret_identity[0].object_id
# }
18 changes: 18 additions & 0 deletions deployment/terraform/resources/output.tf
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,24 @@ output "location" {
value = local.location
}

output "tenant_id" {
value = data.azurerm_client_config.current.tenant_id
}

## Ingress

output "secret_provider_keyvault_name" {
value = var.secret_provider_keyvault_name
}

output "secret_provider_managed_identity_id" {
value = azurerm_kubernetes_cluster.pctasks.key_vault_secrets_provider[0].secret_identity[0].client_id
}

output "secret_provider_keyvault_secret" {
value = var.secret_provider_keyvault_secret
}

## AKS

output "cluster_name" {
Expand Down
2 changes: 1 addition & 1 deletion deployment/terraform/resources/providers.tf
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "3.97.1"
version = "3.103.1"
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions deployment/terraform/resources/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ variable "region" {
type = string
}

# Ingress
variable "secret_provider_keyvault_name" {
type = string
description = "The name of the KeyVault that holds the secrets"
default = "pc-deploy-secrets"
}

variable "secret_provider_keyvault_secret" {
type = string
description = "The name of the certificate in the KeyVault for TLS ingress"
default = "planetarycomputer-test-certificate"
}

# APIM

variable "apim_sku_name" {
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.console.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ services:
dockerfile: Dockerfile.dev
volumes:
- .:/opt/src
- ~/.azure:/root/.azure
environment:
- PCTASKS_CLIENT__ENDPOINT=http://nginx/tasks
- PCTASKS_CLIENT__API_KEY=hunter2
Expand Down Expand Up @@ -111,3 +112,4 @@ services:
networks:
default:
name: pctasks-network
external: true
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: "2.1"
services:
azurite:
container_name: pctasks-azurite
image: mcr.microsoft.com/azure-storage/azurite:3.27.0
image: mcr.microsoft.com/azure-storage/azurite:3.30.0
hostname: azurite
command: "azurite --silent --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost
0.0.0.0 -l /workspace"
Expand Down
Loading