-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathhooks-enr-job.yaml
More file actions
243 lines (227 loc) · 12.8 KB
/
Copy pathhooks-enr-job.yaml
File metadata and controls
243 lines (227 loc) · 12.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
{{- if eq (include "dv-pod.shouldCreateEnrJob" .) "true" -}}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "dv-pod.fullname" . }}-enr-job
namespace: {{ .Release.Namespace }}
labels:
{{- include "dv-pod.labels" . | nindent 4 }}
app.kubernetes.io/component: enr-job
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-5" # After RBAC, before main statefulset
"helm.sh/hook-delete-policy": "before-hook-creation"
{{- if .Values.charon.enr.generate.annotations }}
{{- toYaml .Values.charon.enr.generate.annotations | nindent 4 }}
{{- end }}
spec:
template:
metadata:
name: {{ include "dv-pod.fullname" . }}-enr-job-pod
labels:
{{- include "dv-pod.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: enr-job-pod
spec:
restartPolicy: Never
serviceAccountName: {{ include "dv-pod.fullname" . }}-enr-job # Service account with permission to access k8s secrets
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
volumes:
- name: enr-data
emptyDir: {}
initContainers:
- name: check-existing-secret
image: "{{ .Values.charon.enr.generate.kubectlImage.repository }}:{{ .Values.charon.enr.generate.kubectlImage.tag }}"
imagePullPolicy: {{ .Values.charon.enr.generate.kubectlImage.pullPolicy }}
command:
- /bin/sh
- -c
- |
set -e
# Get the name of the k8s secret where the private key and public address are saved
NAMESPACE="{{ .Release.Namespace }}"
SECRET_NAME="{{ include "dv-pod.enrSecretName" . }}"
# Get the name of the key the private key value is saved as within the secret
ENR_PRIVATE_KEY_DATA_NAME="{{ include "dv-pod.enrSecretDataKey" . }}"
# Get the name of the key the ENR address value is saved as within the secret
ENR_PUBLIC_ADDRESS_DATA_NAME="{{ include "dv-pod.enrSecretPublicDataKey" . }}"
# Check if the secret already exists
echo "Checking if secret '$SECRET_NAME' already exists in namespace '$NAMESPACE'..."
if kubectl get secret "${SECRET_NAME}" -n "${NAMESPACE}" >/dev/null 2>&1; then
echo "INFO: Secret '$SECRET_NAME' already exists. Checking field completeness..."
# Check if both required fields exist in the secret
HAS_PRIVATE_KEY=""
HAS_PUBLIC_ENR=""
if kubectl get secret "${SECRET_NAME}" -n "${NAMESPACE}" -o jsonpath="{.data.${ENR_PRIVATE_KEY_DATA_NAME}}" >/dev/null 2>&1; then
PRIVATE_KEY_DATA=$(kubectl get secret "${SECRET_NAME}" -n "${NAMESPACE}" -o jsonpath="{.data.${ENR_PRIVATE_KEY_DATA_NAME}}")
if [ -n "$PRIVATE_KEY_DATA" ]; then
HAS_PRIVATE_KEY="true"
echo "INFO: Private key field '${ENR_PRIVATE_KEY_DATA_NAME}' found in secret."
fi
fi
if kubectl get secret "${SECRET_NAME}" -n "${NAMESPACE}" -o jsonpath="{.data.${ENR_PUBLIC_ADDRESS_DATA_NAME}}" >/dev/null 2>&1; then
PUBLIC_ENR_DATA=$(kubectl get secret "${SECRET_NAME}" -n "${NAMESPACE}" -o jsonpath="{.data.${ENR_PUBLIC_ADDRESS_DATA_NAME}}")
if [ -n "$PUBLIC_ENR_DATA" ]; then
HAS_PUBLIC_ENR="true"
echo "INFO: Public ENR field '${ENR_PUBLIC_ADDRESS_DATA_NAME}' found in secret."
fi
fi
# If both fields exist, skip generation entirely
if [ "$HAS_PRIVATE_KEY" = "true" ] && [ "$HAS_PUBLIC_ENR" = "true" ]; then
echo "INFO: Both private key and public ENR are present. Skipping ENR generation."
touch /enr-shared/skip-generation
# If only private key exists, generate the missing public ENR
elif [ "$HAS_PRIVATE_KEY" = "true" ] && [ "$HAS_PUBLIC_ENR" != "true" ]; then
echo "INFO: Private key exists but public ENR is missing. Will generate public ENR from existing private key."
# Decode the private key and save it for charon to use
echo "$PRIVATE_KEY_DATA" | base64 -d > /enr-shared/existing-private-key
touch /enr-shared/generate-enr-only
# If neither field exists or only public ENR exists (unusual case), do full generation
else
echo "INFO: Secret exists but required fields are missing or incomplete. Will proceed with full ENR generation."
fi
else
echo "Secret '$SECRET_NAME' does not exist. ENR generation will proceed."
fi
volumeMounts:
- name: enr-data
mountPath: /enr-shared
containers:
- name: charon-key-generator
image: {{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
- /bin/sh
- -c
- |
set -e
# Check if we should skip generation entirely
if [ -f /enr-shared/skip-generation ]; then
echo "INFO: Skipping ENR generation as secret already contains both fields."
exit 0
fi
mkdir -p /enr-output
# Check if we only need to generate public ENR from existing private key
if [ -f /enr-shared/generate-enr-only ]; then
echo "INFO: Generating public ENR from existing private key in secret."
# Copy the existing private key to the expected location
cp /enr-shared/existing-private-key /enr-output/charon-enr-private-key
chmod 600 /enr-output/charon-enr-private-key
echo "INFO: Deriving public ENR from existing private key using 'charon enr --data-dir /enr-output' ..."
# 'charon enr --data-dir' will read the key from /enr-output/charon-enr-private-key
PUBLIC_ENR_STRING=$(charon enr --data-dir /enr-output)
elif [ -n "{{ .Values.charon.enr.privateKey }}" ]; then
echo "INFO: Using provided ENR private key from values."
# Remove 0x prefix and write to the default location within data-dir
echo "{{ .Values.charon.enr.privateKey | trimPrefix "0x" }}" > /enr-output/charon-enr-private-key
chmod 600 /enr-output/charon-enr-private-key
echo "INFO: Deriving public ENR from provided private key using 'charon enr --data-dir /enr-output' ..."
# 'charon enr --data-dir' will read the key from /enr-output/charon-enr-private-key
PUBLIC_ENR_STRING=$(charon enr --data-dir /enr-output)
else
echo "INFO: No private key provided, generating new ENR private key and public ENR using 'charon create enr --data-dir /enr-output' ..."
# 'charon create enr --data-dir' will generate the private key file in /enr-output
# and output the public ENR string to stdout.
PUBLIC_ENR_STRING=$(charon create enr --data-dir /enr-output)
# Ensure the generated private key is readable only by the owner
if [ -f "/enr-output/charon-enr-private-key" ]; then
echo "INFO: Setting secure permissions on generated /enr-output/charon-enr-private-key"
chmod 600 /enr-output/charon-enr-private-key
else
echo "ERROR: Expected private key /enr-output/charon-enr-private-key not found after generation."
exit 1 # or handle error appropriately
fi
fi
echo "INFO: Public ENR String: ${PUBLIC_ENR_STRING}"
# The PUBLIC_ENR_STRING might contain log lines from charon, extract just the ENR string.
# A common pattern is that the ENR string itself is on the last line or is prefixed.
# Assuming ENR is the last non-empty line of output, or adjust if charon's output is different.
# A simple way: filter for 'enr:' prefix.
CLEAN_PUBLIC_ENR_STRING=$(echo "${PUBLIC_ENR_STRING}" | grep '^enr:' | tail -n 1)
if [ -z "${CLEAN_PUBLIC_ENR_STRING}" ]; then
echo "ERROR: Could not extract clean ENR string from output: ${PUBLIC_ENR_STRING}"
exit 1
fi
echo "${CLEAN_PUBLIC_ENR_STRING}" > /enr-output/enr
# Ensure the private key file is present and not empty
if [ ! -s "/enr-output/charon-enr-private-key" ]; then
echo "ERROR: charon-enr-private-key not found or empty in /enr-output."
ls -l /enr-output # For debugging
exit 1
fi
echo "INFO: ENR private key and public string prepared in /enr-output."
volumeMounts:
- name: enr-data
mountPath: /enr-output
- name: enr-data
mountPath: /enr-shared
- name: kubectl-secret-creator
image: "{{ .Values.charon.enr.generate.kubectlImage.repository }}:{{ .Values.charon.enr.generate.kubectlImage.tag }}"
imagePullPolicy: {{ .Values.charon.enr.generate.kubectlImage.pullPolicy }}
command:
- /bin/sh
- -c
- |
set -e
SECRET_NAME="{{ include "dv-pod.enrSecretName" . }}"
NAMESPACE="{{ .Release.Namespace }}"
ENR_PUBLIC_ADDRESS_DATA_NAME="{{ include "dv-pod.enrSecretPublicDataKey" . }}"
# Check if we should skip (double-check in case secret was created between containers)
if [ -f /enr-shared/skip-generation ]; then
echo "INFO: Skipping secret operations as it already contains both fields."
exit 0
fi
echo "Waiting for ENR key files in /enr-input..."
# Wait for the private key file and the public ENR string file
while [ ! -f /enr-input/charon-enr-private-key ] || [ ! -f /enr-input/enr ]; do
echo "Waiting for /enr-input/charon-enr-private-key and /enr-input/enr..."
ls -l /enr-input # For debugging
sleep 2
done
echo "ENR key files found."
PRIVATE_KEY=$(cat /enr-input/charon-enr-private-key)
PUBLIC_ENR=$(cat /enr-input/enr)
if [ -z "$PRIVATE_KEY" ] || [ -z "$PUBLIC_ENR" ]; then
echo "ERROR: Private key or public ENR is empty."
echo "Private Key Content: $(cat /enr-input/charon-enr-private-key | wc -c) characters"
echo "Public ENR Content: $(cat /enr-input/enr)"
exit 1
fi
# Check if we're only adding the missing ENR field to an existing secret
if [ -f /enr-shared/generate-enr-only ]; then
echo "INFO: Adding missing public ENR field to existing secret '$SECRET_NAME'..."
# Use kubectl patch to add only the missing field
kubectl patch secret "${SECRET_NAME}" -n "${NAMESPACE}" --type='merge' --validate=false -p="{\"data\":{\"${ENR_PUBLIC_ADDRESS_DATA_NAME}\":\"$(echo -n "$PUBLIC_ENR" | base64 -w 0)\"}}"
echo "Secret '$SECRET_NAME' updated with missing ENR field successfully."
else
# Check again if the secret already exists (race condition protection)
echo "Double-checking if secret '$SECRET_NAME' already exists in namespace '$NAMESPACE'..."
if kubectl get secret "${SECRET_NAME}" -n "${NAMESPACE}" >/dev/null 2>&1; then
echo "INFO: Secret '$SECRET_NAME' already exists. Skipping creation."
exit 0
fi
echo "Creating new secret '$SECRET_NAME' with both fields..."
# Using kubectl create secret generic with --dry-run and kubectl apply for idempotency
kubectl create secret generic "${SECRET_NAME}" \
--namespace="${NAMESPACE}" \
--from-literal=charon-enr-private-key="${PRIVATE_KEY}" \
--from-literal=enr="${PUBLIC_ENR}" \
--dry-run=client -o yaml | kubectl apply --validate=false -f -
echo "Secret '$SECRET_NAME' created successfully."
fi
env:
- name: GODEBUG
value: "x509sha1=1,tls13=0"
volumeMounts:
- name: enr-data
mountPath: /enr-input
- name: enr-data
mountPath: /enr-shared
{{- with .Values.charon.enr.generate.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
backoffLimit: 3 # Number of retries before considering the job failed
{{- end }}