vault-armada-app/vault-helm/vault-helm/helm-charts/vault-init.yaml

241 lines
8.3 KiB
YAML

apiVersion: v1
data:
init.sh: |
#!/bin/bash
CERT=$CA_CERT # Get the CA path from environment vars
CA_ONELINE=$(awk '{printf "%s\\n", $0}' $CERT) # Store cert as a oneliner for curl purposes
DOMAIN={{ .Release.Namespace }}.pod.cluster.local # Set the domain for resolving pod names
SVCDOMAIN={{ .Release.Namespace }}.svc.cluster.local
WORKDIR=$PVCDIR # PVC location so that keys can be persisted
# FUNCTIONS
# Creates a list of all k8s vault pods and stores in text file.
# Converts ips from X.X.X.X or a:b:c::d to X-X-X-X for use as pod dns names
function getVaultPods {
kubectl get pods \
-n {{ .Release.Namespace }} \
-l component=server,app.kubernetes.io/name=vault \
-o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIPs[].ip}{"\n"}{end}' \
> $WORKDIR/pods.txt
sed -i 's/\.\|:/-/g' $WORKDIR/pods.txt
}
# Wait for the vault servers in the stateful set to be created before initializing
function waitForPods {
CURRENT_PODS=$(kubectl get pods \
-l component=server,app.kubernetes.io/name=vault \
-o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIPs[].ip}{"\t"}{.status.phase}{"\n"} \
{end}' \
| grep Running \
| wc -l)
DESIRED_PODS={{ .Values.server.ha.replicas }}
while [ $CURRENT_PODS != $DESIRED_PODS ]; do
sleep 5
echo "Waiting for {{ template "vault.fullname" . }} statefulset running pods ($CURRENT_PODS) to equal desired pods ($DESIRED_PODS)"
CURRENT_PODS=$(kubectl get pods \
-l component=server,app.kubernetes.io/name=vault \
-o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIPs[].ip}{"\t"}{.status.phase}{"\n"} \
{end}' \
| grep Running \
| wc -l)
done
}
# Initializes the first vault pod, only needs to be performed once after deploying the helm chart
# Stores the root token and master key shards in plaintext in working directory as cluster_keys.json - insecure.
function initVault {
V0=$(awk 'NR==1{print $2}' $WORKDIR/pods.txt)
echo "Initializing $V0"
curl -s \
--cacert $CERT \
--request POST \
--data '{"secret_shares": 5, "secret_threshold": 3}' \
https://$V0.$DOMAIN:8200/v1/sys/init \
> $WORKDIR/cluster_keys.json
}
# Uses the master key shards in cluster_keys.json to unseal vault
function unsealVault {
for shard in $(cat $WORKDIR/cluster_keys.json | jq -r .keys_base64[]); do
echo {\"key\": \"$shard\"} | curl -s --cacert $CERT --request POST -d @- https://$VAULT.$DOMAIN:8200/v1/sys/unseal > /dev/null
sleep 3 #Some sleep is required to allow Raft convergence
done
}
# Takes the address of vault-0 as the cluster leader and joins other nodes to raft
function joinRaft {
CLUSTER_LEAD=$(awk 'NR==1{print $2}' $WORKDIR/pods.txt)
ROOT_TOKEN=$(cat $WORKDIR/cluster_keys.json | jq -r .root_token)
RAFT_STATUS=""
while [ "$RAFT_STATUS" != "true" ]; do
RAFT_STATUS=$(curl -s \
--cacert $CERT \
-H "X-Vault-Token: $ROOT_TOKEN" \
--request POST \
--data "{\"leader_api_addr\": \"https://sva-{{ template "vault.name" .}}-active.$SVCDOMAIN:8200\", \"leader_ca_cert\": \"$CA_ONELINE\"}" \
https://$row.$DOMAIN:8200/v1/sys/storage/raft/join)
echo "$row $RAFT_STATUS"
RAFT_STATUS=$(echo $RAFT_STATUS | jq -r .joined)
sleep 1
done
}
# Simply calls the status check of a vault, used to check if it is initialized, unsealed, or part of raft cluster
function vaultServerStatus {
curl --cacert $CERT -s https://$row.$DOMAIN:8200/v1/sys/health
}
#
# LOGIC
#
# Waiting for vault servers to come up
waitForPods
echo ""
echo "Putting a list of vault pods and ip in $WORKDIR/pods.txt"
getVaultPods
echo ""
row=$(awk 'NR==1{print $2}' $WORKDIR/pods.txt)
vaultServerStatus > $WORKDIR/healthcheck.txt
TEMP=$(cat $WORKDIR/healthcheck.txt | jq -r .initialized)
grep $row $WORKDIR/pods.txt & echo "Initialized status is $TEMP"
if [ ! -z $TEMP ] && [ $TEMP = false ]; then
echo "Initializing the vault on vault-0 and storing keys in $WORKDIR/cluster_keys.json"
initVault
cp $WORKDIR/cluster_keys.json $WORKDIR/cluster_init.json
sleep 10 #Some sleep required to allow convergence"
echo ""
echo "Unsealing vault-0 using the init shards"
for row in $(awk 'NR==1{print $2}' $WORKDIR/pods.txt); do
VAULT=$row
unsealVault
done
echo ""
echo "Joining other vault servers to the HA Raft cluster"
for row in $(awk 'NR>1{print $2}' $WORKDIR/pods.txt); do
grep $row $WORKDIR/pods.txt
joinRaft
sleep 5
done
echo ""
echo "Unsealing the remaining vaults"
for row in $(awk 'NR>1{print $2}' $WORKDIR/pods.txt); do
grep $row $WORKDIR/pods.txt
VAULT=$row
unsealVault
sleep 10
done
fi
# Loop forever to check the seal status of vaults and unseal if required
while true; do
sleep 5
echo "Checking vault pods seal status"
rm $WORKDIR/pods.txt
getVaultPods
for row in $(awk '{print $2}' $WORKDIR/pods.txt); do
vaultServerStatus > $WORKDIR/healthcheck.txt
TEMP=$(cat $WORKDIR/healthcheck.txt | jq -r .sealed)
grep $row $WORKDIR/pods.txt & echo "Sealed status is $TEMP"
if [ ! -z $TEMP ] && [ $TEMP = true ]; then
VAULT=$row
echo "Unsealing $row"
unsealVault
fi
done
done
kind: ConfigMap
metadata:
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:init.sh: {}
manager: vault-init-unseal
name: vault-init-unseal
namespace: {{ .Release.Namespace }}
---
{{- if and (eq (.Values.injector.enabled | toString) "true" ) (eq (.Values.global.enabled | toString) "true") }}
# Deployment for the unsealer
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ template "vault.fullname" . }}-manager
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/name: {{ include "vault.name" . }}-manager
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
component: webhook
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/instance: {{ .Release.Name }}
component: webhook
template:
metadata:
labels:
app.kubernetes.io/name: {{ template "vault.name" . }}-manager
app.kubernetes.io/instance: {{ .Release.Name }}
component: webhook
spec:
serviceAccountName: "{{ template "vault.fullname" . }}"
{{- if .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.global.imagePullSecrets | nindent 8 }}
{{- end }}
containers:
- name: manager
image: starlingx/stx-vault-manager:stx.5.0-v1.18.3
imagePullPolicy: "{{ .Values.injector.image.pullPolicy }}"
args:
- bash
- /opt/script/init.sh
env:
- name: PVCDIR
value: /mnt/data
- name: CA_CERT
value: /mnt/data/ca/tls.crt
volumeMounts:
- name: vault-init-unseal
mountPath: /opt/script
readOnly: false
- name: manager-pvc
mountPath: /mnt/data
readOnly: false
- name: vault-ca
mountPath: /mnt/data/ca
readOnly: true
volumes:
- name: vault-init-unseal
configMap:
name: vault-init-unseal
- name: vault-ca
secret:
secretName: vault-ca
volumeClaimTemplates:
- metadata:
name: manager-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
{{ end }}