Skip to content

Commit a4d79b9

Browse files
committed
docs: finalize tasks
1 parent 97af51c commit a4d79b9

3 files changed

Lines changed: 323 additions & 3 deletions

File tree

labs/k8s/SECRETS.md

Lines changed: 300 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Task 1
1+
## Task 1
22

33
Create secrets via:
44

@@ -29,7 +29,7 @@ I used [cyberchef](https://gchq.github.io/CyberChef/#recipe=From_Base64('A-Za-z0
2929

3030
![alt](./assets/image.png)
3131

32-
# Task 2
32+
## Task 2
3333

3434
See git diffs how values.yaml and deployment.yaml was changed. [secrets.yaml](./service.yaml) created
3535

@@ -86,4 +86,301 @@ python-info-service-python-info-service-7f8959cf57-jwj8x 1/1 Running 0
8686
kubectl exec -it python-info-service-python-i
8787
nfo-service-8654bfdbb6-ntgwk -- printenv | grep -E 'usename|password|APP_'
8888
password=change-me
89-
```
89+
```
90+
91+
## Task 3
92+
93+
HashiCorp Vault deployment:
94+
95+
```bash
96+
kubectl get pods
97+
NAME READY STATUS RESTARTS AGE
98+
python-info-service-python-info-service-8654bfdbb6-ntgwk 1/1 Running 1 (19m ago) 33m
99+
python-info-service-python-info-service-8654bfdbb6-vvp4n 1/1 Running 1 (19m ago) 33m
100+
python-info-service-python-info-service-8654bfdbb6-wltkh 1/1 Running 1 (19m ago) 33m
101+
vault-0 1/1 Running 0 29m
102+
vault-agent-injector-848dd747d7-dkf66 1/1 Running 1 (19m ago) 29m
103+
(.venv) projacktor@projacktorLaptop ~/P/e/D/l/k8s (lab11)> kubectl exec -it vault
104+
-0 -- /bin/sh
105+
```
106+
107+
vault work
108+
109+
```sh
110+
/ $ export VAULT_ADDR=http://127.0.0.1:8200
111+
/ $ vault status
112+
Key Value
113+
--- -----
114+
Seal Type shamir
115+
Initialized true
116+
Sealed false
117+
Total Shares 1
118+
Threshold 1
119+
Version 1.21.2
120+
Build Date 2026-01-06T08:33:05Z
121+
Storage Type inmem
122+
Cluster Name vault-cluster-956efc88
123+
Cluster ID 8e8fe3e0-4f1f-8863-5c5a-36cff09c17c6
124+
HA Enabled false
125+
/ $ vault login root
126+
Success! You are now authenticated. The token information displayed below
127+
is already stored in the token helper. You do NOT need to run "vault login"
128+
again. Future Vault requests will automatically use this token.
129+
130+
Key Value
131+
--- -----
132+
token root
133+
token_accessor TBPUXazHkRbNCf0hiXqzD4MY
134+
token_duration ∞
135+
token_renewable false
136+
token_policies ["root"]
137+
identity_policies []
138+
policies ["root"]
139+
/ $ vault secrets enable -path=secret kv-v2
140+
Error enabling: Error making API request.
141+
142+
URL: POST http://127.0.0.1:8200/v1/sys/mounts/secret
143+
Code: 400. Errors:
144+
145+
* path is already in use at secret/
146+
/ $ vault kv put secret/python-info-service
147+
Must supply data
148+
/ $ vault kv put secret/python-info-service username=change-me password=change-me
149+
========= Secret Path =========
150+
secret/data/python-info-service
151+
152+
======= Metadata =======
153+
Key Value
154+
--- -----
155+
created_time 2026-04-09T20:13:53.908958525Z
156+
custom_metadata <nil>
157+
deletion_time n/a
158+
destroyed false
159+
version 1
160+
/ $ vault kv get secret/python-info-service
161+
========= Secret Path =========
162+
secret/data/python-info-service
163+
164+
======= Metadata =======
165+
Key Value
166+
--- -----
167+
created_time 2026-04-09T20:13:53.908958525Z
168+
custom_metadata <nil>
169+
deletion_time n/a
170+
destroyed false
171+
version 1
172+
173+
====== Data ======
174+
Key Value
175+
--- -----
176+
password change-me
177+
username change-me
178+
/ $ vault auth enable kubernetes
179+
Success! Enabled kubernetes auth method at: kubernetes/
180+
/ $ export K8S_HOST=htts://kubernetes.default.svc:443
181+
/ $ export K8S_CA_CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
182+
/ $ export TOKEN_REVIEW_JWT=$(cat /var/run/secrets/kubernetes.io/serviceaccount/t
183+
oken)
184+
/ $ export K8S_HOST=https://kubernetes.default.svc:443
185+
/ $ vault write auth/kubernetes/role/python-info-service bound_service_account_na
186+
mes=default bound_service_account_namespaces=default policies=python-info-service
187+
ttl=24h
188+
WARNING! The following warnings were returned from Vault:
189+
190+
* Role python-info-service does not have an audience configured. While
191+
audiences are not required, consider specifying one if your use case would
192+
benefit from additional JWT claim verification.
193+
194+
/ $ vault read auth/kubernetes/role/python-info-service
195+
Key Value
196+
--- -----
197+
alias_name_source serviceaccount_uid
198+
bound_service_account_names [default]
199+
bound_service_account_namespace_selector n/a
200+
bound_service_account_namespaces [default]
201+
policies [python-info-service]
202+
token_bound_cidrs []
203+
token_explicit_max_ttl 0s
204+
token_max_ttl 0s
205+
token_no_default_policy false
206+
token_num_uses 0
207+
token_period 0s
208+
token_policies [python-info-service]
209+
token_ttl 24h
210+
token_type default
211+
ttl 24h
212+
/ $ vault write auth/kubernetes/config \
213+
> token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
214+
> kubernetes_host="https://kubernetes.default.svc:443" \
215+
> kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
216+
Success! Data written to: auth/kubernetes/config
217+
/ $ vault policy write python-info-service - <<'EOF'
218+
> path "secret/data/python-info-service" {
219+
> capabilities = ["read"]
220+
> }
221+
> EOF
222+
Success! Uploaded policy: python-info-service
223+
/ $ vault write auth/kubernetes/role/python-info-service \
224+
> bound_service_account_names=default \
225+
> bound_service_account_namespaces=default \
226+
> policies=python-info-service \
227+
> ttl=24h
228+
WARNING! The following warnings were returned from Vault:
229+
230+
* Role python-info-service does not have an audience configured. While
231+
audiences are not required, consider specifying one if your use case would
232+
benefit from additional JWT claim verification.
233+
234+
/ $ vault read auth/kubernetes/config
235+
Key Value
236+
--- -----
237+
disable_iss_validation true
238+
disable_local_ca_jwt false
239+
issuer n/a
240+
kubernetes_ca_cert -----BEGIN CERTIFICATE-----
241+
# my cert hidden :))
242+
-----END CERTIFICATE-----
243+
kubernetes_host https://kubernetes.default.svc:443
244+
pem_keys []
245+
token_reviewer_jwt_set true
246+
use_annotations_as_alias_metadata false
247+
/ $ vault policy read python-info-service
248+
path "secret/data/python-info-service" {
249+
capabilities = ["read"]
250+
}
251+
/ $ vault read auth/kubernetes/role/python-info-service
252+
Key Value
253+
--- -----
254+
alias_name_source serviceaccount_uid
255+
bound_service_account_names [default]
256+
bound_service_account_namespace_selector n/a
257+
bound_service_account_namespaces [default]
258+
policies [python-info-service]
259+
token_bound_cidrs []
260+
token_explicit_max_ttl 0s
261+
token_max_ttl 0s
262+
token_no_default_policy false
263+
token_num_uses 0
264+
token_period 0s
265+
token_policies [python-info-service]
266+
token_ttl 24h
267+
token_type default
268+
ttl 24h
269+
/ $ vault kv get secret/python-info-service
270+
========= Secret Path =========
271+
secret/data/python-info-service
272+
273+
======= Metadata =======
274+
Key Value
275+
--- -----
276+
created_time 2026-04-09T20:13:53.908958525Z
277+
custom_metadata <nil>
278+
deletion_time n/a
279+
destroyed false
280+
version 1
281+
282+
====== Data ======
283+
Key Value
284+
--- -----
285+
password change-me
286+
username change-me
287+
```
288+
289+
Check if applied
290+
291+
```bash
292+
command terminated with exit code 130
293+
(.venv) projacktor@projacktorLaptop ~/P/e/D/l/k8s (lab11) [0|SIGINT]> helm upgrade --install pyth
294+
on-info-service ./python-info-service --set vault.enabled=true
295+
Release "python-info-service" has been upgraded. Happy Helming!
296+
NAME: python-info-service
297+
LAST DEPLOYED: Thu Apr 9 23:28:05 2026
298+
NAMESPACE: default
299+
STATUS: deployed
300+
REVISION: 3
301+
DESCRIPTION: Upgrade complete
302+
TEST SUITE: None
303+
NOTES:
304+
1. Get the application URL:
305+
export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services python-info-service-python-info-service)
306+
export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")
307+
echo http://$NODE_IP:$NODE_PORT
308+
(.venv) projacktor@projacktorLaptop ~/P/e/D/l/k8s (lab11)> kubectl get pods
309+
NAME READY STATUS RESTARTS AGE
310+
python-info-service-python-info-service-8654bfdbb6-ntgwk 1/1 Running 1 (36m ago) 49m
311+
python-info-service-python-info-service-8654bfdbb6-vvp4n 1/1 Running 1 (36m ago) 49m
312+
python-info-service-python-info-service-8654bfdbb6-wltkh 1/1 Running 1 (36m ago) 49m
313+
python-info-service-python-info-service-d8cb78fc7-4jssc 1/2 Running 0 5s
314+
vault-0 1/1 Running 0 45m
315+
vault-agent-injector-848dd747d7-dkf66 1/1 Running 1 (36m ago) 45m
316+
317+
kubectl describe pod python-info-servi
318+
ce-python-info-service-d8cb78fc7-4jssc | grep vault.hashicorp.com
319+
Annotations: vault.hashicorp.com/agent-inject: true
320+
vault.hashicorp.com/agent-inject-secret-app.env: secret/data/python-info-service
321+
vault.hashicorp.com/agent-inject-status: injected
322+
vault.hashicorp.com/agent-inject-template-app.env:
323+
vault.hashicorp.com/role: python-info-service
324+
325+
kubectl exec -it python-info-service-p
326+
ython-info-service-d8cb78fc7-4jssc -- ls /vault/secrets
327+
Defaulted container "python-info-service" out of: python-info-service, vault-agent, vault-agent-init (init)
328+
app.env
329+
(.venv) projacktor@projacktorLaptop ~/P/e/D/l/k8s (lab11)> kubectl exec -it python-info-service-p
330+
ython-info-service-d8cb78fc7-4jssc -- cat /vault/secrets/app.env
331+
Defaulted container "python-info-service" out of: python-info-service, vault-agent, vault-agent-init (init)
332+
export APP_USERNAME="change-me"
333+
export APP_PASSWORD="change-me"
334+
```
335+
336+
## K8s Secrets vs Vault
337+
338+
#### Kubernetes Secrets:
339+
340+
- Easy to implement and quick to configure.
341+
- Natively integrated with Deployment/StatefulSet.
342+
- Well suited for basic scenarios and small projects.
343+
- Stored in etcd (encryption at rest recommended).
344+
- Limited rotation and dynamic secrets capabilities without additional tools.
345+
346+
#### Vault:
347+
348+
- Centralized secret management and auditing.
349+
- Flexible access policies (fine-grained ACLs).
350+
- Support for dynamic secrets and rotation.
351+
- Pod injection without storing secrets in Git/values ​​as plain values.
352+
- More complex operationally: requires configuring auth, policy, role, Vault accessibility, and lifecycle tokens.
353+
354+
355+
### When to Use Each Approach
356+
357+
#### Use Kubernetes Secrets when:
358+
359+
- You need a quick start and simple operation.
360+
- You have a small number of secrets and infrequent rotation.
361+
- Your environment doesn't require advanced auditing/compliance.
362+
363+
#### Use Vault when:
364+
365+
- You need strict access policies and auditing.
366+
- You need rotation and dynamic/temporary credentials.
367+
- Multiple applications/teams use a common secrets platform.
368+
- You have production-level security requirements.
369+
370+
### Production Recommendations
371+
372+
- Don't store real secrets in Git or in regular values.yaml.
373+
- For Kubernetes Secrets, enable encryption at rest in etcd and strict RBAC.
374+
- For Vault, don't use dev mode/inmem storage.
375+
- For Vault, use HA + persistent storage (e.g., Raft), backup, and auto-unseal (KMS/HSM).
376+
- For Vault roles, set the audience and minimum required policy.
377+
- For applications, use a separate ServiceAccount instead of the default.
378+
- Enable monitoring and auditing:
379+
- Kubernetes events,
380+
- Vault audit logs,
381+
- injector/server unavailability alerts.
382+
- Regularly rotate secrets and test failover.
383+
- Post-deploy checks:
384+
- pod annotations for Vault injection,
385+
- presence of the ```/vault/secrets/...``` file,
386+
- no secret leaks in ```kubectl describe```.

labs/k8s/python-info-service/templates/deployment.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,19 @@ spec:
1818
metadata:
1919
labels:
2020
{{- include "python-info-service.selectorLabels" . | nindent 8 }}
21+
{{- if .Values.vault.enabled }}
22+
annotations:
23+
vault.hashicorp.com/agent-inject: "true"
24+
vault.hashicorp.com/role: {{ .Values.vault.role | quote }}
25+
vault.hashicorp.com/agent-inject-secret-{{ .Values.vault.injectFile }}: {{ .Values.vault.secretPath | quote }}
26+
vault.hashicorp.com/agent-inject-template-{{ .Values.vault.injectFile }}: |
27+
{{ "{{- with secret \"" }}{{ .Values.vault.secretPath }}{{ "\" -}}" }}
28+
export APP_USERNAME="{{ "{{ .Data.data.username }}" }}"
29+
export APP_PASSWORD="{{ "{{ .Data.data.password }}" }}"
30+
{{ "{{- end }}" }}
31+
{{- end }}
2132
spec:
33+
serviceAccountName: {{ .Values.serviceAccount.name }}
2234
containers:
2335
- name: {{ include "python-info-service.name" . }}
2436
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
@@ -33,6 +45,8 @@ spec:
3345
{{- toYaml .Values.livenessProbe | nindent 12 }}
3446
readinessProbe:
3547
{{- toYaml .Values.readinessProbe | nindent 12 }}
48+
{{- if .Values.secrets.enabled }}
3649
envFrom:
3750
- secretRef:
3851
name: {{ default (printf "%s-credentials" (include "python-info-service.fullname" .)) .Values.secrets.name }}
52+
{{- end }}

labs/k8s/python-info-service/values.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ service:
1414
targetPort: 8080
1515
nodePort: 30080
1616

17+
serviceAccount:
18+
name: default
19+
1720
resources:
1821
requests:
1922
cpu: 100m
@@ -51,6 +54,12 @@ secrets:
5154
username: "change-me"
5255
password: "change-me"
5356

57+
vault:
58+
enabled: false
59+
role: "python-info-service"
60+
secretPath: "secret/data/python-info-service"
61+
injectFile: "app.env"
62+
5463
hooks:
5564
enabled: true
5665
image:

0 commit comments

Comments
 (0)