chore: testing build
Some checks failed
Build, test, publish, deploy / build-test (push) Has been cancelled
Build, test, publish, deploy / integration-test (push) Has been cancelled
Build, test, publish, deploy / publish (push) Has been cancelled
Build, test, publish, deploy / deploy-prd (push) Has been cancelled

This commit is contained in:
Daniel Noulet 2026-10-10 18:58:32 +02:00
parent d3ee64282d
commit 8942383ff9
13 changed files with 353 additions and 44 deletions

View file

@ -23,6 +23,10 @@ secret_value() {
printf '%s' "$encoded" | base64 -d 2>/dev/null || true printf '%s' "$encoded" | base64 -d 2>/dev/null || true
} }
kubectl get --raw=/readyz >/dev/null kubectl get --raw=/readyz >/dev/null
if [[ "$RELEASE" == go-loose ]] && ! kubectl -n "$NAMESPACE" get secret go-loose-wildcard-tls >/dev/null 2>&1; then
echo "Required go-loose-wildcard-tls Secret is missing; provision the tenant wildcard certificate before bootstrap/deploy." >&2
exit 1
fi
kubectl create namespace "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - kubectl create namespace "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -
for attempt in {1..360}; do if kubectl -n "$NAMESPACE" create configmap "$LOCK_NAME" --from-literal=owner="${GITHUB_REPOSITORY:-local}:${GITHUB_RUN_ID:-$$}" >/dev/null 2>&1; then LOCK_ACQUIRED=true; break; fi; sleep 5; done for attempt in {1..360}; do if kubectl -n "$NAMESPACE" create configmap "$LOCK_NAME" --from-literal=owner="${GITHUB_REPOSITORY:-local}:${GITHUB_RUN_ID:-$$}" >/dev/null 2>&1; then LOCK_ACQUIRED=true; break; fi; sleep 5; done
if [[ "$LOCK_ACQUIRED" != true ]]; then echo "Timed out waiting for another go-insane-prd deploy to release its lock." >&2; exit 1; fi if [[ "$LOCK_ACQUIRED" != true ]]; then echo "Timed out waiting for another go-insane-prd deploy to release its lock." >&2; exit 1; fi
@ -69,18 +73,24 @@ if [[ "$RELEASE" != go-guess ]]; then HELM_ARGS+=(--set-string "image.repository
[[ ! -f "$CHART/values-prd.yaml" ]] || HELM_ARGS+=(--values "$CHART/values-prd.yaml") [[ ! -f "$CHART/values-prd.yaml" ]] || HELM_ARGS+=(--values "$CHART/values-prd.yaml")
case "$RELEASE" in case "$RELEASE" in
go-loose) go-loose)
if ! kubectl -n "$NAMESPACE" get secret go-loose-runtime >/dev/null 2>&1; then
db_password="$(secret_value go-loose-postgresql postgres-password)"
if [[ -z "$db_password" ]] && kubectl -n "$NAMESPACE" get pvc -o name | grep -Eq 'go-loose.*postgresql|data-go-loose'; then echo "Existing go-loose database storage has no recoverable password secret; refusing to rotate its credentials." >&2; exit 1; fi
[[ -n "$db_password" ]] || db_password="$(openssl rand -hex 24)"
session_secret="$(secret_value go-loose-env GO_LOOSE_SESSION_SECRET)"
[[ -n "$session_secret" ]] || session_secret="$(openssl rand -hex 32)"
kubectl -n "$NAMESPACE" create secret generic go-loose-runtime --from-literal=postgresql-password="$db_password" --from-literal=session-secret="$session_secret"
fi
db_password="$(secret_value go-loose-runtime postgresql-password)" db_password="$(secret_value go-loose-runtime postgresql-password)"
[[ -n "$db_password" ]] || db_password="$(secret_value go-loose-postgresql postgres-password)"
[[ -n "$db_password" ]] || db_password="$(secret_value go-loose-postgresql password)"
if [[ -z "$db_password" ]] && kubectl -n "$NAMESPACE" get pvc -o name | grep -Eq 'go-loose.*postgresql|data-go-loose'; then
echo "Existing go-loose database storage has no recoverable password secret; refusing to rotate its credentials." >&2
exit 1
fi
[[ -n "$db_password" ]] || db_password="$(openssl rand -hex 24)"
session_secret="$(secret_value go-loose-runtime session-secret)" session_secret="$(secret_value go-loose-runtime session-secret)"
[[ -n "$session_secret" ]] || session_secret="$(secret_value go-loose-env GO_LOOSE_SESSION_SECRET)"
[[ -n "$session_secret" ]] || session_secret="$(openssl rand -hex 32)"
database_url="postgres://goloose:${db_password}@go-loose-postgresql:5432/goloose?sslmode=disable" database_url="postgres://goloose:${db_password}@go-loose-postgresql:5432/goloose?sslmode=disable"
HELM_ARGS+=(--set-string "postgresql.auth.password=${db_password}" --set-string "envSecrets.GO_LOOSE_DATABASE_URL=${database_url}" --set-string "envSecrets.GO_LOOSE_SESSION_SECRET=${session_secret}") kubectl -n "$NAMESPACE" create secret generic go-loose-runtime \
--from-literal=postgresql-password="$db_password" \
--from-literal=postgres-password="$db_password" \
--from-literal=database-url="$database_url" \
--from-literal=session-secret="$session_secret" \
--dry-run=client -o yaml | kubectl apply -f -
;; ;;
go-mad) go-mad)
if ! kubectl -n "$NAMESPACE" get secret go-mad-runtime >/dev/null 2>&1; then if ! kubectl -n "$NAMESPACE" get secret go-mad-runtime >/dev/null 2>&1; then

View file

@ -109,5 +109,4 @@ jobs:
NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}
run: | run: |
set -Eeuo pipefail set -Eeuo pipefail
bash .forgejo/scripts/deploy-prd.sh deploy/helm/go-loose go-loose go-loose "${{ github.sha }}" \ bash .forgejo/scripts/deploy-prd.sh deploy/helm/go-loose go-loose go-loose "${{ github.sha }}"
--set-string ingress.className=nginx

View file

@ -2,7 +2,7 @@ apiVersion: v2
name: go-loose name: go-loose
description: Authentication and authorization service for urpi platform. Provides OIDC, session management, and tenant isolation. description: Authentication and authorization service for urpi platform. Provides OIDC, session management, and tenant isolation.
type: application type: application
version: 1.0.0 version: 1.1.0
appVersion: "1.0.0" appVersion: "1.0.0"
kubeVersion: ">=1.28.0-0" kubeVersion: ">=1.28.0-0"
dependencies: dependencies:

View file

@ -0,0 +1,81 @@
# Go Loose Helm chart
This is the canonical Go Loose chart. The central deploy script and the Go Loose
CI workflow both use this directory.
## Environment profiles
Use one profile file per environment:
| Environment | Namespace | Profile | Ingress class | TLS issuer |
|---|---|---|---|---|
| `dev` | `go-insane-dev` | `values-dev.yaml` | nginx | `letsencrypt-staging` |
| `tst` | `go-insane-tst` | `values-tst.yaml` | nginx | `letsencrypt-staging` |
| `prd` | `go-insane-prd` | `values-prd.yaml` | nginx | `letsencrypt-prod` |
| `lcl` | `go-insane-lcl` | `values-lcl.yaml` | Traefik | local TLS |
The namespace is supplied by Helm's release namespace; it is not hard-coded in
the chart templates.
## Runtime Secret contract
Before a release, the target namespace must contain `go-loose-runtime` with:
| Key | Used for |
|---|---|
| `postgresql-password` | PostgreSQL application user's password |
| `postgres-password` | PostgreSQL administrator password |
| `database-url` | `GO_LOOSE_DATABASE_URL` |
| `session-secret` | `GO_LOOSE_SESSION_SECRET` |
The chart reads these values using Kubernetes `secretKeyRef` and the PostgreSQL
subchart's `existingSecret` support. Runtime credentials are not stored in chart
values or Helm release configuration.
To provision/update the secret from protected files without printing its values:
```bash
/home/dnoulet/go/ops/scripts/apply-go-loose-secret.sh \
<dev|tst|prd|lcl> \
/secure/path/postgresql-password \
/secure/path/database-url \
/secure/path/session-secret \
[kubeconfig]
```
The input files should contain only the required value (no trailing newline).
The helper creates the environment namespace if it is not present, then applies
the Secret. It does not print secret values.
The PRD CI deployment script maintains the same secret contract automatically.
Do not commit the input files or put their contents in a Helm values file.
## Tenant wildcard ingress
Go Guess signs in through tenant-specific hosts such as
`nmbs.auth.dev-urpi.be` and `nmbs.auth.urpi.be`. The chart therefore
creates a second ingress for `*.auth-<env>...` and requires a separate TLS Secret
named `go-loose-wildcard-tls` in the same namespace. The central deploy script
and PRD CI check for this Secret before installing the release.
Provision it from a trusted wildcard certificate and private key:
```bash
kubectl --kubeconfig <kubeconfig> --namespace go-insane-<env> create secret tls \
go-loose-wildcard-tls --cert=/secure/path/wildcard.crt \
--key=/secure/path/wildcard.key
```
The PRD `letsencrypt-prod` issuer documented for the apex ingress uses HTTP-01;
HTTP-01 cannot issue wildcard certificates. Obtain this certificate through
DNS-01 or the approved certificate authority, then provision the Secret. Do not
put the private key in the chart, repository, or Helm release values.
## Validation
```bash
helm lint . -f values-dev.yaml
helm lint . -f values-tst.yaml
helm lint . -f values-prd.yaml
helm lint . -f values-lcl.yaml
```

View file

@ -40,14 +40,20 @@ spec:
containerPort: {{ .Values.service.targetPort }} containerPort: {{ .Values.service.targetPort }}
protocol: TCP protocol: TCP
env: env:
- name: GO_LOOSE_DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ .Values.runtimeSecret.name }}
key: {{ .Values.runtimeSecret.keys.databaseURL }}
- name: GO_LOOSE_SESSION_SECRET
valueFrom:
secretKeyRef:
name: {{ .Values.runtimeSecret.name }}
key: {{ .Values.runtimeSecret.keys.sessionSecret }}
{{- range $key, $value := .Values.env }} {{- range $key, $value := .Values.env }}
- name: {{ $key }} - name: {{ $key }}
value: {{ $value | quote }} value: {{ $value | quote }}
{{- end }} {{- end }}
{{- range $key, $value := .Values.envSecrets }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
livenessProbe: livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }} {{- toYaml .Values.livenessProbe | nindent 12 }}
readinessProbe: readinessProbe:
@ -65,4 +71,4 @@ spec:
{{- with .Values.tolerations }} {{- with .Values.tolerations }}
tolerations: tolerations:
{{- toYaml . | nindent 8 }} {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}

View file

@ -1,12 +0,0 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ include "go-loose.fullname" . }}-env
namespace: {{ .Release.Namespace }}
labels:
{{- include "go-loose.labels" . | nindent 4 }}
type: Opaque
stringData:
{{- range $key, $value := .Values.envSecrets }}
{{ $key }}: {{ $value | quote }}
{{- end }}

View file

@ -0,0 +1,30 @@
{{- if .Values.wildcardIngress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "go-loose.fullname" . }}-wildcard
namespace: {{ .Release.Namespace }}
labels:
{{- include "go-loose.labels" . | nindent 4 }}
{{- with .Values.wildcardIngress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
ingressClassName: {{ .Values.wildcardIngress.className }}
tls:
- secretName: {{ .Values.wildcardIngress.tlsSecretName }}
hosts:
- {{ .Values.wildcardIngress.host | quote }}
rules:
- host: {{ .Values.wildcardIngress.host | quote }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ include "go-loose.fullname" . }}
port:
number: {{ .Values.service.port }}
{{- end }}

View file

@ -0,0 +1,46 @@
replicaCount: 1
image:
repository: nexus.urpi.be/docker-hosted/go-loose
tag: latest
pullPolicy: Always
imagePullSecrets:
- name: nexus-registry
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging
nginx.ingress.kubernetes.io/ssl-redirect: "true"
hosts:
- host: auth.dev-urpi.be
paths:
- path: /
pathType: Prefix
tls:
- secretName: go-loose-dev-tls
hosts:
- auth.dev-urpi.be
wildcardIngress:
enabled: true
className: nginx
host: "*.auth.dev-urpi.be"
tlsSecretName: go-loose-wildcard-tls
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
postgresql:
primary:
persistence:
enabled: true
size: 5Gi
env:
GO_LOOSE_BASE_URL: https://auth.dev-urpi.be
GO_LOOSE_AUTH_DOMAIN: auth.dev-urpi.be
GO_LOOSE_OIDC_REDIRECT_URL: https://auth.dev-urpi.be/auth/callback
GO_LOOSE_DEV_LOGIN: "true"
GO_LOOSE_LOCAL_LOGIN: "true"

View file

@ -0,0 +1,65 @@
replicaCount: 1
imagePullSecrets: []
ingress:
enabled: true
className: traefik
annotations:
cert-manager.io/cluster-issuer: urpi-private-ca
hosts:
# Apex only. The tenant hosts are served by wildcardIngress below, so that
# each host family keeps its own certificate: go-loose-lcl-tls covers the
# apex, go-loose-wildcard-tls covers *.auth.lcl-urpi.be.
- host: auth.lcl-urpi.be
paths:
- path: /
pathType: Prefix
tls:
- secretName: go-loose-lcl-tls
hosts:
- auth.lcl-urpi.be
# Serves every <tenant>.auth.lcl-urpi.be. Adding a tenant is then a row in the
# database only: no Helm upgrade, no new certificate, no per-host rule.
wildcardIngress:
enabled: true
className: traefik
host: "*.auth.lcl-urpi.be"
tlsSecretName: go-loose-wildcard-tls
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
certManager:
enabled: true
issuerRef:
name: urpi-private-ca
kind: ClusterIssuer
postgresql:
enabled: true
image:
# Bitnami removed its versioned tags from Docker Hub; only :latest remains
# for bitnami/postgresql. The tag pinned in values.yaml (16.4.0) now 404s
# and leaves the StatefulSet in ImagePullBackOff.
tag: "latest"
auth:
database: goloose
username: goloose
password: "goloose"
existingSecret: go-loose-runtime
secretKeys:
adminPasswordKey: postgres-password
userPasswordKey: postgresql-password
primary:
persistence:
enabled: false
size: 5Gi
env:
GO_LOOSE_ADDR: ":8080"
GO_LOOSE_BASE_URL: "https://lcl-urpi.be"
GO_LOOSE_AUTH_DOMAIN: "auth.lcl-urpi.be"
GO_LOOSE_OIDC_REDIRECT_URL: "https://lcl-urpi.be/auth/callback"
GO_LOOSE_DEV_LOGIN: "true"
GO_LOOSE_LOCAL_LOGIN: "true"
GO_LOOSE_ALLOW_PRIVATE_CONTRACT_URLS: "true"

View file

@ -5,7 +5,7 @@ imagePullSecrets:
- name: nexus-registry - name: nexus-registry
image: image:
repository: nexus-registry.urpi.be/docker-hosted/go-loose repository: nexus.urpi.be/docker-hosted/go-loose
tag: "0.0.1" tag: "0.0.1"
pullPolicy: Always pullPolicy: Always
@ -16,9 +16,10 @@ service:
ingress: ingress:
enabled: true enabled: true
className: traefik className: nginx
annotations: annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
# nginx.ingress.kubernetes.io/ssl-redirect: "true" # nginx.ingress.kubernetes.io/ssl-redirect: "true"
# nginx.ingress.kubernetes.io/proxy-body-size: "10m" # nginx.ingress.kubernetes.io/proxy-body-size: "10m"
hosts: hosts:
@ -31,6 +32,14 @@ ingress:
hosts: hosts:
- auth.urpi.be - auth.urpi.be
wildcardIngress:
enabled: true
className: nginx
host: "*.auth.urpi.be"
tlsSecretName: go-loose-wildcard-tls
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
certManager: certManager:
enabled: true enabled: true
issuerRef: issuerRef:
@ -69,13 +78,9 @@ env:
GO_LOOSE_AUTH_DOMAIN: "auth.urpi.be" GO_LOOSE_AUTH_DOMAIN: "auth.urpi.be"
GO_LOOSE_OIDC_REDIRECT_URL: "https://auth.urpi.be/auth/callback" GO_LOOSE_OIDC_REDIRECT_URL: "https://auth.urpi.be/auth/callback"
GO_LOOSE_DEV_LOGIN: "false" GO_LOOSE_DEV_LOGIN: "false"
GO_LOOSE_LOCAL_LOGIN: "true" GO_LOOSE_LOCAL_LOGIN: "false"
GO_LOOSE_ALLOW_PRIVATE_CONTRACT_URLS: "true" GO_LOOSE_ALLOW_PRIVATE_CONTRACT_URLS: "true"
envSecrets:
GO_LOOSE_DATABASE_URL: "postgres://goloose:changeme@go-loose-postgresql:5432/goloose?sslmode=disable"
GO_LOOSE_SESSION_SECRET: "changeme-generate-secure-random"
livenessProbe: livenessProbe:
httpGet: httpGet:
path: /healthz path: /healthz
@ -92,4 +97,4 @@ readinessProbe:
initialDelaySeconds: 10 initialDelaySeconds: 10
periodSeconds: 10 periodSeconds: 10
timeoutSeconds: 3 timeoutSeconds: 3
failureThreshold: 3 failureThreshold: 3

View file

@ -0,0 +1,46 @@
replicaCount: 1
image:
repository: nexus.urpi.be/docker-hosted/go-loose
tag: latest
pullPolicy: Always
imagePullSecrets:
- name: nexus-registry
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging
nginx.ingress.kubernetes.io/ssl-redirect: "true"
hosts:
- host: auth.tst-urpi.be
paths:
- path: /
pathType: Prefix
tls:
- secretName: go-loose-tst-tls
hosts:
- auth.tst-urpi.be
wildcardIngress:
enabled: true
className: nginx
host: "*.auth.tst-urpi.be"
tlsSecretName: go-loose-wildcard-tls
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
postgresql:
primary:
persistence:
enabled: true
size: 5Gi
env:
GO_LOOSE_BASE_URL: https://auth.tst-urpi.be
GO_LOOSE_AUTH_DOMAIN: auth.tst-urpi.be
GO_LOOSE_OIDC_REDIRECT_URL: https://auth.tst-urpi.be/auth/callback
GO_LOOSE_DEV_LOGIN: "false"
GO_LOOSE_LOCAL_LOGIN: "false"

View file

@ -54,6 +54,13 @@ ingress:
hosts: hosts:
- auth.urpi.be - auth.urpi.be
wildcardIngress:
enabled: false
className: nginx
host: "*.auth.urpi.be"
tlsSecretName: go-loose-wildcard-tls
annotations: {}
certManager: certManager:
enabled: true enabled: true
issuerRef: issuerRef:
@ -74,12 +81,24 @@ autoscaling:
maxReplicas: 3 maxReplicas: 3
targetCPUUtilizationPercentage: 80 targetCPUUtilizationPercentage: 80
runtimeSecret:
name: go-loose-runtime
keys:
databaseURL: database-url
sessionSecret: session-secret
postgresql: postgresql:
enabled: true enabled: true
image:
tag: "16.4.0"
auth: auth:
database: goloose database: goloose
username: goloose username: goloose
password: changeme password: "goloose"
existingSecret: go-loose-runtime
secretKeys:
adminPasswordKey: postgres-password
userPasswordKey: postgresql-password
primary: primary:
persistence: persistence:
enabled: true enabled: true
@ -98,13 +117,9 @@ env:
GO_LOOSE_AUTH_DOMAIN: "auth.urpi.be" GO_LOOSE_AUTH_DOMAIN: "auth.urpi.be"
GO_LOOSE_OIDC_REDIRECT_URL: "https://auth.urpi.be/auth/callback" GO_LOOSE_OIDC_REDIRECT_URL: "https://auth.urpi.be/auth/callback"
GO_LOOSE_DEV_LOGIN: "false" GO_LOOSE_DEV_LOGIN: "false"
GO_LOOSE_LOCAL_LOGIN: "true" GO_LOOSE_LOCAL_LOGIN: "false"
GO_LOOSE_ALLOW_PRIVATE_CONTRACT_URLS: "true" GO_LOOSE_ALLOW_PRIVATE_CONTRACT_URLS: "true"
envSecrets:
GO_LOOSE_DATABASE_URL: ""
GO_LOOSE_SESSION_SECRET: ""
livenessProbe: livenessProbe:
httpGet: httpGet:
path: /healthz path: /healthz
@ -125,4 +140,4 @@ readinessProbe:
nodeSelector: {} nodeSelector: {}
tolerations: [] tolerations: []
affinity: {} affinity: {}

18
docs/commands Normal file
View file

@ -0,0 +1,18 @@
# Delete the incomplete secret
kubectl delete secret go-loose-runtime --kubeconfig=/home/dnoulet/.kube/config_lcl --insecure-skip-tls-verify -n go-insane-lcl --ignore-not-found
# Recreate it with all required properties mapped correctly
kubectl create secret generic go-loose-runtime \
--from-literal=postgres-password="goloose" \
--from-literal=postgresql-password="goloose" \
--from-literal=session-secret="localdevsecret123" \
--from-literal=database-url="postgres://goloose:goloose@go-loose-postgresql:5432/goloose?sslmode=disable" \
--kubeconfig=/home/dnoulet/.kube/config_lcl \
--insecure-skip-tls-verify \
-n go-insane-lcl
KUBECONFIG=~/.kube/config_lcl kubectl config set-cluster local --insecure-skip-tls-verify=true
openssl s_client -showcerts -connect rancher.intern-urpi.be:443 </dev/null 2>/dev/null | sed -n '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/.kube/rancher-ca.crt
KUBECONFIG=~/.kube/config_lcl kubectl config set-cluster local --certificate-authority=/home/dnoulet/.kube/rancher-ca.crt
KUBECONFIG=~/.kube/config_lcl kubectl config set-cluster local --insecure-skip-tls-verify=false