diff --git a/.gitignore b/.gitignore index c17a3a9fa8..76d0e9555f 100644 --- a/.gitignore +++ b/.gitignore @@ -58,9 +58,6 @@ mongo_database # User Guide docs/user_guide/site -# Vagrant instances -.vagrant/ - # Python .tox .mypy_cache diff --git a/deploy/helm/cert-proxy-client/templates/env-cert-proxy-configmap.yaml b/deploy/helm/cert-proxy-client/templates/env-cert-proxy-configmap.yaml index 332a697237..5af5034e35 100644 --- a/deploy/helm/cert-proxy-client/templates/env-cert-proxy-configmap.yaml +++ b/deploy/helm/cert-proxy-client/templates/env-cert-proxy-configmap.yaml @@ -6,7 +6,7 @@ metadata: namespace: {{ .Release.Namespace }} data: AWS_S3_BUCKET: {{ print "s3://" .Values.global.awsS3Location "/" .Values.awsS3CertLoc | quote }} - CERT_SECRET: {{ template "cert-proxy-client.certSecretName" . }} + CERT_SECRET: {{ include "cert-proxy-client.certSecretName" . }} CERT_NAMESPACE: {{ .Release.Namespace }} CERT_RENEW_BEFORE: "{{ .Values.certRenewBefore }}" TEST_URL: "https://aws.amazon.com" diff --git a/deploy/helm/cert-proxy-client/templates/update-cert-cronjob.yaml b/deploy/helm/cert-proxy-client/templates/update-cert-cronjob.yaml index 71b14d4e96..8b6d303673 100644 --- a/deploy/helm/cert-proxy-client/templates/update-cert-cronjob.yaml +++ b/deploy/helm/cert-proxy-client/templates/update-cert-cronjob.yaml @@ -17,7 +17,7 @@ spec: spec: serviceAccountName: {{ .Values.serviceAccount.name }} containers: - - image: {{ template "cert-proxy-client.containerImage" . }} + - image: {{ include "cert-proxy-client.containerImage" . }} imagePullPolicy: {{ .Values.global.imagePullPolicy }} name: update-cert-cronjob command: diff --git a/deploy/helm/cert-proxy-client/templates/update-cert-oneshot.yaml b/deploy/helm/cert-proxy-client/templates/update-cert-oneshot.yaml index c80738d084..5011eb043a 100644 --- a/deploy/helm/cert-proxy-client/templates/update-cert-oneshot.yaml +++ b/deploy/helm/cert-proxy-client/templates/update-cert-oneshot.yaml @@ -16,7 +16,7 @@ spec: spec: serviceAccountName: {{ .Values.serviceAccount.name }} containers: - - image: {{ template "cert-proxy-client.containerImage" . }} + - image: {{ include "cert-proxy-client.containerImage" . }} imagePullPolicy: {{ .Values.global.imagePullPolicy }} name: update-cert-oneshot command: diff --git a/deploy/helm/cert-proxy-client/values.yaml b/deploy/helm/cert-proxy-client/values.yaml index 8b08ec56c2..ea2e8e63de 100644 --- a/deploy/helm/cert-proxy-client/values.yaml +++ b/deploy/helm/cert-proxy-client/values.yaml @@ -19,9 +19,10 @@ global: awsDefaultRegion: "Override" awsAccessKeyId: "Override" awsSecretAccessKey: "Override" - imageTag: "latest" + imagePullPolicy: "IfNotPresent" # Define the image registry to use (may be blank for local images) imageRegistry: awsEcr + imageTag: "latest" # Default AWS S3 location awsS3Location: "thecombine.app" diff --git a/deploy/helm/cert-proxy-server/templates/_helpers.tpl b/deploy/helm/cert-proxy-server/templates/_helpers.tpl index e3609ab1a3..7f4fbf5510 100644 --- a/deploy/helm/cert-proxy-server/templates/_helpers.tpl +++ b/deploy/helm/cert-proxy-server/templates/_helpers.tpl @@ -10,3 +10,11 @@ {{- printf "%s:%s" .Values.imageName .Values.global.imageTag }} {{- end }} {{- end }} + +{{/* Build string of certificates for ConfigMap data */}} +{{- define "cert-proxy-server.cert-proxy-list-config-data" -}} + {{- $awsCertLoc := .Values.awsS3CertLoc }} + {{- range .Values.combineCertProxyList -}} + {{- printf "%s@%s/%s " .hostname .bucket $awsCertLoc -}} + {{- end }} +{{- end }} diff --git a/deploy/helm/cert-proxy-server/templates/deployment-cert-proxy-server.yaml b/deploy/helm/cert-proxy-server/templates/deployment-cert-proxy-server.yaml index 01dabdc5d3..bde548e960 100644 --- a/deploy/helm/cert-proxy-server/templates/deployment-cert-proxy-server.yaml +++ b/deploy/helm/cert-proxy-server/templates/deployment-cert-proxy-server.yaml @@ -60,7 +60,7 @@ spec: name: {{ .Values.envCertProxy }} - name: CERT_PROXY_NAMESPACE value: {{ .Release.Namespace }} - image: {{ template "cert-proxy-server.containerImage" . }} + image: {{ include "cert-proxy-server.containerImage" . }} imagePullPolicy: {{ .Values.global.imagePullPolicy }} name: combine-cert-proxy resources: diff --git a/deploy/helm/cert-proxy-server/templates/env-cert-proxy-configmap.yaml b/deploy/helm/cert-proxy-server/templates/env-cert-proxy-configmap.yaml index 89abc39792..6fdd0eacd7 100644 --- a/deploy/helm/cert-proxy-server/templates/env-cert-proxy-configmap.yaml +++ b/deploy/helm/cert-proxy-server/templates/env-cert-proxy-configmap.yaml @@ -6,4 +6,4 @@ metadata: namespace: {{ .Release.Namespace }} data: AWS_S3_BUCKET: {{ print "s3://" .Values.global.awsS3Location "/" .Values.awsS3CertLoc | quote }} - CERT_PROXY_CERTIFICATES: {{ .Values.combineCertProxyList | default "" | quote }} + CERT_PROXY_CERTIFICATES: {{ include "cert-proxy-server.cert-proxy-list-config-data" . | trim | default "" | quote }} diff --git a/deploy/helm/cert-proxy-server/templates/ingress-nuc.yaml b/deploy/helm/cert-proxy-server/templates/ingress-nuc.yaml index 334dbf7dae..a5afb60914 100644 --- a/deploy/helm/cert-proxy-server/templates/ingress-nuc.yaml +++ b/deploy/helm/cert-proxy-server/templates/ingress-nuc.yaml @@ -1,9 +1,9 @@ -{{- range regexSplit "\\s+" .Values.combineCertProxyList -1 }} +{{- range .Values.combineCertProxyList }} --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: {{ replace "." "-" . }} + name: {{ replace "." "-" .hostname }} namespace: {{ $.Release.Namespace }} annotations: {{- if eq $.Values.ingressClass "nginx" }} @@ -18,10 +18,10 @@ spec: ingressClassName: {{ $.Values.ingressClass }} tls: - hosts: - - {{ . }} - secretName: {{ replace "." "-" . }}-tls + - {{ .hostname }} + secretName: {{ replace "." "-" .hostname }}-tls rules: - - host: {{ . }} + - host: {{ .hostname }} http: paths: - backend: diff --git a/deploy/helm/cert-proxy-server/values.yaml b/deploy/helm/cert-proxy-server/values.yaml index bec82be53c..1069b2b921 100644 --- a/deploy/helm/cert-proxy-server/values.yaml +++ b/deploy/helm/cert-proxy-server/values.yaml @@ -43,6 +43,9 @@ awsEcr: role: role-ecr-login-cert-proxy roleBinding: role-ecr-login-cert-proxy-binding +combineCertProxyList: + - hostname: Override + bucket: Override envCertProxy: env-cert-proxy envNginxProxy: env-nginx-proxy nginxPages: init-nginx-pages diff --git a/deploy/helm/create-admin-user/templates/job-create-admin-user.yaml b/deploy/helm/create-admin-user/templates/job-create-admin-user.yaml index babf7b7021..0022f1df25 100644 --- a/deploy/helm/create-admin-user/templates/job-create-admin-user.yaml +++ b/deploy/helm/create-admin-user/templates/job-create-admin-user.yaml @@ -45,7 +45,7 @@ spec: secretKeyRef: key: COMBINE_ADMIN_EMAIL name: admin-user-secrets - image: {{ template "create-admin-user.containerImage" . }} + image: {{ include "create-admin-user.containerImage" . }} imagePullPolicy: {{ .Values.global.imagePullPolicy }} name: create-admin-user resources: {} diff --git a/deploy/helm/thecombine/charts/backend/templates/deployment-backend.yaml b/deploy/helm/thecombine/charts/backend/templates/deployment-backend.yaml index b3ccd09a21..49905e60fa 100644 --- a/deploy/helm/thecombine/charts/backend/templates/deployment-backend.yaml +++ b/deploy/helm/thecombine/charts/backend/templates/deployment-backend.yaml @@ -34,7 +34,7 @@ spec: imagePullPolicy: IfNotPresent containers: - name: backend - image: {{ template "backend.containerImage" . }} + image: {{ include "backend.containerImage" . }} imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: - name: COMBINE_JWT_SECRET_KEY diff --git a/deploy/helm/thecombine/charts/database/templates/database.yaml b/deploy/helm/thecombine/charts/database/templates/database.yaml index d71dca154c..7c30530888 100644 --- a/deploy/helm/thecombine/charts/database/templates/database.yaml +++ b/deploy/helm/thecombine/charts/database/templates/database.yaml @@ -43,7 +43,7 @@ spec: combine-component: database spec: containers: - - image: {{ template "database.containerImage" . }} + - image: {{ include "database.containerImage" . }} imagePullPolicy: {{ .Values.global.imagePullPolicy }} name: database ports: diff --git a/deploy/helm/thecombine/charts/frontend/templates/deployment-frontend.yaml b/deploy/helm/thecombine/charts/frontend/templates/deployment-frontend.yaml index ca75e1cae5..eaee461ae1 100644 --- a/deploy/helm/thecombine/charts/frontend/templates/deployment-frontend.yaml +++ b/deploy/helm/thecombine/charts/frontend/templates/deployment-frontend.yaml @@ -26,7 +26,7 @@ spec: spec: containers: - name: frontend - image: {{ template "frontend.containerImage" . }} + image: {{ include "frontend.containerImage" . }} imagePullPolicy: {{ .Values.global.imagePullPolicy }} env: - name: CERT_ADDL_DOMAINS diff --git a/deploy/helm/thecombine/charts/maintenance/templates/deployment-maintenance.yaml b/deploy/helm/thecombine/charts/maintenance/templates/deployment-maintenance.yaml index a80c5c220d..ce8d958fa8 100644 --- a/deploy/helm/thecombine/charts/maintenance/templates/deployment-maintenance.yaml +++ b/deploy/helm/thecombine/charts/maintenance/templates/deployment-maintenance.yaml @@ -29,7 +29,7 @@ spec: - name: maintenance command: [ "/bin/bash", "-c", "--" ] args: [ 'sleep infinity & PID=$! ; trap "kill $PID" INT TERM ; wait' ] - image: {{ template "maintenance.containerImage" . }} + image: {{ include "maintenance.containerImage" . }} imagePullPolicy: {{ .Values.global.imagePullPolicy }} securityContext: runAsUser: 999 diff --git a/deploy/helm/thecombine/charts/maintenance/templates/env-maintenance-configmap.yaml b/deploy/helm/thecombine/charts/maintenance/templates/env-maintenance-configmap.yaml index c8bcf0e530..6c8bcd979f 100644 --- a/deploy/helm/thecombine/charts/maintenance/templates/env-maintenance-configmap.yaml +++ b/deploy/helm/thecombine/charts/maintenance/templates/env-maintenance-configmap.yaml @@ -14,7 +14,7 @@ data: # for this host from the list of backups. This is done as an environment variable # to provide flexibility for future clean schemes while minimizing the need to # rebuild the container image. - backup_filter: {{ template "maintenance.backupNameFilter" . }} + backup_filter: {{ include "maintenance.backupNameFilter" . }} wait_time: {{ .Values.waitTime | quote }} max_backups: {{ .Values.maxBackups | quote }} font_dir: {{ .Values.fontsDir | quote }} diff --git a/deploy/scripts/setup_files/profiles/prod.yaml b/deploy/scripts/setup_files/profiles/prod.yaml index d8e2968962..2e45809325 100644 --- a/deploy/scripts/setup_files/profiles/prod.yaml +++ b/deploy/scripts/setup_files/profiles/prod.yaml @@ -41,4 +41,12 @@ charts: global: awsS3Location: prod.thecombine.app pullSecretName: None - combineCertProxyList: nuc1.thecombine.app nuc2.thecombine.app nuc3.thecombine.app local.thecombine.app + combineCertProxyList: + - hostname: nuc1.thecombine.app + bucket: prod.thecombine.app + - hostname: nuc2.thecombine.app + bucket: prod.thecombine.app + - hostname: nuc3.thecombine.app + bucket: prod.thecombine.app + - hostname: local.thecombine.app + bucket: local.thecombine.app diff --git a/maintenance/scripts/monitor.py b/maintenance/scripts/monitor.py index 0bbf116835..8e82c8396d 100755 --- a/maintenance/scripts/monitor.py +++ b/maintenance/scripts/monitor.py @@ -4,19 +4,48 @@ monitor.py will monitor the secrets specified in the CERT_PROXY_CERTIFICATES environment variable. When a secret is updated, the updated secret is pushed -to the AWS_S3_BUCKET. +to the specified AWS S3 bucket. """ import base64 import os from pathlib import Path import tempfile -from typing import List +from typing import Dict from aws_backup import AwsBackup from kubernetes import client, config, watch +class TlsSecret: + def __init__(self, cert_spec: str) -> None: + (self.hostname, aws_bucket) = cert_spec.split("@") + self.aws = AwsBackup(bucket=aws_bucket) + self.name = f"{self.hostname.replace('.', '-')}-tls" + + def push(self, data: bytes, filename: str) -> None: + """ + Push single file associated with the TLS secret to AWS S3 storage. + + Args: + data: contents of the file to be pushed. + filename: name of the file, e.g. cert.pem, key.pem. + + Example: + a_secret.push(data=b'', filename="cert.pem") + will create a file that contains the data received in the AWS + S3 bucket under my-cert-tls/cert.pem. + """ + secret_dest = f"{self.name}/{filename}" + print(f"Push {secret_dest} to AWS {self.aws.bucket}", flush=True) + with tempfile.TemporaryDirectory() as temp_dir: + secret_dir = Path(temp_dir) / self.name + secret_dir.mkdir(parents=True, exist_ok=True) + secret_filename = secret_dir / filename + secret_filename.write_bytes(data) + self.aws.push(secret_filename, secret_dest) + + class CertMonitor: """ Monitor SSL certificate secrets and push a copy to the cloud on change. @@ -37,12 +66,11 @@ class CertMonitor: trigger_events = ("ADDED", "MODIFIED") def __init__(self) -> None: - self.secrets_list = CertMonitor.get_secrets_list() + self.secrets_dict = CertMonitor.get_secrets_dict() self.k8s_namespace = os.environ["CERT_PROXY_NAMESPACE"] - self.aws = AwsBackup(bucket=os.environ["AWS_S3_BUCKET"]) @staticmethod - def get_secrets_list() -> List[str]: + def get_secrets_dict() -> Dict[str, TlsSecret]: """ Create the list of secrets to monitor. @@ -50,40 +78,12 @@ def get_secrets_list() -> List[str]: environment variable. This function assumes that the secret name is '-tls' appended to the dns name of the server (with '.' converted to '-'). """ + secret_dict: Dict[str, TlsSecret] = {} if "CERT_PROXY_CERTIFICATES" in os.environ: - domain_list = os.environ["CERT_PROXY_CERTIFICATES"].replace(".", "-").split() - for index, item in enumerate(domain_list): - domain_list[index] = f"{item}-tls" - return domain_list - return [] - - def push_secret_file(self, *, secret_name: str, data: bytes, filename: str) -> None: - """ - Push single file associated with the TLS secret to AWS S3 storage. - - Args: - secret_name: name of the secret that is being pushed to AWS S3. - - data: contents of the file to be pushed. - - filename: name of the file. - - Example: - push_secret_file( - secret_name="my-cert-tls", - data=b'', - filename="cert.pem") - will create a file that contains the data received in the AWS - S3 bucket under my-cert-tls/cert.pem. - """ - secret_dest = f"{secret_name}/{filename}" - print(f"Push {secret_dest} to AWS S3", flush=True) - with tempfile.TemporaryDirectory() as temp_dir: - secret_dir = Path(temp_dir) / secret_name - secret_dir.mkdir(parents=True, exist_ok=True) - secret_filename = secret_dir / filename - secret_filename.write_bytes(data) - self.aws.push(secret_filename, f"{secret_dest}") + for item in os.environ["CERT_PROXY_CERTIFICATES"].split(): + secret = TlsSecret(item) + secret_dict[secret.name] = secret + return secret_dict def monitor(self) -> None: """Monitor for updates to the secrets.""" @@ -99,14 +99,12 @@ def monitor(self) -> None: secret_name = event["object"].metadata.name event_type = event["type"] print(f"Event: {event_type} {secret_name}", flush=True) - if (event_type in CertMonitor.trigger_events) and (secret_name in self.secrets_list): - self.push_secret_file( - secret_name=secret_name, + if (event_type in CertMonitor.trigger_events) and (secret_name in self.secrets_dict): + self.secrets_dict[secret_name].push( data=base64.b64decode(event["object"].data["tls.key"]), filename="key.pem", ) - self.push_secret_file( - secret_name=secret_name, + self.secrets_dict[secret_name].push( data=base64.b64decode(event["object"].data["tls.crt"]), filename="cert.pem", )