241 lines
8.3 KiB
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 }}
|