From 0127749909d27d6b25223b0bf1987026f03c6a3d Mon Sep 17 00:00:00 2001 From: Daniel Berteaud Date: Wed, 15 Nov 2023 22:44:50 +0100 Subject: [PATCH] BounCA bundle --- bounca.nomad.hcl | 151 ++++++++++++++++++ bundles.yml | 4 + consul/config/service-defaults/bounca.hcl | 3 + consul/config/service-intentions/bounca.hcl | 16 ++ images/bounca/Dockerfile | 71 ++++++++ .../bounca/root/entrypoint.d/10-settings.sh | 9 ++ images/bounca/root/entrypoint.d/20-venv.env | 4 + images/bounca/root/entrypoint.d/30-migrate.sh | 24 +++ images/bounca/root/etc/supervisord.conf | 8 + .../etc/supervisord.d/bounca-gunicorn.ini | 10 ++ .../root/etc/supervisord.d/bounca-nginx.ini | 10 ++ .../etc/supervisord.d/bounca-pub-export.ini | 10 ++ .../bounca/root/etc/supervisord.d/bounca.ini | 2 + .../root/opt/bounca/bounca/docker_settings.py | 34 ++++ .../bounca/etc/bounca/services.yaml.template | 41 +++++ .../opt/bounca/etc/nginx/nginx.conf.template | 64 ++++++++ .../root/usr/local/bin/bounca-pub-export | 37 +++++ init/vault-bounca | 8 + prep.d/10-mv-conf.sh | 1 + prep.d/10-rand-pwd.sh | 17 ++ templates/docker_settings.py.tpl | 36 +++++ variables.yml | 41 +++++ vault/policies/bounca.hcl | 7 + 23 files changed, 608 insertions(+) create mode 100644 bounca.nomad.hcl create mode 100644 bundles.yml create mode 100644 consul/config/service-defaults/bounca.hcl create mode 100644 consul/config/service-intentions/bounca.hcl create mode 100644 images/bounca/Dockerfile create mode 100755 images/bounca/root/entrypoint.d/10-settings.sh create mode 100644 images/bounca/root/entrypoint.d/20-venv.env create mode 100755 images/bounca/root/entrypoint.d/30-migrate.sh create mode 100644 images/bounca/root/etc/supervisord.conf create mode 100644 images/bounca/root/etc/supervisord.d/bounca-gunicorn.ini create mode 100644 images/bounca/root/etc/supervisord.d/bounca-nginx.ini create mode 100644 images/bounca/root/etc/supervisord.d/bounca-pub-export.ini create mode 100644 images/bounca/root/etc/supervisord.d/bounca.ini create mode 100644 images/bounca/root/opt/bounca/bounca/docker_settings.py create mode 100644 images/bounca/root/opt/bounca/etc/bounca/services.yaml.template create mode 100644 images/bounca/root/opt/bounca/etc/nginx/nginx.conf.template create mode 100755 images/bounca/root/usr/local/bin/bounca-pub-export create mode 100755 init/vault-bounca create mode 100755 prep.d/10-mv-conf.sh create mode 100755 prep.d/10-rand-pwd.sh create mode 100644 templates/docker_settings.py.tpl create mode 100644 variables.yml create mode 100644 vault/policies/bounca.hcl diff --git a/bounca.nomad.hcl b/bounca.nomad.hcl new file mode 100644 index 0000000..9dcbe6c --- /dev/null +++ b/bounca.nomad.hcl @@ -0,0 +1,151 @@ +job [[ .bounca.instance | toJSON ]] { + +[[- $c := merge .bounca . ]] + +[[ template "common/job_start.tpl" $c ]] + + group "bounca" { + network { + mode = "bridge" + } + + service { + name = "[[ .bounca.instance ]][[ .consul.suffix ]]" + port = 8749 + +[[ template "common/connect.tpl" $c ]] + + tags = [ + "[[ $c.traefik.instance ]].enable=[[ if $c.traefik.enabled ]]true[[ else ]]false[[ end ]]", + "[[ $c.traefik.instance ]].http.routers.[[ .bounca.instance ]][[ $c.consul.suffix ]].rule=Host(`[[ (urlParse .bounca.public_url).Hostname ]]`) + [[- if not (regexp.Match "^/?$" (urlParse .bounca.public_url).Path) ]] && PathPrefix(`[[ (urlParse .bounca.public_url).Path ]]`)[[ end ]]", + "[[ $c.traefik.instance ]].http.routers.[[ .bounca.instance ]][[ $c.consul.suffix ]].entrypoints=[[ join $c.traefik.entrypoints "," ]]", +[[- if not (regexp.Match "^/?$" (urlParse .bounca.public_url).Path) ]] + "[[ $c.traefik.instance ]].http.middlewares.[[ .bounca.instance ]][[ $c.consul.suffix ]]-prefix.stripprefix.prefixes=[[ (urlParse .bounca.public_url).Path ]]", + "[[ $c.traefik.instance ]].http.routers.[[ .bounca.instance ]][[ $c.consul.suffix ]].middlewares=[[ .bounca.instance ]][[ $c.consul.suffix ]]-prefix,[[ template "common/traefik_middlewares.tpl" $c.traefik ]]", +[[- else ]] + "[[ $c.traefik.instance ]].http.routers.[[ .bounca.instance ]][[ $c.consul.suffix ]].middlewares=[[ template "common/traefik_middlewares.tpl" $c.traefik ]]", +[[- end ]] + ] + + + } + + task "bounca" { + driver = [[ $c.nomad.driver | toJSON ]] + user = 8749 + + config { + image = [[ $c.image | toJSON ]] + pids_limit = 50 + readonly_rootfs = true + command = "gunicorn" + args = [ + "--bind=unix:/alloc/data/bounca.sock", + "--threads=4", + "--max-requests=10000", + "bounca.wsgi:application" + ] +[[ template "common/tmpfs.tpl" dict "target" "/tmp" "size" 1000000 ]] + volumes = ["local/docker_settings.py:/opt/bounca/bounca/docker_settings.py:ro"] + } + + vault { + policies = ["[[ .bounca.instance ]][[ .consul.suffix ]]"] + disable_file = true + env = false + } + + env { + BOUNCA_UNIX_SOCKET = "/alloc/data/bounca.sock" + BOUNCA_HOST = "[[ (urlParse .bounca.public_url).Hostname ]]" + } + +[[ template "common/file_env.tpl" $c.env ]] + + template { + data =<<_EOT +[[ template "bounca/docker_settings.py.tpl" $c ]] +_EOT + destination = "local/docker_settings.py" + } + +[[ template "common/resources.tpl" $c.resources ]] + } + + task "public-exporter" { + driver = [[ $c.nomad.driver | toJSON ]] + user = 8749 + + lifecycle { + hook = "poststart" + sidecar = true + } + + config { + image = [[ $c.image | toJSON ]] + pids_limit = 50 + readonly_rootfs = true + command = "bounca-pub-export" + args = [ + "300", + ] +[[ template "common/tmpfs.tpl" dict "target" "/tmp" "size" 1000000 ]] + } + + vault { + policies = ["[[ .bounca.instance ]][[ .consul.suffix ]]"] + disable_file = true + env = false + } + + env { + BOUNCA_MIGRATE = "false" + BOUNCA_PUBLIC_DIR = "/alloc/data/public" + } + +[[ template "common/file_env.tpl" $c.env ]] + + resources { + cpu = 10 + memory = 10 + memory_max = 20 + } + } + + task "nginx" { + driver = [[ $c.nomad.driver | toJSON ]] + user = 8749 + + lifecycle { + hook = "poststart" + sidecar = true + } + + config { + image = [[ $c.image | toJSON ]] + pids_limit = 30 + readonly_rootfs = true + command = "nginx" + args = [ + "-c", + "/tmp/nginx.conf" + ] +[[ template "common/tmpfs.tpl" dict "target" "/tmp" "size" 1000000 ]] + } + + env { + BOUNCA_BIND_ADDR = "127.0.0.1:8749" + BOUNCA_MIGRATE = "false" + BOUNCA_UNIX_SOCKET = "/alloc/data/bounca.sock" + BOUNCA_PUBLIC_DIR = "/alloc/data/public" + BOUNCA_HOST = "[[ (urlParse .bounca.public_url).Hostname ]]" + } + + resources { + cpu = 20 + memory = 20 + } + } + } +} diff --git a/bundles.yml b/bundles.yml new file mode 100644 index 0000000..5b9120e --- /dev/null +++ b/bundles.yml @@ -0,0 +1,4 @@ +--- + +dependencies: + - url: ../common.git diff --git a/consul/config/service-defaults/bounca.hcl b/consul/config/service-defaults/bounca.hcl new file mode 100644 index 0000000..c229bed --- /dev/null +++ b/consul/config/service-defaults/bounca.hcl @@ -0,0 +1,3 @@ +Kind = "service-defaults" +Name = "[[ .bounca.instance ]][[ .consul.suffix ]]" +Protocol = "http" diff --git a/consul/config/service-intentions/bounca.hcl b/consul/config/service-intentions/bounca.hcl new file mode 100644 index 0000000..5cea717 --- /dev/null +++ b/consul/config/service-intentions/bounca.hcl @@ -0,0 +1,16 @@ +Kind = "service-intentions" +Name = "[[ .bounca.instance ]][[ .consul.suffix ]]" +Sources = [ + { + Name = "[[ .traefik.instance ]]" + Permissions = [ + { + Action = "allow" + HTTP { + PathPrefix = "/" + Methods = ["GET", "HEAD", "POST", "OPTIONS", "PUT", "DELETE", "PATCH"] + } + } + ] + } +] diff --git a/images/bounca/Dockerfile b/images/bounca/Dockerfile new file mode 100644 index 0000000..8ad68e0 --- /dev/null +++ b/images/bounca/Dockerfile @@ -0,0 +1,71 @@ +# syntax=docker/dockerfile:labs + +FROM python:3.11-alpine AS builder + +ARG BOUNCA_VERSION=0.4.4 + +workdir /opt +RUN set -euxo pipefail &&\ + apk --no-cache add \ + curl \ + ca-certificates \ + rdfind \ + &&\ + curl -sSLo bounca.tar.gz https://github.com/repleo/bounca/releases/download/v${BOUNCA_VERSION}/bounca.tar.gz &&\ + python3 -m venv venv &&\ + source /opt/venv/bin/activate &&\ + tar xvzf bounca.tar.gz &&\ + rm -f bounca.tar.gz &&\ + cd bounca &&\ + pip --no-cache-dir install -r requirements.txt &&\ + pip --no-cache-dir install \ + gunicorn \ + &&\ + mkdir -p /var/log/bounca/ &&\ + cp /opt/bounca/etc/bounca/services.yaml.example /opt/bounca/etc/bounca/services.yaml &&\ + ./manage.py collectstatic &&\ + rm -f /opt/bounca/etc/bounca/services.yaml &&\ + sed -i -E 's|/etc/bounca/services.yaml|/tmp/services.yaml|g' /opt/bounca/bounca/settings.py &&\ + rdfind /opt + +FROM python:3.11-alpine +MAINTAINER [[ .docker.maintainer ]] + +ENV PATH=/opt/venv/bin:${PATH} \ + BOUNCA_MIGRATE=true \ + BOUNCA_UNIX_SOCKET=/tmp/bounca.sock \ + BOUNCA_HOST='*' \ + DJANGO_SETTINGS_MODULE=bounca.docker_settings \ + BOUNCA_BIND_ADDR=0.0.0.0:8749 \ + BOUNCA_DB_NAME=bounca \ + BOUNCA_DB_USER=bounca \ + BOUNCA_DB_PASSWORD=bounca \ + BOUNCA_DB_HOST=localhost \ + BOUNCA_DB_PORT=5432 \ + BOUNCA_DJANGO_SECRET=changeme \ + BOUNCA_SMTP_SERVER=127.0.0.1 \ + BOUNCA_SMTP_PORT=25 \ + BOUNCA_ADMIN_EMAIL=admin@localhost \ + BOUNCA_FROM_EMAIL=no-reply@localhost \ + BOUNCA_ADMIN_USER=admin \ + BOUNCA_ADMIN_PASSWORD=password \ + BOUNCA_PUBLIC_DIR=/tmp/public + +ADD https://git.lapiole.org/nomad/base_tools.git#master / +COPY --from=builder /opt /opt + +RUN set -euxo pipefail &&\ + apk --no-cache add \ + tini \ + gettext \ + openssl \ + nginx \ + supervisor \ + postgresql15-client + +COPY root/ / + +WORKDIR /opt/bounca +EXPOSE 8749 +ENTRYPOINT ["tini", "--", "/entrypoint.sh"] +CMD ["supervisord", "-n", "-c", "/etc/supervisord.conf"] diff --git a/images/bounca/root/entrypoint.d/10-settings.sh b/images/bounca/root/entrypoint.d/10-settings.sh new file mode 100755 index 0000000..c4a692d --- /dev/null +++ b/images/bounca/root/entrypoint.d/10-settings.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -euo pipefail + +echo "Populating /tmpservices.yaml from /opt/bounca/etc/bounca/services.yaml.template" +envsubst < /opt/bounca/etc/bounca/services.yaml.template > /tmp/services.yaml +chmod 600 /tmp/services.yaml +echo "Populating /tmp/nginx.conf from /opt/bounca/etc/nginx/nginx.conf.template" +envsubst < /opt/bounca/etc/nginx/nginx.conf.template > /tmp/nginx.conf diff --git a/images/bounca/root/entrypoint.d/20-venv.env b/images/bounca/root/entrypoint.d/20-venv.env new file mode 100644 index 0000000..a493355 --- /dev/null +++ b/images/bounca/root/entrypoint.d/20-venv.env @@ -0,0 +1,4 @@ +#!/bin/sh + +set -euo pipefail +source /opt/venv/bin/activate diff --git a/images/bounca/root/entrypoint.d/30-migrate.sh b/images/bounca/root/entrypoint.d/30-migrate.sh new file mode 100755 index 0000000..38b201c --- /dev/null +++ b/images/bounca/root/entrypoint.d/30-migrate.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +set -euo pipefail + +if [ "${BOUNCA_MIGRATE}" = "false" ]; then + echo "Not running migration" + exit 0 +fi + +echo "Migrating database" +cd /opt/bounca +./manage.py migrate + +if [ "${BOUNCA_HOST}" != "*" ]; then + echo "Configure site URL" + ./manage.py site ${BOUNCA_HOST} +fi + +if [ -n "${BOUNCA_ADMIN_USER}" -a -n "${BOUNCA_ADMIN_PASSWORD}" ]; then + echo "Creating admin user ${BOUNCA_ADMIN_USER}" + export DJANGO_SUPERUSER_PASSWORD="${BOUNCA_ADMIN_PASSWORD}" + ./manage.py createsuperuser --noinput --username ${BOUNCA_ADMIN_USER} --email ${BOUNCA_ADMIN_EMAIL} ||\ + echo "Failed to create user ${BOUNCA_ADMIN_USER} (maybe it already exists ?)" +fi diff --git a/images/bounca/root/etc/supervisord.conf b/images/bounca/root/etc/supervisord.conf new file mode 100644 index 0000000..c94ef46 --- /dev/null +++ b/images/bounca/root/etc/supervisord.conf @@ -0,0 +1,8 @@ +[supervisord] +pidfile=/tmp/supervisord.pi +nodaemon=true +logfile=/dev/stdout +logfile_maxbytes=0 + +[include] +files = supervisord.d/*.ini diff --git a/images/bounca/root/etc/supervisord.d/bounca-gunicorn.ini b/images/bounca/root/etc/supervisord.d/bounca-gunicorn.ini new file mode 100644 index 0000000..1068d17 --- /dev/null +++ b/images/bounca/root/etc/supervisord.d/bounca-gunicorn.ini @@ -0,0 +1,10 @@ +[program:gunicorn] +command=sh -c "source /opt/venv/bin/activate && gunicorn --bind=unix:${BOUNCA_UNIX_SOCKET} --threads=4 --max-requests=10000 bounca.wsgi:application" +stdout_logfile=/proc/self/fd/1 +stdout_logfile_backups=0 +stdout_logfile_maxbytes=0 +stderr_logfile=/proc/self/fd/2 +stderr_logfile_backups=0 +stderr_logfile_maxbytes=0 +autostart=true +autorestart=true diff --git a/images/bounca/root/etc/supervisord.d/bounca-nginx.ini b/images/bounca/root/etc/supervisord.d/bounca-nginx.ini new file mode 100644 index 0000000..b13d198 --- /dev/null +++ b/images/bounca/root/etc/supervisord.d/bounca-nginx.ini @@ -0,0 +1,10 @@ +[program:nginx] +command=/usr/sbin/nginx -c /tmp/nginx.conf +stdout_logfile=/proc/self/fd/1 +stdout_logfile_backups=0 +stdout_logfile_maxbytes=0 +stderr_logfile=/proc/self/fd/2 +stderr_logfile_backups=0 +stderr_logfile_maxbytes=0 +autostart=true +autorestart=true diff --git a/images/bounca/root/etc/supervisord.d/bounca-pub-export.ini b/images/bounca/root/etc/supervisord.d/bounca-pub-export.ini new file mode 100644 index 0000000..98fd8ab --- /dev/null +++ b/images/bounca/root/etc/supervisord.d/bounca-pub-export.ini @@ -0,0 +1,10 @@ +[program:pub-export] +command=/usr/local/bin/bounca-pub-export 300 +stdout_logfile=/proc/self/fd/1 +stdout_logfile_backups=0 +stdout_logfile_maxbytes=0 +stderr_logfile=/proc/self/fd/2 +stderr_logfile_backups=0 +stderr_logfile_maxbytes=0 +autostart=true +autorestart=true diff --git a/images/bounca/root/etc/supervisord.d/bounca.ini b/images/bounca/root/etc/supervisord.d/bounca.ini new file mode 100644 index 0000000..da3725a --- /dev/null +++ b/images/bounca/root/etc/supervisord.d/bounca.ini @@ -0,0 +1,2 @@ +[group:bounca] +programs=gunicorn,nginx,pub-export diff --git a/images/bounca/root/opt/bounca/bounca/docker_settings.py b/images/bounca/root/opt/bounca/bounca/docker_settings.py new file mode 100644 index 0000000..9fad921 --- /dev/null +++ b/images/bounca/root/opt/bounca/bounca/docker_settings.py @@ -0,0 +1,34 @@ +from bounca.settings import * + +LOGGING: dict = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "verbose": { + "format": "%(levelname)s [%(asctime)s] %(name)s %(message)s", + }, + "simple": {"format": "[%(asctime)s] %(message)s"}, + }, + "handlers": { + "null": { + "class": "logging.NullHandler", + }, + "console": { + "class": "logging.StreamHandler", + "formatter": "simple", + }, + "mail_admins": {"level": "ERROR", "class": "django.utils.log.AdminEmailHandler"}, + }, + "root": { + "level": "DEBUG", + "handlers": ["console"], + }, + "loggers": {}, +} + +TIME_ZONE = os.environ.get('TZ') + +CSRF_TRUSTED_ORIGINS = [ + "http://localhost:%d" % (os.environ.get('BOUNCA_BIND_ADDR').split(':'))[1], + "https://%s" % os.environ.get('BOUNCA_HOST') +] diff --git a/images/bounca/root/opt/bounca/etc/bounca/services.yaml.template b/images/bounca/root/opt/bounca/etc/bounca/services.yaml.template new file mode 100644 index 0000000..08dda73 --- /dev/null +++ b/images/bounca/root/opt/bounca/etc/bounca/services.yaml.template @@ -0,0 +1,41 @@ +psql: + dbname: ${BOUNCA_DB_NAME} + username: ${BOUNCA_DB_USER} + password: ${BOUNCA_DB_PASSWORD} + host: ${BOUNCA_DB_HOST} + port: ${BOUNCA_DB_PORT} + +admin: + enabled: True + superuser_signup: False + +django: + debug: False + secret_key: '${BOUNCA_DJANGO_SECRET}' + hosts: + # add your hosts here + - localhost + - 127.0.0.1 + - bounca + - ${BOUNCA_HOST} + +mail: + host: ${BOUNCA_SMTP_SERVER} + port: ${BOUNCA_SMTP_PORT} + # port: 587 optionally, only for tls and ssl + # username: + # password: + # connection: none # allowed values: none, tls, ssl + admin: ${BOUNCA_ADMIN_EMAIL} + from: ${BOUNCA_FROM_EMAIL} + +certificate-engine: + # allowed values: ed25519, rsa + # Ed25519 is a a modern, fast and safe key algorithm, however not supported by all operating systems, like MacOS. + # Keep the 'rsa' option if unsure. Root and intermediate keys are 4096 bits, client and server certificates + # use 2048 bits keys. + key_algorithm: rsa + +registration: + # allowed values: mandatory, optional, off + email_verification: off diff --git a/images/bounca/root/opt/bounca/etc/nginx/nginx.conf.template b/images/bounca/root/opt/bounca/etc/nginx/nginx.conf.template new file mode 100644 index 0000000..1e1bc61 --- /dev/null +++ b/images/bounca/root/opt/bounca/etc/nginx/nginx.conf.template @@ -0,0 +1,64 @@ +worker_processes auto; +error_log /dev/stderr warn; +pid /tmp/nginx.pid; +daemon off; + +events { + worker_connections 1024; +} + +http { + + proxy_temp_path /tmp/proxy_temp; + client_body_temp_path /tmp/client_temp; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + include /etc/nginx/mime.types; + default_type application/octet-stream; + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /dev/stdout main; + sendfile on; + keepalive_timeout 65; + + set_real_ip_from 127.0.0.1; + real_ip_header X-Forwarded-For; + real_ip_recursive on; + + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + upstream bounca { + server unix:${BOUNCA_UNIX_SOCKET}; + } + + server { + listen ${BOUNCA_BIND_ADDR} default_server; + server_tokens off; + + location /static { + root /opt/bounca/media; + } + location /public { + alias ${BOUNCA_PUBLIC_DIR}; + } + location ~ ^/(api|admin) { + proxy_pass http://bounca; + } + location / { + root /opt/bounca/front/dist; + try_files $uri $uri/ /index.html; + } + } +} diff --git a/images/bounca/root/usr/local/bin/bounca-pub-export b/images/bounca/root/usr/local/bin/bounca-pub-export new file mode 100755 index 0000000..e276cee --- /dev/null +++ b/images/bounca/root/usr/local/bin/bounca-pub-export @@ -0,0 +1,37 @@ +#!/bin/sh + +set -euo pipefail + +export PGHOST=${BOUNCA_DB_HOST} +export PGPORT=${BOUNCA_DB_PORT} +export PGUSER=${BOUNCA_DB_USER} +export PGPASSWORD=${BOUNCA_DB_PASSWORD} +export PGDATABASE=${BOUNCA_DB_NAME} + +mkdir -p ${BOUNCA_PUBLIC_DIR} + +extract_pub(){ + echo "Exporting public keys and CRL" + for CERT_ID in $(psql -A -q -t -c "SELECT id FROM x509_pki_certificate WHERE type IN ('R', 'I') AND revoked_at IS NULL;"); do + CERT_NAME=$(psql -A -q -t -c "SELECT name FROM x509_pki_certificate WHERE id='${CERT_ID}'") + echo "Exporting for certificate ${CERT_ID} (${CERT_NAME})" + psql -A -q -t -c "SELECT crt FROM x509_pki_keystore WHERE id='${CERT_ID}';" > ${BOUNCA_PUBLIC_DIR}/${CERT_ID}.crt + ln -sf ${BOUNCA_PUBLIC_DIR}/${CERT_ID}.crt "${BOUNCA_PUBLIC_DIR}/${CERT_NAME}.crt" + if [ "$(psql -A -q -t -c "SELECT COUNT(crl) from x509_pki_crlstore WHERE id='${CERT_ID}'")" != "0" ]; then + psql -A -q -t -c "SELECT crl FROM x509_pki_crlstore WHERE id='${CERT_ID}';" > ${BOUNCA_PUBLIC_DIR}/${CERT_ID}.crl + ln -sf ${BOUNCA_PUBLIC_DIR}/${CERT_ID}.crl ${BOUNCA_PUBLIC_DIR}/${CERT_NAME}.crl + fi + done +} + +# Extract once when we start +extract_pub + +# First arg of the script is an optional delay between exports. +# If set, the script keeps running and export certs and crl every X seconds +if [ ${1:-0} -gt 0 ]; then + while true; do + sleep ${1} + extract_pub; + done +fi diff --git a/init/vault-bounca b/init/vault-bounca new file mode 100755 index 0000000..b5aee11 --- /dev/null +++ b/init/vault-bounca @@ -0,0 +1,8 @@ +#!/bin/sh + +set -euo pipefail + +[[- template "common/vault.mkpgrole.sh.tpl" + dict "ctx" . + "config" (dict "role" .bounca.instance "database" "postgres") +]] diff --git a/prep.d/10-mv-conf.sh b/prep.d/10-mv-conf.sh new file mode 100755 index 0000000..ae09a94 --- /dev/null +++ b/prep.d/10-mv-conf.sh @@ -0,0 +1 @@ +[[ template "common/mv_conf.sh.tpl" dict "ctx" . "services" (dict "bounca" .bounca.instance) ]] diff --git a/prep.d/10-rand-pwd.sh b/prep.d/10-rand-pwd.sh new file mode 100755 index 0000000..e25f494 --- /dev/null +++ b/prep.d/10-rand-pwd.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -euo pipefail + +# Initialize random passwords if needed + +if ! vault kv list [[ .vault.prefix ]]kv/service 2>/dev/null | grep -q -E '^[[ .bounca.instance ]]$'; then + vault kv put [[ .vault.prefix ]]kv/service/[[ .bounca.instance ]] \ + django_secret=$(pwgen -s -n 50 1) +fi + +for PWD in django_secret; do + if ! vault kv get -field ${PWD} [[ .vault.prefix ]]kv/service/[[ .bounca.instance ]] >/dev/null 2>&1; then + vault kv patch [[ .vault.prefix ]]kv/service/[[ .bounca.instance ]] \ + ${PWD}=$(pwgen -s -n 50 1) + fi +done diff --git a/templates/docker_settings.py.tpl b/templates/docker_settings.py.tpl new file mode 100644 index 0000000..088ba76 --- /dev/null +++ b/templates/docker_settings.py.tpl @@ -0,0 +1,36 @@ +from bounca.settings import * + +LOGGING: dict = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "verbose": { + "format": "%(levelname)s [%(asctime)s] %(name)s %(message)s", + }, + "simple": {"format": "[%(asctime)s] %(message)s"}, + }, + "handlers": { + "null": { + "class": "logging.NullHandler", + }, + "console": { + "class": "logging.StreamHandler", + "formatter": "simple", + }, + "mail_admins": {"level": "ERROR", "class": "django.utils.log.AdminEmailHandler"}, + }, + "root": { + "level": "DEBUG", + "handlers": ["console"], + }, + "loggers": {}, +} + +TIME_ZONE = os.environ.get('TZ') + +CSRF_TRUSTED_ORIGINS = [ + "[[ .bounca.public_url ]]" +] + + +[[ .bounca.django_custom_settings ]] diff --git a/variables.yml b/variables.yml new file mode 100644 index 0000000..a73d885 --- /dev/null +++ b/variables.yml @@ -0,0 +1,41 @@ +--- + +bounca: + + # Name of this instance (controls job and service name) + instance: bounca + + # The image to use + image: danielberteaud/bounca:0.4.4-1 + + # Env variable to pass to the container + env: + BOUNCA_DB_USER: '{{ with secret "[[ .vault.prefix ]]/database/creds/[[ .bounca.instance ]]" }}{{ .Data.username }}{{ end }}' + BOUNCA_DB_PASSWORD: '{{ with secret "[[ .vault.prefix ]]/database/creds/[[ .bounca.instance ]]" }}{{ .Data.password }}{{ end }}' + BOUNCA_DJANGO_SECRET: '{{ with secret "[[ .vault.prefix ]]/kv/service/[[ .bounca.instance ]]" }}{{ .Data.data.django_secret }}{{ end }}' + + # Public URL where user can reach the app + public_url: https://pki.example.org + + # Custom django settings + django_custom_settings: "" + + # Wait for postgres to be ready before starting + wait_for: + - service: master.postgres[[ .consul.suffix ]] + + # Connect to the postgres service through the service mesh] + consul: + connect: + upstreams: + - destination_name: postgres[[ .consul.suffix ]] + local_bind_port: 5432 + + # Traefik settings + traefik: + enabled: true + + # Resource allocation for the main bounca task + resources: + cpu: 200 + memory: 192 diff --git a/vault/policies/bounca.hcl b/vault/policies/bounca.hcl new file mode 100644 index 0000000..a05b90b --- /dev/null +++ b/vault/policies/bounca.hcl @@ -0,0 +1,7 @@ +path "[[ .vault.prefix ]]kv/data/service/[[ .bounca.instance ]]" { + capabilities = ["read"] +} + +path "[[ .vault.prefix ]]database/creds/[[ .bounca.instance ]]" { + capabilities = ["read"] +}