NG: secret manager and VW
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Failing after 14m33s

This commit is contained in:
Aleksandr Bogomiakov committed 2026-10-05 18:40:46 +01:00
1 parent 862a7619e2
commit cf88753ff5
15 files changed
+533

No files matched your search

@@ -0,0 +1,26 @@
FROM debian:sid
ENV BW_CLI_VERSION=2025.12.1
RUN apt update && \
apt install -y wget unzip && \
wget https://github.com/bitwarden/clients/releases/download/cli-v${BW_CLI_VERSION}/bw-linux-${BW_CLI_VERSION}.zip && \
unzip bw-linux-${BW_CLI_VERSION}.zip && \
chmod +x bw && \
mv bw /usr/local/bin/bw && \
rm -rfv *.zip && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Build entrypoint script
RUN echo '#!/bin/sh\n\
set -e\n\
bw config server ${BW_HOST}\n\
export BW_SESSION=$(bw login ${BW_USER} --passwordenv BW_PASSWORD --raw)\n\
bw unlock --check\n\
echo "Running \`bw server\` on port 8087"\n\
bw serve --hostname 0.0.0.0\n' > /entrypoint.sh && \
chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]
+21
View File
@@ -0,0 +1,21 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: external-secrets
namespace: argocd
spec:
project: core
destination:
namespace: external-secrets
server: https://kubernetes.default.svc
source:
repoURL: https://gt.hexor.cy/ab/homelab.git
targetRevision: main
path: k8s-ng/external-secrets
syncPolicy:
automated:
selfHeal: true
prune: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
@@ -0,0 +1,153 @@
# ---
# apiVersion: v1
# kind: Secret
# metadata:
# name: bitwarden-cli
# namespace: external-secrets
# data:
# BW_HOST: base64(url)
# BW_USERNAME: base64(name)
# BW_PASSWORD: base64(pass)
# Vaultwarden bot - 81212111-6350-4069-8bcf-19a67d3964a5
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: bitwarden-cli
labels:
reloader.stakater.com/auto: "true"
app.kubernetes.io/instance: bitwarden-cli
app.kubernetes.io/name: bitwarden-cli
spec:
replicas: 1
strategy:
type: RollingUpdate
selector:
matchLabels:
app.kubernetes.io/name: bitwarden-cli
app.kubernetes.io/instance: bitwarden-cli
template:
metadata:
labels:
app.kubernetes.io/name: bitwarden-cli
app.kubernetes.io/instance: bitwarden-cli
spec:
nodeSelector:
kubernetes.io/arch: amd64
containers:
- name: bitwarden-cli
image: ultradesu/bitwarden-client:2025.12.1
imagePullPolicy: Always
resources:
requests:
memory: "128Mi"
cpu: "300m"
limits:
memory: "512Mi"
cpu: "1000m"
env:
- name: BW_HOST
valueFrom:
secretKeyRef:
name: bitwarden-cli
key: BW_HOST
- name: BW_USER
valueFrom:
secretKeyRef:
name: bitwarden-cli
key: BW_USERNAME
- name: BW_PASSWORD
valueFrom:
secretKeyRef:
name: bitwarden-cli
key: BW_PASSWORD
ports:
- name: http
containerPort: 8087
protocol: TCP
livenessProbe:
exec:
command:
- wget
- -q
- http://127.0.0.1:8087/sync
- --post-data=''
initialDelaySeconds: 20
failureThreshold: 3
timeoutSeconds: 1
periodSeconds: 120
readinessProbe:
tcpSocket:
port: 8087
initialDelaySeconds: 20
failureThreshold: 3
timeoutSeconds: 1
periodSeconds: 10
startupProbe:
tcpSocket:
port: 8087
initialDelaySeconds: 10
failureThreshold: 30
timeoutSeconds: 1
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: bitwarden-cli
labels:
app.kubernetes.io/instance: bitwarden-cli
app.kubernetes.io/name: bitwarden-cli
annotations:
spec:
type: ClusterIP
ports:
- port: 8087
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: bitwarden-cli
app.kubernetes.io/instance: bitwarden-cli
---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: external-secret-2-bw-cli
spec:
podSelector:
matchLabels:
app.kubernetes.io/instance: bitwarden-cli
app.kubernetes.io/name: bitwarden-cli
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/instance: external-secrets
app.kubernetes.io/name: external-secrets
---
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: vaultwarden-login
spec:
provider:
webhook:
url: "http://bitwarden-cli:8087/object/item/{{ .remoteRef.key }}"
headers:
Content-Type: application/json
result:
jsonPath: "$.data.{{ .remoteRef.property }}"
---
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: vaultwarden-fields
spec:
provider:
webhook:
url: "http://bitwarden-cli:8087/object/item/{{ .remoteRef.key }}"
result:
jsonPath: "$.data.fields[?@.name==\"{{ .remoteRef.property }}\"].value"
@@ -0,0 +1,17 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- app.yaml
# Enable after the Vaultwarden bot account exists and the bitwarden-cli
# secret is created (see the header of bitwarden-store.yaml):
# - bitwarden-store.yaml
helmCharts:
- name: external-secrets
repo: https://charts.external-secrets.io
version: 2.11.0
releaseName: external-secrets
namespace: external-secrets
valuesFile: values.yaml
includeCRDs: true
+1
View File
@@ -0,0 +1 @@
installCRDs: true
@@ -0,0 +1,81 @@
# Database "vaultwarden": password, role, database and read access for the app.
# To add another database copy this file and replace the name.
# Generated once; the role and the application both read this Secret.
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: vaultwarden-db
namespace: psql
spec:
refreshPolicy: CreatedOnce
target:
name: vaultwarden-db
template:
type: kubernetes.io/basic-auth
metadata:
labels:
cnpg.io/reload: "true"
data:
username: vaultwarden
password: "{{ .password }}"
dataFrom:
- sourceRef:
generatorRef:
apiVersion: generators.external-secrets.io/v1alpha1
kind: Password
name: db-password
---
apiVersion: postgresql.cnpg.io/v1
kind: DatabaseRole
metadata:
name: main-vaultwarden
namespace: psql
spec:
cluster:
name: main
name: vaultwarden
login: true
passwordSecret:
name: vaultwarden-db
---
apiVersion: postgresql.cnpg.io/v1
kind: Database
metadata:
name: main-vaultwarden
namespace: psql
spec:
cluster:
name: main
name: vaultwarden
owner: vaultwarden
databaseReclaimPolicy: retain
---
# Lets the vaultwarden namespace read only its own credentials Secret.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: vaultwarden-db-reader
namespace: psql
rules:
- apiGroups: [""]
resources: [secrets]
resourceNames: [vaultwarden-db]
verbs: [get, list, watch]
- apiGroups: [authorization.k8s.io]
resources: [selfsubjectrulesreviews]
verbs: [create]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: vaultwarden-db-reader
namespace: psql
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: vaultwarden-db-reader
subjects:
- kind: ServiceAccount
name: db-credentials-reader
namespace: vaultwarden
+2
View File
@@ -4,3 +4,5 @@ kind: Kustomization
resources:
- app.yaml
- cluster.yaml
- password-generator.yaml
- databases/vaultwarden.yaml
+12
View File
@@ -0,0 +1,12 @@
# Random passwords for database roles. Referenced by the ExternalSecrets in databases/.
apiVersion: generators.external-secrets.io/v1alpha1
kind: Password
metadata:
name: db-password
namespace: psql
spec:
length: 40
digits: 10
symbols: 0 # the password ends up inside a connection URL
noUpper: false
allowRepeat: true
+22
View File
@@ -0,0 +1,22 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: vaultwarden
namespace: argocd
spec:
project: apps
destination:
namespace: vaultwarden
server: https://kubernetes.default.svc
source:
repoURL: https://gt.hexor.cy/ab/homelab.git
targetRevision: main
path: k8s-ng/vaultwarden
syncPolicy:
automated:
selfHeal: true
prune: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
- SkipDryRunOnMissingResource=true # the external-secrets Application installs the CRDs
+64
View File
@@ -0,0 +1,64 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: vaultwarden
labels:
app: vaultwarden
spec:
replicas: 1
strategy:
type: Recreate # the RWO local volume cannot be mounted by two pods
selector:
matchLabels:
app: vaultwarden
template:
metadata:
labels:
app: vaultwarden
spec:
containers:
- name: vaultwarden
image: vaultwarden/server:1.37.3
resources:
requests:
memory: 128Mi
cpu: 100m
limits:
memory: 512Mi
env:
- name: DOMAIN
value: https://vw.hexor.cy
- name: ORG_GROUPS_ENABLED
value: "true"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: vaultwarden-db-url
key: DATABASE_URL
- name: ADMIN_TOKEN
valueFrom:
secretKeyRef:
name: admin-token
key: ADMIN_TOKEN
ports:
- name: http
containerPort: 80
protocol: TCP
readinessProbe:
httpGet:
path: /alive
port: http
periodSeconds: 10
livenessProbe:
httpGet:
path: /alive
port: http
initialDelaySeconds: 30
periodSeconds: 30
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: vaultwarden-data
+23
View File
@@ -0,0 +1,23 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: vaultwarden
annotations:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
rules:
- host: vw.hexor.cy
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: vaultwarden
port:
number: 80
tls:
- secretName: vaultwarden-tls
hosts:
- vw.hexor.cy
+10
View File
@@ -0,0 +1,10 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- app.yaml
- secrets.yaml
- pvc.yaml
- deployment.yaml
- service.yaml
- ingress.yaml
+12
View File
@@ -0,0 +1,12 @@
# Attachments, sends, icon cache and RSA keys live here; the database is in PostgreSQL.
# A local-path volume is bound to one node: the pod stays on the node it first ran on.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: vaultwarden-data
spec:
accessModes: [ReadWriteOnce]
storageClassName: local-path
resources:
requests:
storage: 2Gi
+78
View File
@@ -0,0 +1,78 @@
# Read-only access to the vaultwarden-db Secret in the psql namespace
# (see postgresql/databases/vaultwarden.yaml for the other half).
apiVersion: v1
kind: ServiceAccount
metadata:
name: db-credentials-reader
---
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: psql-credentials
spec:
provider:
kubernetes:
remoteNamespace: psql
server:
caProvider:
type: ConfigMap
name: kube-root-ca.crt
key: ca.crt
auth:
serviceAccount:
name: db-credentials-reader
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: vaultwarden-db-url
spec:
refreshInterval: 1h
secretStoreRef:
kind: SecretStore
name: psql-credentials
target:
name: vaultwarden-db-url
template:
data:
DATABASE_URL: "postgresql://{{ .username }}:{{ .password }}@main-rw.psql.svc:5432/vaultwarden"
data:
- secretKey: username
remoteRef:
key: vaultwarden-db
property: username
- secretKey: password
remoteRef:
key: vaultwarden-db
property: password
---
# Admin panel token (/admin). Generated once; read it with:
# kubectl -n vaultwarden get secret admin-token -o jsonpath='{.data.ADMIN_TOKEN}' | base64 -d
apiVersion: generators.external-secrets.io/v1alpha1
kind: Password
metadata:
name: admin-token
spec:
length: 48
digits: 10
symbols: 0
noUpper: false
allowRepeat: true
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: admin-token
spec:
refreshPolicy: CreatedOnce
target:
name: admin-token
template:
data:
ADMIN_TOKEN: "{{ .password }}"
dataFrom:
- sourceRef:
generatorRef:
apiVersion: generators.external-secrets.io/v1alpha1
kind: Password
name: admin-token
+11
View File
@@ -0,0 +1,11 @@
apiVersion: v1
kind: Service
metadata:
name: vaultwarden
spec:
selector:
app: vaultwarden
ports:
- protocol: TCP
port: 80
targetPort: http