Skip to content
Open
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
428 changes: 428 additions & 0 deletions k8s/HELM.md

Large diffs are not rendered by default.

447 changes: 447 additions & 0 deletions k8s/LAB10.md

Large diffs are not rendered by default.

155 changes: 155 additions & 0 deletions k8s/SECRETS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Kubernetes Secrets
### Output of creating and viewing your secret
```sh
kubectl get secret app-credentials -o yaml
```
```text
apiVersion: v1
data:
password: YWRtaW4xMjM=
username: dGltdXI=
kind: Secret
metadata:
creationTimestamp: "2026-04-08T14:10:34Z"
name: app-credentials
namespace: default
resourceVersion: "7121"
uid: cde19220-e95a-4b61-9386-b6b8f4b37f45
type: Opaque
```

### Decoded secret values demonstration
```sh
base64 -d <<<"dGltdXI="
```
```text
timur
```
```sh
base64 -d <<<"YWRtaW4xMjM="
```
```text
admin123
```

### Explanation of base64 encoding vs encryption
`base64`-encoding is is still plaintext: it obfuscates the information to a human reader, but the data is decodeable
with one command.

Real encryption requires the attacker to guess a key, which should be difficult to do.

# Helm Secret Integration
### Chart structure showing secrets.yaml
```yaml
apiVersion: v1
kind: Secret
metadata:
name: app-credentials
type: Opaque
stringData:
username: {{ .Values.secret.username | quote }}
password: {{ .Values.secret.password | quote }}
```

### How secrets are consumed in deployment
Secrets are consumed through environment variables.

### Verification output (env vars in pod, excluding actual values)
```sh
kubectl exec dinfoserv-dinfochart-575476d846-4424h -- env
```
```text
PATH=/venv/bin:/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=dinfoserv-dinfochart-575476d846-4424h
username=placeholder
password=placeholder
DINFOSERV_DINFOCHART_PORT_80_TCP=tcp://10.100.233.185:80
DINFOSERV_DINFOCHART_PORT_80_TCP_PROTO=tcp
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
DINFOSERV_DINFOCHART_PORT_80_TCP_PORT=80
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
DINFOSERV_DINFOCHART_SERVICE_HOST=10.100.233.185
DINFOSERV_DINFOCHART_SERVICE_PORT=80
DINFOSERV_DINFOCHART_PORT=tcp://10.100.233.185:80
DINFOSERV_DINFOCHART_PORT_80_TCP_ADDR=10.100.233.185
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
GPG_KEY=7169605F62C751356D054A26A821E680E5FA6305
PYTHON_VERSION=3.13.12
PYTHON_SHA256=2a84cd31dd8d8ea8aaff75de66fc1b4b0127dd5799aa50a64ae9a313885b4593
VIRTUAL_ENV=/venv
HOME=/home/infoservice
```

Note the "username" and "password" env variables.

# Resource Management
### Resource limits configuration
Resource limits are configured with the `values` files.

### Explanation of requests vs limits
Requests = how much is guaranteed.
Limits = how much is the hard maximum.

### How to choose appropriate values
In the development environment, give more resources to debug and test.
In production, look at the cluster and see how much is available. Then assign that much as the limit. Requests are the
minimal requirements for the containers to function at all.

# Vault Integration
### Vault installation verification (`kubectl get pods`)
```text
NAME READY STATUS RESTARTS AGE
dinfoserv-dinfochart-b9cc59fdf-2dv4n 2/2 Running 0 4m4s
dinfoserv-dinfochart-b9cc59fdf-6chxr 2/2 Running 0 4m4s
dinfoserv-dinfochart-b9cc59fdf-b6cqg 2/2 Running 0 4m4s
dinfoserv-dinfochart-b9cc59fdf-fkfwn 2/2 Running 0 4m4s
dinfoserv-dinfochart-b9cc59fdf-nh9r2 2/2 Running 0 4m4s
vault-0 1/1 Running 0 15h
vault-agent-injector-848dd747d7-b2pt4 1/1 Running 0 15h
```

### Policy and role configuration (sanitized)
Policy:
```sh
cat >dinfoservice-secret-access.hcl <<EOF
path "secret/myapp/config" {
capabilities = ["read"]
}
EOF
vault policy write dinfoservice-secret-access.hcl dinfoservice-secret-access.hcl
```

Role:
```sh
vault write auth/kubernetes/role/demo \
bound_service_account_names=myapp \
bound_service_account_namespaces=default \
policies=default \
ttl=1h
```

### Proof of secret injection (show file exists, path structure)
The secrets are mounted at `/etc/app-secret`:
```text
app-secret/
├── password
└── username
```

# Security Analysis
### Comparison: K8s Secrets vs Vault
K8s secrets are the native tool that is easy to use with kubernetes. HashiCorp Vault is a separate solution, although it
integrates well with Kubernetes.

K8s Secrets are stored in `etcd` by Kubernetes directly, by default unencrypted; Vault secrets are stored separately in
any provider that is configured, but that requires additional configuration.

### When to use each approach
There are ways to use both K8s secrets and HashiCorp Vault securely. But if the cloud provider, for example, provides a
secret management service, HC Vault probably will be easier to configure to use it, especially if the cloud provider
supplies documentation for that.
43 changes: 0 additions & 43 deletions k8s/deployment.yml

This file was deleted.

1 change: 1 addition & 0 deletions k8s/dinfochart/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/charts/
23 changes: 23 additions & 0 deletions k8s/dinfochart/.helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
24 changes: 24 additions & 0 deletions k8s/dinfochart/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: v2
name: dinfochart
description: A Helm chart for devops-infoservice

# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.1.1"
Empty file.
62 changes: 62 additions & 0 deletions k8s/dinfochart/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "dinfochart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "dinfochart.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "dinfochart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "dinfochart.labels" -}}
helm.sh/chart: {{ include "dinfochart.chart" . }}
{{ include "dinfochart.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "dinfochart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "dinfochart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "dinfochart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "dinfochart.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
64 changes: 64 additions & 0 deletions k8s/dinfochart/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "dinfochart.fullname" . }}
labels:
{{- include "dinfochart.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "dinfochart.selectorLabels" . | nindent 6 }}
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Extra pods during update
maxUnavailable: 0 # Ensure availability
template:
metadata:
labels:
{{- include "dinfochart.labels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "dinfoservice-role"
vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp/config"
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
ports:
- containerPort: {{ .Values.service.port }}
{{- with .Values.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.livenessProbe }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.readinessProbe }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
env:
- name: username
valueFrom:
secretKeyRef:
name: app-credentials
key: username
- name: password
valueFrom:
secretKeyRef:
name: app-credentials
key: password
volumeMounts:
- name: app-secret
mountPath: "/etc/app-secret"
readOnly: true
volumes:
- name: app-secret
secret:
secretName: app-credentials
20 changes: 20 additions & 0 deletions k8s/dinfochart/templates/hooks/post-install-job.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: batch/v1
kind: Job
metadata:
name: '{{ include "dinfochart.fullname" . }}-post-install'
labels:
{{- include "dinfochart.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": post-install
"helm.sh/hook-weight": "5"
"helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
metadata:
name: '{{ include "dinfochart.fullname" . }}-post-install'
spec:
restartPolicy: Never
containers:
- name: post-install-job
image: busybox
command: ['sh', '-c', 'echo Post-install validation && sleep 1 && echo Validation passed']
Loading
Loading