Skip to content
Merged
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
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,29 @@ written while it was being built. See [RELEASING.md](RELEASING.md).
- Pages an organiser has not published no longer appear in the hackathon's
navigation. They are reached through Manage Pages, which has moved above
Manage Voting.
- Hackagon can now run against a Postgres it does not install — a managed cloud
database, or one an operator provisions. Set `postgresql.enabled` to `false`,
point `backend.config.database.host` and `keycloak.database.external.host` at
your server, and create the two databases yourself. Previously the chart
always addressed a server named after its own release, so there was no way to
reach anything else.
- The database password can now be read from a Kubernetes Secret you already
hold, via `backend.config.database.existingSecret` and
`existingSecretPasswordKey` (and the equivalent keys under
`keycloak.database.external`). This is what a secret store or a Postgres
operator needs: it writes the credentials, the chart reads them, and nobody
has to copy a password into a values file.

### Changed

- The backend's database password is no longer written into a ConfigMap. The
chart puts it in a Secret — its own, or the one you named — and hands it to
the backend as an environment variable. Before, anyone able to list ConfigMaps
in the namespace could read the database password.
- Fixed `keycloak.database.external.database` and `.user` being silently
ignored: the Keycloak chart calls them `name` and `username`, so a non-default
database name or user never reached Keycloak and it quietly used `keycloak`
for both. The values are now named to match and the setting takes effect.

- A hackathon overview with no phase running no longer opens with "No phase is
running" and a footnote saying the timeline follows the dates alone. The card
Expand Down
2 changes: 1 addition & 1 deletion helm-chart/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type: application

# The chart's own version. Bumped by hand when the chart changes, and
# independent of the app: the CI publishes whatever it finds here.
version: 0.4.0
version: 0.5.0
# The app release this chart deploys. A new app release does
# not become deployable until someone points the chart at it.
appVersion: "0.9.1"
Expand Down
12 changes: 9 additions & 3 deletions helm-chart/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ To get the generated frontend OIDC secrets, run:
kubectl get secret {{ include "hackagon.fullname" . }}-frontend-secrets \
-n {{ .Release.Namespace }} -o jsonpath='{.data.secrets\.yaml}' | base64 -d

Passwords are set in your values files. Retrieve them with:
The backend reads its database password from a Secret:

# Platform (hackagon) user password
kubectl get secret {{ include "hackagon.fullname" . }}-postgresql \
kubectl get secret {{ include "hackagon.backendDatabaseSecretName" . }} \
-n {{ .Release.Namespace }} -o jsonpath='{.data.{{ include "hackagon.backendDatabaseSecretKey" . }}}' | base64 -d
{{- if .Values.postgresql.enabled }}

The bundled postgres superuser password:

kubectl get secret {{ .Release.Name }}-postgresql \
-n {{ .Release.Namespace }} -o jsonpath='{.data.postgres-password}' | base64 -d
{{- end }}

Check the status of your release:

Expand Down
28 changes: 28 additions & 0 deletions helm-chart/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,34 @@ PostgreSQL service name (bitnami chart names it <release>-postgresql)
{{- printf "%s-postgresql" .Release.Name }}
{{- end }}

{{/*
The postgres host the backend connects to. `backend.config.database.host` wins;
empty falls back to the bundled subchart's service, which is where this chart
puts postgres when `postgresql.enabled`. Rendered with `tpl`, like every other
host value.
*/}}
{{- define "hackagon.backendDatabaseHost" -}}
{{- $host := tpl (.Values.backend.config.database.host | default "") . }}
{{- $host | default (include "hackagon.postgresqlServiceName" .) }}
{{- end }}

{{/*
Secret holding the backend's database password. An `existingSecret` is used as
given; otherwise the chart manages its own, so the password never lands in the
backend ConfigMap either way.
*/}}
{{- define "hackagon.backendDatabaseSecretName" -}}
{{- .Values.backend.config.database.existingSecret | default (printf "%s-backend-db" (include "hackagon.fullname" .)) }}
{{- end }}

{{/*
Key within that Secret. The chart-managed Secret is written under the same key,
so both paths read the same way.
*/}}
{{- define "hackagon.backendDatabaseSecretKey" -}}
{{- .Values.backend.config.database.existingSecretPasswordKey | default "password" }}
{{- end }}

{{/*
Get password: use provided value or generate one
*/}}
Expand Down
7 changes: 5 additions & 2 deletions helm-chart/templates/backend-configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ data:
adminkeycloakid: {{ .Values.backend.config.server.adminkeycloakid | quote }}
database:
driver: {{ .Values.backend.config.database.driver | quote }}
host: {{ include "hackagon.postgresqlServiceName" . | quote }}
host: {{ include "hackagon.backendDatabaseHost" . | quote }}
port: {{ .Values.backend.config.database.port }}
dbname: {{ .Values.backend.config.database.dbname | quote }}
user: {{ .Values.backend.config.database.user | quote }}
password: {{ .Values.backend.config.database.postgresPassword | required "postgresql.auth.postgresPassword is required" | quote }}
# No `password` here on purpose: a ConfigMap is world-readable to anything
# that can read the namespace. The backend reads it from the environment
# instead (HACKAGON_DATABASE_PASSWORD, set from a Secret in
# backend-deployment.yaml), which koanf layers over this file.
oidc:
jwksurl: {{ tpl .Values.backend.config.oidc.jwksurl . | quote }}
issuerurl: {{ include "hackagon.oidcIssuer" . | quote }}
Expand Down
8 changes: 8 additions & 0 deletions helm-chart/templates/backend-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ spec:
{{- end }}
args:
- "--config-dir=/etc/hackagon/"
env:
# Overrides `database.password` from the mounted config.yaml, which
# deliberately leaves it unset.
- name: HACKAGON_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "hackagon.backendDatabaseSecretName" . }}
key: {{ include "hackagon.backendDatabaseSecretKey" . }}
ports:
- name: grpc
containerPort: 3000
Expand Down
13 changes: 13 additions & 0 deletions helm-chart/templates/backend-postgres-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{{- if not .Values.backend.config.database.existingSecret }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "hackagon.backendDatabaseSecretName" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "hackagon.labels" . | nindent 4 }}
app.kubernetes.io/component: backend
type: Opaque
stringData:
{{ include "hackagon.backendDatabaseSecretKey" . }}: {{ .Values.backend.config.database.postgresPassword | required "backend.config.database.postgresPassword is required unless backend.config.database.existingSecret is set" | quote }}
{{- end }}
22 changes: 16 additions & 6 deletions helm-chart/templates/keycloak-init-configmap.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
{{- if .Values.postgresql.enabled }}
{{/*
Bootstraps the roles and databases inside the postgres this chart installs. An
external postgres is expected to have them already, so with
`postgresql.enabled: false` this ConfigMap is not rendered at all and neither
password has to be given to the chart in plaintext.
*/}}
apiVersion: v1
kind: ConfigMap
metadata:
Expand All @@ -7,9 +14,12 @@ metadata:
{{- include "hackagon.labels" . | nindent 4 }}
data:
01-create-keycloak-db.sql: |
CREATE USER keycloak WITH PASSWORD '{{ .Values.keycloak.database.external.password | required "keycloak.database.external.password is required" }}';
CREATE DATABASE keycloak OWNER keycloak;
GRANT ALL PRIVILEGES ON DATABASE keycloak TO keycloak;
CREATE USER hackagon WITH PASSWORD '{{ .Values.backend.config.database.postgresPassword | required "backend.config.database.postgresPassword" }}';
CREATE DATABASE hackagon OWNER hackagon;
GRANT ALL PRIVILEGES ON DATABASE hackagon TO hackagon;
{{- if .Values.keycloak.enabled }}
CREATE USER {{ .Values.keycloak.database.external.username }} WITH PASSWORD '{{ .Values.keycloak.database.external.password }}';
CREATE DATABASE {{ .Values.keycloak.database.external.name }} OWNER {{ .Values.keycloak.database.external.username }};
GRANT ALL PRIVILEGES ON DATABASE {{ .Values.keycloak.database.external.name }} TO {{ .Values.keycloak.database.external.username }};
{{- end }}
CREATE USER {{ .Values.backend.config.database.user }} WITH PASSWORD '{{ .Values.backend.config.database.postgresPassword | required "backend.config.database.postgresPassword is required when postgresql.enabled is true" }}';
CREATE DATABASE {{ .Values.backend.config.database.dbname }} OWNER {{ .Values.backend.config.database.user }};
GRANT ALL PRIVILEGES ON DATABASE {{ .Values.backend.config.database.dbname }} TO {{ .Values.backend.config.database.user }};
{{- end }}
2 changes: 0 additions & 2 deletions helm-chart/templates/keycloak-realm-configmap.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
{{- $host := .Values.keycloak.database.external.host | required "keycloak.database.external.host is required (e.g. \"hackagon-postgresql\")" -}}
{{- $keycloakPassword := .Values.keycloak.database.external.password | required "keycloak.database.external.password is required" -}}
apiVersion: v1
kind: ConfigMap
metadata:
Expand Down
32 changes: 32 additions & 0 deletions helm-chart/templates/validations.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{{/*
Value combinations the chart cannot render. Deliberately produces no manifest;
it exists so these failures are reported in one place with a usable message
rather than as a `required` deep inside an unrelated template.
*/}}

{{- $db := .Values.backend.config.database }}

{{/*
The bundled postgres bootstraps its databases from an initdb SQL script, which
is a ConfigMap: it can only be written with a password the chart can read.
*/}}
{{- if and .Values.postgresql.enabled $db.existingSecret }}
{{- fail "backend.config.database.existingSecret is set, but postgresql.enabled is true: the bundled postgres creates the hackagon role from an init script and cannot read a Secret. Either set backend.config.database.postgresPassword instead, or set postgresql.enabled=false and provision the database on your own postgres." }}
{{- end }}

{{- if and $db.existingSecret $db.postgresPassword }}
{{- fail "backend.config.database.existingSecret and backend.config.database.password cannot be set at the same time. Choose one." }}
{{- end }}

{{- if .Values.keycloak.enabled }}
{{- if and .Values.postgresql.enabled .Values.keycloak.database.external.existingSecret }}
{{- fail "keycloak.database.external.existingSecret is set, but postgresql.enabled is true: the bundled postgres creates the keycloak role from an init script and cannot read a Secret. Either set keycloak.database.external.password instead, or set postgresql.enabled=false and provision the database on your own postgres." }}
{{- end }}
{{- $_ := .Values.keycloak.database.external.host | required "keycloak.database.external.host is required (e.g. \"hackagon-postgresql\", or the hostname of an external postgres)" }}
{{- if not .Values.keycloak.database.external.existingSecret }}
{{- $_ := .Values.keycloak.database.external.password | required "keycloak.database.external.password is required unless keycloak.database.external.existingSecret is set" }}
{{- end }}
{{- if and .Values.keycloak.database.external.existingSecret .Values.keycloak.database.external.password }}
{{- fail "keycloak.database.external.existingSecret and keycloak.database.external.password cannot be set at the same time. Choose one." }}
{{- end }}
{{- end }}
46 changes: 39 additions & 7 deletions helm-chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,25 @@ backend:
adminkeycloakid: ""
database:
driver: postgres
host: "" # Auto-generated from release name in template
# -- Postgres host the backend connects to. Empty falls back to the
# bundled subchart's service (`<release>-postgresql`). Set it to reach a
# postgres this chart does not manage — a managed provider, or one
# installed under a different release name. Rendered with `tpl`.
host: ""
port: 5432
dbname: hackagon
user: hackagon
postgresPassword: ""
# -- Password for `user`. The chart puts it in a Secret of its own and
# injects it as an env var, never into the backend ConfigMap.
postgresPassword: "" # Only use it existingSecret is empty
# -- Read the password from a Secret you already have instead of letting
# the chart manage one. Required by a postgres operator or an external
# secret store, which writes the credentials before the chart runs.
# Incompatible with `postgresql.enabled`: the bundled postgres bootstraps
# its roles from an init script and cannot read a Secret.
existingSecret: ""
# -- Key inside `existingSecret` holding the password.
existingSecretPasswordKey: password
oidc:
# -- Where the backend fetches Keycloak's signing keys.
# With an external Keycloak (keycloak.enabled: false),
Expand Down Expand Up @@ -164,15 +178,26 @@ keycloak:
admin:
username: hackagon-admin

# -- External database (reuse the same postgres instance)
# -- External database. Points at the bundled postgres by default, but any
# reachable postgres works. These keys are passed to the keycloak subchart
# verbatim, so they must use its names (`name`/`username`, not
# `database`/`user`).
database:
external:
vendor: postgres
host: "" # Required: set to <release>-postgresql (e.g. "hackagon-postgresql")
# -- Required: <release>-postgresql (e.g. "hackagon-postgresql") for the
# bundled postgres, otherwise the external host.
host: ""
port: 5432
database: keycloak
user: keycloak
password: ""
name: keycloak
username: keycloak
password: "" # Only use it existingSecret is empty
# -- Read the password from a Secret you already have. Handled by the
# keycloak subchart. Incompatible with `postgresql.enabled`, for the same
# reason as the backend's.
existingSecret: ""
# -- Key inside `existingSecret` holding the password.
existingSecretPasswordKey: password

# -- Realm import from ConfigMap
realmImport:
Expand Down Expand Up @@ -211,11 +236,18 @@ keycloakIngress:
# ============================================================
# PostgreSQL (bitnami) — two databases, two users
# ============================================================
# Set `enabled: false` to run against a postgres this chart does not install.
# Then create the `hackagon` and `keycloak` roles and databases yourself, point
# `backend.config.database.host` and `keycloak.database.external.host` at it,
# and supply the passwords — either directly or from existing Secrets.
postgresql:
enabled: true
auth:
username: postgres
database: postgres
# -- The bitnami subchart also accepts `auth.existingSecret` together with
# `auth.secretKeys.adminPasswordKey`, if you would rather not put the
# superuser password in values.
postgresPassword: ""

primary:
Expand Down
Loading