diff --git a/CHANGELOG.md b/CHANGELOG.md index bb25e69adaa1c726bbbf98e0a04f88eecafccc4b..defb8bf78b3c431c6a1d26e4a45cd94c8f659cf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [0.7.0] - Unreleased + +* **BREAKING**: the `oas-secrets` helm chart has been removed. Because of this + `flux` will remove your existing secrets when you change the tracking branch + from `v0.6` to `v0.7`. Follow our [upgrade + guide](https://docs.openappstack.net/en/latest/upgrading.html) to prevent data + loss +* TODO INSERT MORE HERE + ## [0.6.0] - 2021-07-14 ### Added diff --git a/Dockerfile b/Dockerfile index 7f88afb1fe134464d2ee9e7d5a27538cfde5ed98..48b5e5eaad456340916da7c2b74d3a346916b799 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ COPY ./requirements.txt /requirements.txt RUN \ apk --no-cache add \ bash=~5.1.4-r0 \ - cargo=~1.52.0-r0 \ + cargo=~1.52.1-r1 \ chromium=~91.0.4472.164-r0 \ curl=~7.78.0-r0 \ # needed for installing pycurl python module diff --git a/charts/oas-secrets/.helmignore b/charts/oas-secrets/.helmignore deleted file mode 100644 index 0e8a0eb36f4ca2c939201c0d54b5d82a1ea34778..0000000000000000000000000000000000000000 --- a/charts/oas-secrets/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# 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/ diff --git a/charts/oas-secrets/Chart.yaml b/charts/oas-secrets/Chart.yaml deleted file mode 100644 index cbc26985a3f96d4f83f47a443b7e1d8a9ce5a149..0000000000000000000000000000000000000000 --- a/charts/oas-secrets/Chart.yaml +++ /dev/null @@ -1,28 +0,0 @@ ---- -apiVersion: v2 -name: secrets -description: | - A helm chart to generate secrets that are needed by other helm charts. Values - inside the secret will only be generated if they do not exist yet, so it is - safe to run `helm upgrade` on this chart. - -# 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.9 - -# 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: "0.1.0" diff --git a/charts/oas-secrets/templates/NOTES.txt b/charts/oas-secrets/templates/NOTES.txt deleted file mode 100644 index 5e349b352c73698680859520c3556ce353f94de0..0000000000000000000000000000000000000000 --- a/charts/oas-secrets/templates/NOTES.txt +++ /dev/null @@ -1 +0,0 @@ -Secrets generated diff --git a/charts/oas-secrets/templates/_helpers.tpl b/charts/oas-secrets/templates/_helpers.tpl deleted file mode 100644 index d791991eac699d079688804ab0447aac72ee8bf0..0000000000000000000000000000000000000000 --- a/charts/oas-secrets/templates/_helpers.tpl +++ /dev/null @@ -1,84 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "secrets.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 "secrets.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 "secrets.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "secrets.labels" -}} -helm.sh/chart: {{ include "secrets.chart" . }} -{{ include "secrets.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "secrets.selectorLabels" -}} -app.kubernetes.io/name: {{ include "secrets.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "secrets.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "secrets.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} - - -{{/* -Returns a secret if it already in Kubernetes, otherwise it creates -it randomly. -Special thanks to @iTaybb: https://github.com/helm/charts/issues/5167#issuecomment-840698657 -*/}} -{{- define "getOrGeneratePass" }} -{{- $len := (default 16 .Length) | int -}} -{{- $obj := (lookup "v1" .Kind .Namespace .Name).data -}} -{{- $val := "" -}} -{{- if $obj }} -{{- $val := (index $obj .Key) -}} -{{- end -}} -{{- if $val }} -{{- $val -}} -{{- else if (eq (lower .Kind) "secret") -}} -{{- randAlphaNum $len | b64enc -}} -{{- else -}} -{{- randAlphaNum $len -}} -{{- end -}} -{{- end }} \ No newline at end of file diff --git a/charts/oas-secrets/templates/oas-alertmanager-basic-auth.yaml b/charts/oas-secrets/templates/oas-alertmanager-basic-auth.yaml deleted file mode 100644 index f4471fda9ead646670925392fefc4e7730a5aabe..0000000000000000000000000000000000000000 --- a/charts/oas-secrets/templates/oas-alertmanager-basic-auth.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -apiVersion: v1 -kind: Secret -metadata: - namespace: "oas" - name: "oas-alertmanager-basic-auth" -data: - {{- $pass := (include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-alertmanager-basic-auth" "Key" "pass")) | b64dec -}} - {{/* Readable version of the password for humans who want to log in */}} - pass: {{ $pass | b64enc }} - {{/* Encoded version of the password for nginx ingress */}} - auth: {{ htpasswd "admin" $pass | b64enc }} diff --git a/charts/oas-secrets/templates/oas-kube-prometheus-stack-variables.yaml b/charts/oas-secrets/templates/oas-kube-prometheus-stack-variables.yaml deleted file mode 100644 index 0781a9b19205a9b44d26d3d5080e04e3eefd9728..0000000000000000000000000000000000000000 --- a/charts/oas-secrets/templates/oas-kube-prometheus-stack-variables.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -apiVersion: v1 -kind: Secret -metadata: - name: oas-kube-prometheus-stack-variables -data: - grafana_admin_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-kube-prometheus-stack-variables" "Key" "grafana_admin_password") }}" diff --git a/charts/oas-secrets/templates/oas-nextcloud-variables.yaml b/charts/oas-secrets/templates/oas-nextcloud-variables.yaml deleted file mode 100644 index 017cc80830ec4f554aa37586bb5d3149b13eda32..0000000000000000000000000000000000000000 --- a/charts/oas-secrets/templates/oas-nextcloud-variables.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -apiVersion: v1 -kind: Secret -metadata: - name: oas-nextcloud-variables -data: - nextcloud_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-nextcloud-variables" "Key" "nextcloud_password") }}" - nextcloud_mariadb_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-nextcloud-variables" "Key" "nextcloud_mariadb_password") }}" - nextcloud_mariadb_root_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-nextcloud-variables" "Key" "nextcloud_mariadb_root_password") }}" - onlyoffice_jwt_secret: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-nextcloud-variables" "Key" "onlyoffice_jwt_secret") }}" - onlyoffice_postgresql_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-nextcloud-variables" "Key" "onlyoffice_postgresql_password") }}" - onlyoffice_rabbitmq_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-nextcloud-variables" "Key" "onlyoffice_rabbitmq_password") }}" diff --git a/charts/oas-secrets/templates/oas-oauth-variables.yaml b/charts/oas-secrets/templates/oas-oauth-variables.yaml deleted file mode 100644 index 4f4f67a8d836957af71e50443157b9e48329a915..0000000000000000000000000000000000000000 --- a/charts/oas-secrets/templates/oas-oauth-variables.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -apiVersion: v1 -kind: Secret -metadata: - name: oas-oauth-variables -data: - grafana_oauth_client_secret: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-oauth-variables" "Key" "grafana_oauth_client_secret") }}" - nextcloud_oauth_client_secret: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-oauth-variables" "Key" "nextcloud_oauth_client_secret") }}" - rocketchat_oauth_client_secret: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-oauth-variables" "Key" "rocketchat_oauth_client_secret") }}" - userpanel_oauth_client_secret: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-oauth-variables" "Key" "userpanel_oauth_client_secret") }}" - wekan_oauth_client_secret: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-oauth-variables" "Key" "wekan_oauth_client_secret") }}" - wordpress_oauth_client_secret: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-oauth-variables" "Key" "wordpress_oauth_client_secret") }}" diff --git a/charts/oas-secrets/templates/oas-prometheus-basic-auth.yaml b/charts/oas-secrets/templates/oas-prometheus-basic-auth.yaml deleted file mode 100644 index f094270a286fc3345cd2b1f71501d573422c2ead..0000000000000000000000000000000000000000 --- a/charts/oas-secrets/templates/oas-prometheus-basic-auth.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - namespace: "oas" - name: "oas-prometheus-basic-auth" -data: - {{- $pass := (include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-prometheus-basic-auth" "Key" "pass")) | b64dec -}} - {{/* Readable version of the password for humans who want to log in */}} - pass: {{ $pass | b64enc }} - {{/* Encoded version of the password for nginx ingress */}} - auth: {{ htpasswd "admin" $pass | b64enc }} diff --git a/charts/oas-secrets/templates/oas-rocketchat-variables.yaml b/charts/oas-secrets/templates/oas-rocketchat-variables.yaml deleted file mode 100644 index 747ed200b009ee6bd48b7de162265a633c8cdb11..0000000000000000000000000000000000000000 --- a/charts/oas-secrets/templates/oas-rocketchat-variables.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: oas-rocketchat-variables -data: - mongodb_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-rocketchat-variables" "Key" "mongodb_password") }}" - mongodb_root_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-rocketchat-variables" "Key" "mongodb_root_password") }}" - rocketchat_admin_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-rocketchat-variables" "Key" "rocketchat_admin_password") }}" diff --git a/charts/oas-secrets/templates/oas-single-sign-on-variables.yaml b/charts/oas-secrets/templates/oas-single-sign-on-variables.yaml deleted file mode 100644 index 55b29a84bd5a0ac36be99d083d9e5905dbaddcff..0000000000000000000000000000000000000000 --- a/charts/oas-secrets/templates/oas-single-sign-on-variables.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -apiVersion: v1 -kind: Secret -metadata: - name: oas-single-sign-on-variables -data: - userbackend_admin_username: {{ b64enc "admin" }} - userbackend_admin_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-single-sign-on-variables" "Key" "userbackend_admin_password") }}" - userbackend_postgres_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-single-sign-on-variables" "Key" "userbackend_postgres_password") }}" - hydra_system_secret: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-single-sign-on-variables" "Key" "hydra_system_secret") }}" diff --git a/charts/oas-secrets/templates/oas-wekan-variables.yaml b/charts/oas-secrets/templates/oas-wekan-variables.yaml deleted file mode 100644 index 1f0ffccc1d3acdf7200e7d1daa47a8465f3a5d70..0000000000000000000000000000000000000000 --- a/charts/oas-secrets/templates/oas-wekan-variables.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: oas-wekan-variables -data: - mongodb_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-wekan-variables" "Key" "mongodb_password") }}" - mongodb_root_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-wekan-variables" "Key" "mongodb_root_password") }}" diff --git a/charts/oas-secrets/templates/oas-wordpress-variables.yaml b/charts/oas-secrets/templates/oas-wordpress-variables.yaml deleted file mode 100644 index 37df564996fa95d8e0610f4a11711676a3cf0f21..0000000000000000000000000000000000000000 --- a/charts/oas-secrets/templates/oas-wordpress-variables.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -apiVersion: v1 -kind: Secret -metadata: - name: oas-wordpress-variables -data: - wordpress_admin_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-wordpress-variables" "Key" "wordpress_admin_password") }}" - wordpress_mariadb_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-wordpress-variables" "Key" "wordpress_mariadb_password") }}" - wordpress_mariadb_root_password: "{{ include "getOrGeneratePass" (dict "Namespace" .Release.Namespace "Kind" "Secret" "Name" "oas-wordpress-variables" "Key" "wordpress_mariadb_root_password") }}" diff --git a/charts/oas-secrets/values.yaml b/charts/oas-secrets/values.yaml deleted file mode 100644 index 449682fa418728aacfada08e962f59a297aca5c2..0000000000000000000000000000000000000000 --- a/charts/oas-secrets/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# The domain OpenAppStack runs on. There needs to be an A record to -# oas.example.com and an additional record *.oas.example.com, that both point to -# this machine -domain: oas.example.com -# The email address of the cluster administrator -admin_email: admin@oas.example.com \ No newline at end of file diff --git a/docs/upgrading.rst b/docs/upgrading.rst index 17bc83dcd4a630615ab2a077b2d462c83699ac62..26d2da8c0283d68bc10997286d1540d7fcf6f61b 100644 --- a/docs/upgrading.rst +++ b/docs/upgrading.rst @@ -1,6 +1,154 @@ Upgrading OpenAppStack ====================== +Upgrading to 0.7.0 +------------------ + +Because of `problems with Helm and secret management +<https://open.greenhost.net/openappstack/openappstack/-/issues/891>`__ +we had to move away from using a helm chart for secrets, and now use scripts +that run during installation to manage secrets. Because we have removed the +``oas-secrets`` helm chart, Flux will remove the secrets that it has +generated. **It is important that you back up these secrets before switching +from ``v0.6`` to ``v0.7``!** + +Before you start, please ensure that you have the right ``yq`` tool installed, +because you will need it later. There are two very different versions of +``yq``. The one you need is the go based [yq from Mike +Farah](http://mikefarah.github.io/yq/), which installs the same binary name ``yq`` +as the [python-yq](https://github.com/kislyuk/yq), while both have different +command sets. + +The yq needed here can be installed by running ``sudo snap install yq``, +``brew install yq`` or with other methods from the [``yq`` installation +instructions](http://mikefarah.github.io/yq/#install). + + +To back-up your secrets, run the following script: + +.. code:: bash + + bash + #!/usr/bin/env bash + + mkdir secrets-backup + + kubectl get secret -o yaml -n flux-system oas-cluster-variables > secrets-backup/oas-cluster-variables.yaml + kubectl get secret -o yaml -n flux-system oas-wordpress-variables > secrets-backup/oas-wordpress-variables.yaml + kubectl get secret -o yaml -n flux-system oas-wekan-variables > secrets-backup/oas-wekan-variables.yaml + kubectl get secret -o yaml -n flux-system oas-single-sign-on-variables > secrets-backup/oas-single-sign-on-variables.yaml + kubectl get secret -o yaml -n flux-system oas-rocketchat-variables > secrets-backup/oas-rocketchat-variables.yaml + kubectl get secret -o yaml -n flux-system oas-kube-prometheus-stack-variables > secrets-backup/oas-kube-prometheus-stack-variables.yaml + kubectl get secret -o yaml -n oas oas-prometheus-basic-auth > secrets-backup/oas-prometheus-basic-auth.yaml + kubectl get secret -o yaml -n oas oas-alertmanager-basic-auth > secrets-backup/oas-alertmanager-basic-auth.yaml + kubectl get secret -o yaml -n flux-system oas-oauth-variables > secrets-backup/oas-oauth-variables.yaml + kubectl get secret -o yaml -n flux-system oas-nextcloud-variables > secrets-backup/oas-nextcloud-variables.yaml + +This script assumes you have all applications enabled. You might get an error +like: + +.. code:: bash + + Error from server (NotFound): secrets "oas-wekan-variables" not found + +This is not a problem, but it *does* mean you need to add an oauth secret for +Wekan to the file ``secrets-backup/oas-oauth-variables.yaml``. Copy one of the +lines under "data:", rename the field to ``wekan_oauth_client_secret`` and enter +a different random password. Make sure to base64 encode it (``echo "<your random +password>" | base64``). + +If you get the error several times, that means ther + +This script creates a directory called ``secrets-backup`` and places the secrets +that have been generated by Helm in it as ``yaml`` files. + +Now you can upgrade your cluster by running ``kubectl edit gitrepository -n +flux-system openappstack`` and setting ``spec.ref.branch`` to ``v0.7`` + +Flux will now start updating your cluster to version 0.7. This process will fail, +because it will remove the secrets that you just backed up. Make +sure that the ``oas-secrets`` helmrelease has been removed by running ``flux get +hr -A``. You might also see that some helmreleases start failing to be installed +because important secrets do not exist anymore. + +As soon as the ``oas-secrets`` helmrelease does not exist anymore, you can run +the following code: + +.. code:: bash + + #!/usr/bin/env bash + + # Again: make sure you use https://github.com/mikefarah/yq -- install with `snap install yq` + yq eval 'del(.metadata.annotations,.metadata.labels,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.uid)' secrets-backup/oas-cluster-variables.yaml | kubectl apply -f - -n flux-system + yq eval 'del(.metadata.annotations,.metadata.labels,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.uid)' secrets-backup/oas-wordpress-variables.yaml | kubectl apply -f - -n flux-system + yq eval 'del(.metadata.annotations,.metadata.labels,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.uid)' secrets-backup/oas-wekan-variables.yaml | kubectl apply -f - -n flux-system + yq eval 'del(.metadata.annotations,.metadata.labels,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.uid)' secrets-backup/oas-single-sign-on-variables.yaml | kubectl apply -f - -n flux-system + yq eval 'del(.metadata.annotations,.metadata.labels,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.uid)' secrets-backup/oas-rocketchat-variables.yaml | kubectl apply -f - -n flux-system + yq eval 'del(.metadata.annotations,.metadata.labels,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.uid)' secrets-backup/oas-kube-prometheus-stack-variables.yaml | kubectl apply -f - -n flux-system + yq eval 'del(.metadata.annotations,.metadata.labels,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.uid)' secrets-backup/oas-prometheus-basic-auth.yaml | kubectl apply -f - -n flux-system + yq eval 'del(.metadata.annotations,.metadata.labels,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.uid)' secrets-backup/oas-alertmanager-basic-auth.yaml | kubectl apply -f - -n flux-system + yq eval 'del(.metadata.annotations,.metadata.labels,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.uid)' secrets-backup/oas-oauth-variables.yaml | kubectl apply -f - -n flux-system + yq eval 'del(.metadata.annotations,.metadata.labels,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.uid)' secrets-backup/oas-nextcloud-variables.yaml | kubectl apply -f - -n flux-system + +Again this script assumes you have all applications installed. If you get the +following error, you can ignore it: + +.. code:: bash + + error: error validating "STDIN": error validating data: [apiVersion not set, kind not set]; if you choose to ignore these errors, turn validation off with --validate=false + +Now Flux should succeed in finishing the update. Some helmreleases or +kustomizations might have already failed because the secrets did not exist. Once +failed, you can retrigger reconciliation of a kustomization using the commands +``flux reconcile kustomization ...`` or ``flux reconcile helmrelease ...``. This +can take quite a while (over an hour some times), because Flux waits for some +long timeouts before giving up and re-starting a reconciliation. + +Some errors we've seen during our own upgrade process, and how to solve them +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +SSO helm upgrade failed +''''''''''''''''''''''' + +.. code:: + + oas single-sign-on False Helm upgrade failed: template: single-sign-on/templates/secret-oauth2-clients.yaml:9:55: executing "single-sign-on/templates/secret-oauth2-clients.yaml" at <b64enc>: invalid value; expected string 0.2.2 False + +This means that the ``single-sign-on`` helmrelease was created with empty oauth +secrets. The secrets will get a value once the ``core`` *kustomization* is +reconciled: ``flux reconcile ks core`` should solve the problem. + +If that does not solve the problem, you should check if the secret contains a +value for all the apps: + +.. code:: + + # kubectl get secret -n flux-system oas-oauth-variables -o yaml + apiVersion: v1 + data: + grafana_oauth_client_secret: <redacted> + nextcloud_oauth_client_secret: <redacted> + rocketchat_oauth_client_secret: <redacted> + userpanel_oauth_client_secret: <redacted> + wekan_oauth_client_secret: <redacted> + wordpress_oauth_client_secret: <redacted> + ... + +If your secret lacks one of these variables, use ``kubectl edit`` to add them. +You can use any password generator to generate a password for it. Make sure to +base64 encode the data before you enter it in the secret. + +Loki upgrade retries exhausted +'''''''''''''''''''''''''''''' + +While running ``flux get helmrelease -A``, you'll see: + +.. code:: + oas loki False upgrade retries exhausted 2.5.2 False + +This happens sometimes because Loki takes a long time to upgrade. Usually it is +solved by running ``flux reconcile hr loki -n oas`` again. + Upgrading to 0.6.0 ------------------ @@ -19,7 +167,7 @@ production. Upgrading from 0.4.0 to 0.5.0 ----------------------------- -Unfortunatly we can’t ensure a smooth upgrade for this version neither. +Unfortunately we can’t ensure a smooth upgrade for this version neither. Please read the section below on how to do an upgrade by installing a the new OAS version from scratch after backing up your data. diff --git a/flux2/README.md b/flux2/README.md index c6f022574531811150d4222006d6bd6296861c49..6c7fd51a0933999802c3b7ad46b0d796375a64a7 100644 --- a/flux2/README.md +++ b/flux2/README.md @@ -33,6 +33,5 @@ flux2 ├── local-path-provisioner # (default) storage class to safe files on disk ├── namespaces # namespaces used by OAS ├── nginx # Ingress - ├── secrets # Auto-generated secrets for applications └── sources # Helm repositories needed for HelmReleases from other folders ``` diff --git a/flux2/cluster/base/infrastructure.yaml b/flux2/cluster/base/infrastructure.yaml index 5de7ecd6ee7b75e5f19cb89006e4e13bf118bf08..53c949da1bfd9a140c56c273b6a20f21bbefd3be 100644 --- a/flux2/cluster/base/infrastructure.yaml +++ b/flux2/cluster/base/infrastructure.yaml @@ -21,7 +21,3 @@ spec: kind: HelmRelease name: local-path-provisioner namespace: kube-system - - apiVersion: helm.toolkit.fluxcd.io/v1beta1 - kind: HelmRelease - name: oas-secrets - namespace: flux-system diff --git a/flux2/infrastructure/secrets/kustomization.yaml b/flux2/infrastructure/secrets/kustomization.yaml deleted file mode 100644 index c6fd78466ae94c2384b8537ba6d4edc538326709..0000000000000000000000000000000000000000 --- a/flux2/infrastructure/secrets/kustomization.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -# The secrets will be used by Flux in the postBuild Kustomization step and need -# to be in the flux-system namespace -namespace: flux-system -resources: - - release.yaml \ No newline at end of file diff --git a/flux2/infrastructure/secrets/release.yaml b/flux2/infrastructure/secrets/release.yaml deleted file mode 100644 index 4073c1768bdba6a4e64880fbe108d1e15e4dd62d..0000000000000000000000000000000000000000 --- a/flux2/infrastructure/secrets/release.yaml +++ /dev/null @@ -1,24 +0,0 @@ ---- -apiVersion: helm.toolkit.fluxcd.io/v2beta1 -kind: HelmRelease -metadata: - name: oas-secrets -spec: - releaseName: oas-secrets - chart: - spec: - chart: ./charts/oas-secrets/ - # NOTE: Change the GitRepository yaml file if you want a different version - sourceRef: - kind: GitRepository - name: openappstack - namespace: flux-system - interval: 1h0m0s - # Allow custom values either by configMap or by secret - valuesFrom: - - kind: ConfigMap - name: oas-secrets-override - optional: true - - kind: Secret - name: oas-secrets-override - optional: true diff --git a/install/generate_secrets.py b/install/generate_secrets.py new file mode 100644 index 0000000000000000000000000000000000000000..93f5702c779a42061d9b3e15b70d33a1310adf69 --- /dev/null +++ b/install/generate_secrets.py @@ -0,0 +1,148 @@ +""" +Generates Kubernetes secrets based on a provided app name. + +If the `templates` directory contains a secret called `oas-{app}-variables`, it +will check if that secret already exists in the cluster, and if not: generate +it. It does the same for an `oas-{app}-basic-auth` secret that will contain a +password as well as a htpasswd encoded version of it. + +See https://open.greenhost.net/openappstack/openappstack/-/issues/891 for the +context why we use this script and not a helm chart to generate secrets. + +usage: python generate_secrets.py template_filename.j2 +""" + +import crypt +import os +import secrets +import string +import sys +import yaml + +import jinja2 +import jinja2_base64_filters # pylint: disable=unused-import +from kubernetes import client, config +from kubernetes.client.exceptions import ApiException +from kubernetes.utils import create_from_yaml + + +def main(): + """Run everything""" + # Add jinja filters we want to use + env = jinja2.Environment(extensions=["jinja2_base64_filters.Base64Filters"]) + env.filters["generate_password"] = generate_password + + if len(sys.argv) < 2: + print("Please provide an app name as an argument") + sys.exit(1) + app_name = sys.argv[1] + + create_variables_secret(app_name, env) + create_basic_auth_secret(app_name, env) + + +def get_templates_dir(): + """Returns directory that contains the Jinja templates used to create app + secrets""" + return os.path.join(os.path.dirname(os.path.realpath(__file__)), 'templates') + + +def create_variables_secret(app_name, env): + """Checks if a variables secret for app_name already exists, generates it if necessary""" + variables_filename = \ + os.path.join(get_templates_dir(), f"oas-{app_name}-variables.yaml.jinja") + if os.path.exists(variables_filename): + # Check if k8s secret already exists, if not, generate it + with open(variables_filename) as template_file: + lines = template_file.read() + secret_name, secret_namespace = get_secret_metadata(lines) + if get_kubernetes_secret(secret_name, secret_namespace) is None: + print(f"Adding secret {secret_name} in namespace" + f" {secret_namespace} to cluster.") + template = env.from_string(lines) + store_kubernetes_secret(template.render(), secret_namespace) + else: + print(f"Secret {secret_name} in namespace {secret_namespace}" + " already exists. Not generating new secrets.") + + +def create_basic_auth_secret(app_name, env): + """Checks if a basic auth secret for app_name already exists, generates it if necessary""" + basic_auth_filename = \ + os.path.join(get_templates_dir(), f"oas-{app_name}-basic-auth.yaml.jinja") + if os.path.exists(basic_auth_filename): + with open(basic_auth_filename) as template_file: + lines = template_file.read() + secret_name, secret_namespace = get_secret_metadata(lines) + + if get_kubernetes_secret(secret_name, secret_namespace) is None: + basic_auth_username = 'admin' + basic_auth_password = generate_password(32) + basic_auth_htpasswd = gen_htpasswd( + basic_auth_username, + basic_auth_password) + print(f"Adding secret {secret_name} in namespace" + f" {secret_namespace} to cluster.") + template = env.from_string( + lines, + globals={ + 'pass': basic_auth_password, + 'htpasswd': basic_auth_htpasswd + }) + store_kubernetes_secret(template.render(), secret_namespace) + else: + print(f"Secret {secret_name} in namespace {secret_namespace}" + " already exists. Not generating new secrets.") + +def get_secret_metadata(yaml_string): + """Returns secret name and namespace from metadata field in a yaml string""" + secret_info = yaml.safe_load(yaml_string) + secret_name = secret_info['metadata']['name'] + # default namespace is flux-system, but other namespace can be + # provided in secret metadata + if 'namespace' in secret_info['metadata']: + secret_namespace = secret_info['metadata']['namespace'] + else: + secret_namespace = 'flux-system' + return secret_name, secret_namespace + + +def get_kubernetes_secret(secret_name, namespace): + """Returns the contents of a kubernetes secret or None if the secret does not exist.""" + try: + secret = API.read_namespaced_secret(secret_name, namespace).data + except ApiException as ex: + # 404 is expected when the optional secret does not exist. + if ex.status != 404: + raise ex + return None + return secret + +def store_kubernetes_secret(secret_string, namespace): + """Converts secret_string into a yaml object and adds it to the cluster""" + secret_yaml = yaml.safe_load(secret_string) + api_client = client.api_client.ApiClient() + api_response = create_from_yaml( + api_client, + yaml_objects=[secret_yaml], + namespace=namespace) + print(f"Secret created with api response: {api_response}") + +def generate_password(length): + """Generates a password of "length" characters""" + length = int(length) + password = ''.join((secrets.choice(string.ascii_letters) for i in range(length))) + return password + + +def gen_htpasswd(user, password): + """generate htpasswd entry for user with password""" + return "{}:{}".format(user, crypt.crypt( + password, crypt.mksalt(crypt.METHOD_SHA512) + ), + ) + +if __name__ == "__main__": + config.load_kube_config() + API = client.CoreV1Api() + main() diff --git a/install/install-app.sh b/install/install-app.sh index a3f7b47cfc36ab8e3ef343540a3bc2f33807f3ff..bd9d58a7ad03ed01133eded5ba2fdcf3cf55fde7 100755 --- a/install/install-app.sh +++ b/install/install-app.sh @@ -7,6 +7,10 @@ app=$1 # shellcheck disable=SC1090 . "$(dirname "$0")/flux-version-check.sh" +# Check if the secrets for the app already exist in the cluster, and if not, +# generate them and add them to the cluster. +python "$(dirname "$0")/generate_secrets.py" $app + # This kustomization's only purpose is to add the kustomization that is in the # flxu2/cluster/optional/$app folder. After this kustomization is applied # an `add-$app` kustomization will be present on the cluster, as well as a diff --git a/install/install-openappstack.sh b/install/install-openappstack.sh index ee2fb2e44e7281cf1ba08d4e162a2d0ced65d80c..2056236eec5dacedb769925f68fa4bac77987217 100755 --- a/install/install-openappstack.sh +++ b/install/install-openappstack.sh @@ -15,6 +15,18 @@ branch=${CI_COMMIT_REF_NAME:-} echo "Tracking branch $branch for https://open.greenhost.net/openappstack/openappstack flux repo" +# Create oas and oas-apps namespaces +kubectl create namespace oas +kubectl create namespace oas-apps + +# Generate oauth and SSO secrets +python "$(dirname "$0")/generate_secrets.py" single-sign-on +python "$(dirname "$0")/generate_secrets.py" oauth + +# Generate secrets for monitoring +python "$(dirname "$0")/generate_secrets.py" kube-prometheus-stack +python "$(dirname "$0")/generate_secrets.py" alertmanager + flux create source git openappstack \ --url=https://open.greenhost.net/openappstack/openappstack \ --branch=$branch \ diff --git a/install/templates/oas-alertmanager-basic-auth.yaml.jinja b/install/templates/oas-alertmanager-basic-auth.yaml.jinja new file mode 100644 index 0000000000000000000000000000000000000000..fedb272ab91f0794367040944c361f9bccd94690 --- /dev/null +++ b/install/templates/oas-alertmanager-basic-auth.yaml.jinja @@ -0,0 +1,9 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + namespace: "oas" + name: "oas-alertmanager-basic-auth" +data: + pass: "{{ pass | b64encode }}" + auth: "{{ htpasswd | b64encode }}" diff --git a/install/templates/oas-kube-prometheus-stack-variables.yaml.jinja b/install/templates/oas-kube-prometheus-stack-variables.yaml.jinja new file mode 100644 index 0000000000000000000000000000000000000000..035baf54b938c396a8012ca9e36625c26e73750c --- /dev/null +++ b/install/templates/oas-kube-prometheus-stack-variables.yaml.jinja @@ -0,0 +1,7 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: oas-kube-prometheus-stack-variables +data: + grafana_admin_password: "{{ 32 | generate_password | b64encode }}" diff --git a/install/templates/oas-nextcloud-variables.yaml.jinja b/install/templates/oas-nextcloud-variables.yaml.jinja new file mode 100644 index 0000000000000000000000000000000000000000..6ef7809d889c53c9761eeb1b0c771301653b2cb0 --- /dev/null +++ b/install/templates/oas-nextcloud-variables.yaml.jinja @@ -0,0 +1,12 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: oas-nextcloud-variables +data: + nextcloud_password: "{{ 32 | generate_password | b64encode }}" + nextcloud_mariadb_password: "{{ 32 | generate_password | b64encode }}" + nextcloud_mariadb_root_password: "{{ 32 | generate_password | b64encode }}" + onlyoffice_jwt_secret: "{{ 32 | generate_password | b64encode }}" + onlyoffice_postgresql_password: "{{ 32 | generate_password | b64encode }}" + onlyoffice_rabbitmq_password: "{{ 32 | generate_password | b64encode }}" diff --git a/install/templates/oas-oauth-variables.yaml.jinja b/install/templates/oas-oauth-variables.yaml.jinja new file mode 100644 index 0000000000000000000000000000000000000000..befd2f774e40402a86caf20ea51c7173342165f7 --- /dev/null +++ b/install/templates/oas-oauth-variables.yaml.jinja @@ -0,0 +1,12 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: oas-oauth-variables +data: + grafana_oauth_client_secret: "{{ 32 | generate_password | b64encode }}" + nextcloud_oauth_client_secret: "{{ 32 | generate_password | b64encode }}" + rocketchat_oauth_client_secret: "{{ 32 | generate_password | b64encode }}" + userpanel_oauth_client_secret: "{{ 32 | generate_password | b64encode }}" + wekan_oauth_client_secret: "{{ 32 | generate_password | b64encode }}" + wordpress_oauth_client_secret: "{{ 32 | generate_password | b64encode }}" diff --git a/install/templates/oas-prometheus-basic-auth.yaml b/install/templates/oas-prometheus-basic-auth.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d4d2d35521890ed6a3f53951b8a77070a1b8d2f6 --- /dev/null +++ b/install/templates/oas-prometheus-basic-auth.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + namespace: "oas" + name: "oas-prometheus-basic-auth" +data: + # Readable version of the password for humans who want to log in + pass: "{{ pass | b64encode }}" + # Encoded version of the password for nginx ingress + auth: "{{ htpasswd | b64encode }}" diff --git a/install/templates/oas-rocketchat-variables.yaml.jinja b/install/templates/oas-rocketchat-variables.yaml.jinja new file mode 100644 index 0000000000000000000000000000000000000000..4bce7f2911c2375e567b9a16333e89f7ff0c4453 --- /dev/null +++ b/install/templates/oas-rocketchat-variables.yaml.jinja @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: oas-rocketchat-variables +data: + mongodb_password: "{{ 32 | generate_password | b64encode }}" + mongodb_root_password: "{{ 32 | generate_password | b64encode }}" + rocketchat_admin_password: "{{ 32 | generate_password | b64encode }}" diff --git a/install/templates/oas-single-sign-on-variables.yaml.jinja b/install/templates/oas-single-sign-on-variables.yaml.jinja new file mode 100644 index 0000000000000000000000000000000000000000..7424dd4ed4eb672b4030ff632b560f2b19852e80 --- /dev/null +++ b/install/templates/oas-single-sign-on-variables.yaml.jinja @@ -0,0 +1,10 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: oas-single-sign-on-variables +data: + userbackend_admin_username: '{{ "admin" | b64encode }}' + userbackend_admin_password: "{{ 32 | generate_password | b64encode }}" + userbackend_postgres_password: "{{ 32 | generate_password | b64encode }}" + hydra_system_secret: "{{ 32 | generate_password | b64encode }}" diff --git a/install/templates/oas-wekan-variables.yaml.jinja b/install/templates/oas-wekan-variables.yaml.jinja new file mode 100644 index 0000000000000000000000000000000000000000..f3cc5a366cd1b39a18a41903a43f3cff7dee61e0 --- /dev/null +++ b/install/templates/oas-wekan-variables.yaml.jinja @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Secret +metadata: + name: oas-wekan-variables +data: + mongodb_password: "{{ 32 | generate_password | b64encode }}" + mongodb_root_password: "{{ 32 | generate_password | b64encode }}" diff --git a/install/templates/oas-wordpress-variables.yaml.jinja b/install/templates/oas-wordpress-variables.yaml.jinja new file mode 100644 index 0000000000000000000000000000000000000000..18bf4339ca9afa86d1583f9a6ab0716a578fa00d --- /dev/null +++ b/install/templates/oas-wordpress-variables.yaml.jinja @@ -0,0 +1,9 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: oas-wordpress-variables +data: + wordpress_admin_password: "{{ 32 | generate_password | b64encode }}" + wordpress_mariadb_password: "{{ 32 | generate_password | b64encode }}" + wordpress_mariadb_root_password: "{{ 32 | generate_password | b64encode }}" diff --git a/requirements.in b/requirements.in index 6708866660e6ef43d0948e81b7c06ec841e23271..cf4e84c5723c94bd8f6df12a9eabffd59baf573a 100644 --- a/requirements.in +++ b/requirements.in @@ -15,7 +15,12 @@ ansible<2.10 # needed for test_dns.py dnspython -kubernetes +# `install/generate_secrets.py` needs the `create_from_yaml` function that accepts +# the `yaml_objects` parameter which was added in commit +# 13814c0f7e0e587c46512386da3d08c64fc83e04. Until that is merged into a stable +# release, we install from Git. Should be replaced with `kubernetes>18` some time +# in the future. +-e git+git://github.com/kubernetes-client/python.git@2f9643eac71535f7802cd16a078ce50e8866f1ad#egg=kubernetes # Needed for testinfra using the ansible module paramiko psutil @@ -28,3 +33,6 @@ setuptools wheel -e git+https://open.greenhost.net/greenhost/cloud-api#egg=greenhost_cloud passlib +# Needed for secrets generation in install/generate_secrets.py +jinja2-base64-filters +htpasswd diff --git a/requirements.txt b/requirements.txt index b56d665d4b1d51b96755cbc494d8ad2cb4a36997..036e5ae92ed16a265dbd7734c2bb448cdefb5001 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,9 @@ # -e git+https://open.greenhost.net/greenhost/cloud-api#egg=greenhost_cloud # via -r requirements.in -ansible==2.9.23 +-e git+git://github.com/kubernetes-client/python.git@2f9643eac71535f7802cd16a078ce50e8866f1ad#egg=kubernetes + # via -r requirements.in +ansible==2.9.24 # via -r requirements.in attrs==21.2.0 # via pytest @@ -23,7 +25,7 @@ cffi==1.14.6 # bcrypt # cryptography # pynacl -charset-normalizer==2.0.1 +charset-normalizer==2.0.4 # via requests cryptography==3.4.7 # via @@ -32,20 +34,26 @@ cryptography==3.4.7 # pyopenssl dnspython==2.1.0 # via -r requirements.in -google-auth==1.32.1 +google-auth==1.34.0 # via kubernetes +htpasswd==2.3 + # via -r requirements.in idna==3.2 # via requests iniconfig==1.1.1 # via pytest jinja2==3.0.1 - # via ansible -kubernetes==17.17.0 + # via + # ansible + # jinja2-base64-filters +jinja2-base64-filters==0.1.4 # via -r requirements.in markupsafe==2.0.1 # via jinja2 oauthlib==3.1.1 # via requests-oauthlib +orderedmultidict==1.0.1 + # via htpasswd packaging==21.0 # via pytest paramiko==2.7.2 @@ -102,6 +110,7 @@ six==1.16.0 # bcrypt # google-auth # kubernetes + # orderedmultidict # pynacl # pyopenssl # python-dateutil @@ -115,9 +124,9 @@ urllib3==1.26.6 # via # kubernetes # requests -websocket-client==1.1.0 +websocket-client==1.2.0 # via kubernetes -wheel==0.36.2 +wheel==0.37.0 # via -r requirements.in # The following packages are considered to be unsafe in a requirements file: