Skip to content
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ aws/.terraform.tfstate.lock.info
# Templated
gcp/k8s/secret-volume.yml
gcp/k8s/secret-challenge-vault-deployment.yml
azure/k8s/secret-volume.yml
azure/k8s/secret-challenge-vault-deployment.yml
azure/k8s/pod-id.yml

# Challenge 12 ;-)
.github/scripts/yourkey.txt
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ ENV SPRING_PROFILES_ACTIVE=$spring_profile
ENV ARG_BASED_PASSWORD=$argBasedPassword
ENV APP_VERSION=$argBasedVersion
ENV DOCKER_ENV_PASSWORD="This is it"
ENV AZURE_KEY_VAULT_ENABLED=false

RUN echo "2vars"
RUN echo "$ARG_BASED_PASSWORD"
Expand Down
56 changes: 56 additions & 0 deletions azure/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Setup your secrets in Azure - EXPERIMENTAL

In this setup we integrate the secrets exercise with Azure AKS and let pods consume secrets from an Azure Key Vault. If you want to know more about integrating secrets with AKS, check [this link](https://azure.github.io/secrets-store-csi-driver-provider-azure/getting-started/usage/#provide-identity-to-access-key-vault).

## Pre-requisites

Have the following tools installed:

- az CLI - [Installation](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli)
- Tfenv (Optional) - [Installation](https://github.com/tfutils/tfenv)
- Terraform CLI - [Installation](https://learn.hashicorp.com/tutorials/terraform/install-cli)
- Wget - [Installation](https://www.jcchouinard.com/wget/)
- Helm [Installation](https://helm.sh/docs/intro/install/)
- Kubectl [Installation](https://kubernetes.io/docs/tasks/tools/)
- jq [Installation](https://stedolan.github.io/jq/download/)

Make sure you have an active subscription at Azure for which you have configured the credentials on the system where you will execute the steps below.

## Installation

**Note-I**: We create resources in `east us` by default. You can set the region by editing `terraform.tfvars`.

**Note-II**: The cluster you create has its access bound to the public IP of the creator. In other words: the cluster you create with this code has its access bound to your public IP-address if you apply it locally.

1. Set either a new resource group or use an existing resource group in `main.tf` (it defaults to the existing `OWASP-Projects` resource group). Note that you'll need to find/replace references to "data.azurerm_resource_group.default" to "arurerm_resource_group.default" if you want to create a new one.
2. check whether you have the right project by doing `az account show` (after `az login`).
3. If not yet enabled, register the required services for the subscription, run:
- `az provider register --namespace Microsoft.ContainerService`
- `az provider register --namespace Microsoft.KeyVault`
- `az provider register --namespace Microsoft.ManagedIdentity`
4. Run `terraform init` (if required, use `tfenv` to select TF 0.14.0 or higher )
5. Run `terraform plan` to see what will be created (optional).
6. Run `terraform apply`. Note: the apply will take 5 to 20 minutes depending on the speed of the Azure backplane.
7. Run `./k8s-vault-azure-start.sh`. Your kubeconfig file will automatically be updated.

Your AKS cluster should be visible in your resource group. Want a different region? You can modify `terraform.tfvars` or input it directly using the `region` variable in plan/apply.

Are you done playing? Please run `terraform destroy` twice to clean up.

### Test it

Run `./k8s-vault-azure-start.sh` and connect to [http://localhost:8080](http://localhost:8080) when it's ready to accept connections (you'll read the line `Forwarding from 127.0.0.1:8080 -> 8080` in your console). Now challenge 9 and 10 should be available as well.

### Clean it up

When you're done:

1. Kill the port forward.
2. Run `terraform destroy` to clean up the infrastructure.
3. Run `rm terraform.ts*` to remove local state files.

### A few things to consider

1. Does your worker node now have access as well?
2. Can you easily obtain the AKS managed identity of the Node?
3. Can you get the secrets in the Key vault? Which paths do you see?
31 changes: 31 additions & 0 deletions azure/iam.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
resource "azurerm_user_assigned_identity" "aks_pod_identity" {
resource_group_name = data.azurerm_resource_group.default.name
location = data.azurerm_resource_group.default.location
name = "wrongsecrets-identity"
}

resource "azurerm_user_assigned_identity" "aks_extra_pod_identity" {
resource_group_name = data.azurerm_resource_group.default.name
location = data.azurerm_resource_group.default.location
name = "wrongsecrets-extra-identity"
}

# Role assignments
# Details: https://github.com/Azure/aad-pod-identity/blob/master/website/content/en/docs/Getting%20started/role-assignment.md
resource "azurerm_role_assignment" "aks_identity_operator" {
scope = azurerm_user_assigned_identity.aks_pod_identity.id
role_definition_name = "Managed Identity Operator"
principal_id = azurerm_kubernetes_cluster.cluster.kubelet_identity[0].object_id
}

resource "azurerm_role_assignment" "aks_extra_identity_operator" {
scope = azurerm_user_assigned_identity.aks_extra_pod_identity.id
role_definition_name = "Managed Identity Operator"
principal_id = azurerm_kubernetes_cluster.cluster.kubelet_identity[0].object_id
}

resource "azurerm_role_assignment" "aks_vm_contributor" {
scope = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourcegroups/${azurerm_kubernetes_cluster.cluster.node_resource_group}"
role_definition_name = "Virtual Machine Contributor"
principal_id = azurerm_kubernetes_cluster.cluster.kubelet_identity[0].object_id
}
181 changes: 181 additions & 0 deletions azure/k8s-vault-azure-start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
#!/bin/bash
# set -o errexit
# set -o pipefail
# set -o nounset

function checkCommandsAvailable() {
for var in "$@"; do
if ! [ -x "$(command -v "$var")" ]; then
echo "🔥 ${var} is not installed." >&2
exit 1
else
echo "🏄 $var is installed..."
fi
done
}

checkCommandsAvailable helm minikube jq vault sed grep docker grep cat az envsubst

echo "This is a script to bootstrap the configuration. You need to have installed: helm, kubectl, jq, vault, grep, cat, sed, envsubst, and azure cli, and is only tested on mac, Debian and Ubuntu"
echo "This script is based on the steps defined in https://learn.hashicorp.com/tutorials/vault/kubernetes-minikube. Vault is awesome!"

# Most of the variables below are used in envsubst later.
export AZURE_SUBSCRIPTION_ID="$(az account show --query id --output tsv)"
export AZURE_TENANT_ID="$(az account show --query tenantId --output tsv)"

export RESOURCE_GROUP="$(terraform output -raw resource_group)"
export CLUSTER_NAME="$(terraform output -raw cluster_name)"

# for this demo, we will be deploying a user-assigned identity to the AKS node resource group
export IDENTITY_RESOURCE_GROUP="$(az aks show -g ${RESOURCE_GROUP} -n ${CLUSTER_NAME} --query nodeResourceGroup -otsv)"
export IDENTITY_NAME="wrongsecrets-identity"

export AZ_POD_RESOURCE_ID="$(terraform output -raw aad_pod_identity_resource_id)"
export AZ_POD_CLIENT_ID="$(terraform output -raw aad_pod_identity_client_id)"

export AZ_EXTRA_POD_RESOURCE_ID="$(terraform output -raw aad_extra_pod_identity_resource_id)"
export AZ_EXTRA_POD_CLIENT_ID="$(terraform output -raw aad_extra_pod_identity_client_id)"

export AZ_VAULT_URI="$(terraform output -raw vault_uri)"
export AZ_KEY_VAULT_TENANT_ID="$(terraform output -raw tenant_id)"
export AZ_KEY_VAULT_NAME="$(terraform output -raw vault_name)"

# Set the kubeconfig
az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME

kubectl get configmaps | grep 'secrets-file' &>/dev/null
if [ $? == 0 ]; then
echo "secrets config is already installed"
else
kubectl apply -f ../k8s/secrets-config.yml
fi

kubectl get secrets | grep 'funnystuff' &>/dev/null
if [ $? == 0 ]; then
echo "secrets secret is already installed"
else
kubectl apply -f ../k8s/secrets-secret.yml
fi

helm list | grep 'consul' &>/dev/null
if [ $? == 0 ]; then
echo "Consul is already installed"
else
helm repo add hashicorp https://helm.releases.hashicorp.com
helm install consul hashicorp/consul --version 0.30.0 --values ../k8s/helm-consul-values.yml
fi

while [[ $(kubectl get pods -l app=consul -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}') != "True True" ]]; do echo "waiting for Consul" && sleep 2; done

helm list | grep 'vault' &>/dev/null
if [ $? == 0 ]; then
echo "Vault is already installed"
else
helm repo add hashicorp https://helm.releases.hashicorp.com
helm install vault hashicorp/vault --version 0.9.1 --values ../k8s/helm-vault-values.yml
fi

isvaultrunning=$(kubectl get pods --field-selector=status.phase=Running)
while [[ $isvaultrunning != *"vault-0"* ]]; do echo "waiting for Vault0" && sleep 2 && isvaultrunning=$(kubectl get pods --field-selector=status.phase=Running); done
while [[ $isvaultrunning != *"vault-1"* ]]; do echo "waiting for Vaul1" && sleep 2 && isvaultrunning=$(kubectl get pods --field-selector=status.phase=Running); done
while [[ $isvaultrunning != *"vault-2"* ]]; do echo "waiting for Vaul2" && sleep 2 && isvaultrunning=$(kubectl get pods --field-selector=status.phase=Running); done

echo "Setting up port forwarding"
kubectl port-forward vault-0 8200:8200 &
echo "Unsealing Vault"
kubectl exec vault-0 -- vault operator init -key-shares=1 -key-threshold=1 -format=json >cluster-keys.json
cat cluster-keys.json | jq -r ".unseal_keys_b64[]"
VAULT_UNSEAL_KEY=$(cat cluster-keys.json | jq -r ".unseal_keys_b64[]")
kubectl exec vault-0 -- vault operator unseal $VAULT_UNSEAL_KEY
kubectl exec vault-1 -- vault operator unseal $VAULT_UNSEAL_KEY
kubectl exec vault-2 -- vault operator unseal $VAULT_UNSEAL_KEY

echo "Obtaining root token"
jq .root_token cluster-keys.json >commentedroottoken

sed "s/^\([\"']\)\(.*\)\1\$/\2/g" commentedroottoken >root_token
ROOTTOKEN=$(cat root_token)

echo "Logging in"
kubectl exec vault-0 -- vault login $ROOTTOKEN

echo "Enabling kv-v2 kubernetes"
kubectl exec vault-0 -- vault secrets enable -path=secret kv-v2

echo "Putting a secret in"
kubectl exec vault-0 -- vault kv put secret/secret-challenge vaultpassword.password="$(openssl rand -base64 16)"

echo "Enable k8s auth"
kubectl exec vault-0 -- vault auth enable kubernetes

echo "Writing k8s auth config"

kubectl exec vault-0 -- /bin/sh -c 'vault write auth/kubernetes/config \
token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt'

echo "Writing policy for secret-challenge"
kubectl exec vault-0 -- /bin/sh -c 'vault policy write secret-challenge - <<EOF
path "secret/data/secret-challenge" {
capabilities = ["read"]
}
path "secret/data/application" {
capabilities = ["read"]
}
EOF'

echo "Write secrets for secret-challenge"
kubectl exec vault-0 -- vault write auth/kubernetes/role/secret-challenge \
bound_service_account_names=vault \
bound_service_account_namespaces=default \
policies=secret-challenge \
ttl=24h &&
vault kv put secret/secret-challenge vaultpassword.password="$(openssl rand -base64 16)" &&
vault kv put secret/application vaultpassword.password="$(openssl rand -base64 16)"

echo "Add secrets manager driver to repo"
helm repo add secrets-store-csi-driver-azure https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/master/charts

helm list --namespace kube-system | grep 'csi-secrets-store' &>/dev/null
if [ $? == 0 ]; then
echo "CSI driver is already installed"
else
echo "Installing CSI driver"
helm install -n kube-system csi-secrets-store-provider-azure csi-secrets-store-provider-azure/csi-secrets-store-provider-azure --set enableSecretRotation=true --set rotationPollInterval=60s
fi

echo "Add Azure pod identity to repo"
helm repo add aad-pod-identity https://raw.githubusercontent.com/Azure/aad-pod-identity/master/charts

helm list --namespace kube-system | grep 'aad-pod-identity' &>/dev/null
if [ $? == 0 ]; then
echo "Azure pod identity chart already installed"
else
helm install aad-pod-identity aad-pod-identity/aad-pod-identity
fi

echo "Generate secret manager challenge secret 2"
az keyvault secret set --name wrongsecret-2 --vault-name "${AZ_KEY_VAULT_NAME}" --value "$(openssl rand -base64 16)" >/dev/null

echo "Generate secret manager challenge secret 3"
az keyvault secret set --name wrongsecret-3 --vault-name "${AZ_KEY_VAULT_NAME}" --value "$(openssl rand -base64 16)" >/dev/null

echo "Fill-out the secret volume manifest template"
envsubst <./k8s/secret-volume.yml.tpl >./k8s/secret-volume.yml

echo "Apply secretsmanager storage volume"
kubectl apply -f./k8s/secret-volume.yml

envsubst <./k8s/pod-id.yml.tpl >./k8s/pod-id.yml
envsubst <./k8s/secret-challenge-vault-deployment.yml.tpl >./k8s/secret-challenge-vault-deployment.yml

kubectl apply -f./k8s/pod-id.yml
kubectl apply -f./k8s/secret-challenge-vault-deployment.yml
while [[ $(kubectl get pods -l app=secret-challenge -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}') != "True" ]]; do echo "waiting for secret-challenge" && sleep 2; done
#kubectl expose deployment secret-challenge --type=LoadBalancer --port=8080
kubectl port-forward \
$(kubectl get pod -l app=secret-challenge -o jsonpath="{.items[0].metadata.name}") \
8080:8080 \
;
echo "Run terraform destroy to clean everything up."
33 changes: 33 additions & 0 deletions azure/k8s/pod-id.yml.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
apiVersion: "aadpodidentity.k8s.io/v1"
kind: AzureIdentity
metadata:
name: wrongsecrets-pod-id
spec:
type: 0
resourceID: ${AZ_POD_RESOURCE_ID}
clientID: ${AZ_POD_CLIENT_ID}
---
apiVersion: "aadpodidentity.k8s.io/v1"
kind: AzureIdentityBinding
metadata:
name: wrongsecrets-podid-binding
spec:
azureIdentity: wrongsecrets-pod-id
selector: wrongsecrets-pod-id
---
apiVersion: "aadpodidentity.k8s.io/v1"
kind: AzureIdentity
metadata:
name: separate-workload-pod-id
spec:
type: 0
resourceID: ${AZ_EXTRA_POD_RESOURCE_ID}
clientID: ${AZ_EXTRA_POD_CLIENT_ID}
---
apiVersion: "aadpodidentity.k8s.io/v1"
kind: AzureIdentityBinding
metadata:
name: wrongsecrets-extra-podid-binding
spec:
azureIdentity: separate-workload-pod-id
selector: separate-workload-pod-id
Loading