From 4d02165f1f1910cc179d60f7708bf2952a607f25 Mon Sep 17 00:00:00 2001 From: Adrian Richter Date: Mon, 27 Sep 2021 13:17:41 +0200 Subject: [PATCH] add auto https support + manage router by header switch from caddy file to native json --- docker/docker-compose.dev.yml | 2 - proxy/Caddyfile | 16 --- proxy/Caddyfile.dev | 16 --- proxy/Dockerfile | 6 +- proxy/Dockerfile.dev | 10 +- proxy/README.md | 25 +++++ proxy/caddy_base.json | 194 ++++++++++++++++++++++++++++++++++ proxy/entrypoint | 56 ++++++++-- 8 files changed, 279 insertions(+), 46 deletions(-) delete mode 100644 proxy/Caddyfile delete mode 100644 proxy/Caddyfile.dev create mode 100644 proxy/README.md create mode 100644 proxy/caddy_base.json diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 739b30a28..65a034f23 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -135,5 +135,3 @@ services: - autoupdate ports: - "8000:8000" - volumes: - - ../proxy/Caddyfile.dev:/etc/caddy/Caddyfile diff --git a/proxy/Caddyfile b/proxy/Caddyfile deleted file mode 100644 index 0fe36e60d..000000000 --- a/proxy/Caddyfile +++ /dev/null @@ -1,16 +0,0 @@ -import endpoint - - reverse_proxy /system/action* backend:9002 - reverse_proxy /system/presenter* backend:9003 - reverse_proxy /system/autoupdate* autoupdate:9012 { - flush_interval -1 - } - reverse_proxy /system/icc* icc:9013 { - flush_interval -1 - } - reverse_proxy /system/auth* auth:9004 - reverse_proxy /system/media* media:9006 - - reverse_proxy client:9001 - -} diff --git a/proxy/Caddyfile.dev b/proxy/Caddyfile.dev deleted file mode 100644 index c049d9e51..000000000 --- a/proxy/Caddyfile.dev +++ /dev/null @@ -1,16 +0,0 @@ -https://:8000 { - tls /certs/cert.pem /certs/key.pem - - reverse_proxy /system/action* backend:9002 - reverse_proxy /system/presenter* backend:9003 - reverse_proxy /system/autoupdate* autoupdate:9012 { - flush_interval -1 - } - reverse_proxy /system/icc* icc:9013 { - flush_interval -1 - } - reverse_proxy /system/auth* auth:9004 - reverse_proxy /system/media* media:9006 - - reverse_proxy client:9001 -} diff --git a/proxy/Dockerfile b/proxy/Dockerfile index 89473efff..93448e0b4 100644 --- a/proxy/Dockerfile +++ b/proxy/Dockerfile @@ -1,8 +1,10 @@ FROM caddy:2.3.0-alpine -COPY Caddyfile /etc/caddy/Caddyfile +RUN apk update && apk add --no-cache jq gettext + +COPY caddy_base.json /caddy_base.json COPY entrypoint /entrypoint COPY certs /certs ENTRYPOINT ["/entrypoint"] -CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"] +CMD ["caddy", "run", "--config", "/etc/caddy/config.json"] diff --git a/proxy/Dockerfile.dev b/proxy/Dockerfile.dev index 2227d1182..c9ec9d7b1 100644 --- a/proxy/Dockerfile.dev +++ b/proxy/Dockerfile.dev @@ -1,4 +1,12 @@ FROM caddy:2.3.0-alpine -COPY Caddyfile.dev /etc/caddy/Caddyfile +RUN apk update && apk add --no-cache jq gettext + +COPY caddy_base.json /caddy_base.json +COPY entrypoint /entrypoint COPY certs /certs + +ENV ENABLE_LOCAL_HTTPS=1 + +ENTRYPOINT ["/entrypoint"] +CMD ["caddy", "run", "--config", "/etc/caddy/config.json"] diff --git a/proxy/README.md b/proxy/README.md new file mode 100644 index 000000000..07c55c06b --- /dev/null +++ b/proxy/README.md @@ -0,0 +1,25 @@ +# OpenSlides proxy + +The proxy is the entrypoint for traffic going into an OpenSlides instance and +hides all the services needed for production behind a single port. On the +docker container this will be port 8000. An arbitrary port from the host can +then be forwarded to that (e.g. 443->8000). + +## HTTPS + +It is possible to make use of caddy's automatic https feature in order to not +having to manually generate TLS certificates. +Set `ENABLE_AUTO_HTTPS=1` and `EXTERNAL_ADDRESS=openslides.example.com` to +activate it. Caddy will then retrieve a letsencrypt certificate for that +domain. +For testing a setup e.g. +`ACME_ENDPOINT=https://acme-staging-v02.api.letsencrypt.org/directory` can also +be set to avoid hitting rate limits. +Importantly, port 80 on the host must be forwarded to port 8001 on which caddy +will answer the ACME-challenge during certificate retrieval. + +Alternatively a locally generated certificate can be used by executing +`make-localhost-cert.sh` before building the docker image (!) and setting +`ENABLE_LOCAL_HTTPS=1`. This is mostly for dev setup purposes and is not useful +for a public domain as the cert is not issued by a trusted CA and therefore +not trusted by browsers. If set, this overrules `ENABLE_AUTO_HTTPS`. diff --git a/proxy/caddy_base.json b/proxy/caddy_base.json new file mode 100644 index 000000000..e1712b7df --- /dev/null +++ b/proxy/caddy_base.json @@ -0,0 +1,194 @@ +{ + "apps": { + "tls": { + "automation": { + "policies": [ + { + "issuers": [ + { + "module": "acme", + "challenges": { + "tls-alpn": { + "disabled": true + } + } + } + ] + } + ] + } + }, + "http": { + "servers": { + "srv0": { + "listen": [ + ":8000" + ], + "allow_h2c": true, + "routes": [ + { + "handle": [ + { + "flush_interval": -1, + "handler": "reverse_proxy", + "upstreams": [ + { + "dial": "$AUTOUPDATE_HOST:$AUTOUPDATE_PORT" + } + ] + } + ], + "match": [ + { + "path": [ + "/system/autoupdate*" + ] + } + ] + }, + { + "handle": [ + { + "handler": "reverse_proxy", + "upstreams": [ + { + "dial": "$PRESENTER_HOST:$PRESENTER_PORT" + } + ] + } + ], + "match": [ + { + "path": [ + "/system/presenter*" + ] + } + ] + }, + { + "handle": [ + { + "handler": "reverse_proxy", + "upstreams": [ + { + "dial": "$ACTION_HOST:$ACTION_PORT" + } + ] + } + ], + "match": [ + { + "path": [ + "/system/action*" + ] + } + ] + }, + { + "handle": [ + { + "handler": "reverse_proxy", + "upstreams": [ + { + "dial": "$MEDIA_HOST:$MEDIA_PORT" + } + ] + } + ], + "match": [ + { + "path": [ + "/system/media*" + ] + } + ] + }, + { + "handle": [ + { + "handler": "reverse_proxy", + "upstreams": [ + { + "dial": "$AUTH_HOST:$AUTH_PORT" + } + ] + } + ], + "match": [ + { + "path": [ + "/system/auth*" + ] + } + ] + }, + { + "handle": [ + { + "flush_interval": -1, + "handler": "reverse_proxy", + "upstreams": [ + { + "dial": "$ICC_HOST:$ICC_PORT" + } + ] + } + ], + "match": [ + { + "path": [ + "/system/icc*" + ] + } + ] + }, + { + "handle": [ + { + "handler": "reverse_proxy", + "flush_interval": -1, + "transport": { + "protocol": "http", + "versions": [ + "2", + "h2c" + ] + }, + "upstreams": [ + { + "dial": "$MANAGE_HOST:$MANAGE_PORT" + } + ] + } + ], + "match": [ + { + "header": { + "Content-Type": [ + "application/grpc" + ] + } + } + ] + }, + { + "handle": [ + { + "handler": "reverse_proxy", + "upstreams": [ + { + "dial": "$CLIENT_HOST:$CLIENT_PORT" + } + ] + } + ] + } + ], + "automatic_https": { + "disable": true + } + } + } + } + } +} diff --git a/proxy/entrypoint b/proxy/entrypoint index da2650e0d..f07586092 100755 --- a/proxy/entrypoint +++ b/proxy/entrypoint @@ -2,15 +2,53 @@ set -e -if [[ -f "/certs/key.pem" ]] && [[ -f "/certs/cert.pem" ]]; then - cat <> /etc/caddy/endpoint -https://:8000 { - tls /certs/cert.pem /certs/key.pem -EOF - echo "Configured https" +config=/etc/caddy/config.json +base=/caddy_base.json + +# set defaults in base +ACTION_HOST="${ACTION_HOST:-backend}" ACTION_PORT="${ACTION_PORT:-9002}" \ +PRESENTER_HOST="${PRESENTER_HOST:-backend}" PRESENTER_PORT="${PRESENTER_PORT:-9003}" \ +AUTOUPDATE_HOST="${AUTOUPDATE_HOST:-autoupdate}" AUTOUPDATE_PORT="${AUTOUPDATE_PORT:-9012}" \ +ICC_HOST="${ICC_HOST:-icc}" ICC_PORT="${ICC_PORT:-9013}" \ +AUTH_HOST="${AUTH_HOST:-auth}" AUTH_PORT="${AUTH_PORT:-9004}" \ +MEDIA_HOST="${MEDIA_HOST:-media}" MEDIA_PORT="${MEDIA_PORT:-9006}" \ +MANAGE_HOST="${MANAGE_HOST:-manage}" MANAGE_PORT="${MANAGE_PORT:-9008}" \ +CLIENT_HOST="${CLIENT_HOST:-client}" CLIENT_PORT="${CLIENT_PORT:-9001}" \ +envsubst < "$base" > "$base.out" && mv -f "$base.out" "$base" + +jq_write() { + filter=$1 + jq "$filter" "$base" > "$base.out" && mv -f "$base.out" "$base" +} + +### HTTPS ### +if [ -n "$ENABLE_LOCAL_HTTPS" ]; then + [ -f /certs/cert.pem ] && [ -f /certs/key.pem ] || { + echo "ERROR: no local cert-files provided. Did you run make-localhost-cert.sh?" + exit 1 + } + jq_write ".apps.http.servers.srv0.tls_connection_policies = [{ certificate_selection: { any_tag: [ \"cert0\" ] }}]" + jq_write ".apps.tls = { certificates: { load_files: [{ certificate: \"/certs/cert.pem\", key: \"/certs/key.pem\", tags: [ \"cert0\" ] }] }}" else - echo "http://:8000 {" > /etc/caddy/endpoint - echo "Configured http" + if [ -n "$ENABLE_AUTO_HTTPS" ]; then + if [ -n "$EXTERNAL_ADDRESS" ]; then + echo "INFO: For the automatic https to work ports 443 and 80 of the host must be + directed to 8000 and 8001 of this container" + jq_write "del(.apps.http.servers.srv0.automatic_https)" # disabled in base + jq_write ".apps.http.https_port = 8000" + jq_write ".apps.http.http_port = 8001" + jq_write ".apps.http.servers.srv0.routes[-1].match = [{ host: [\"$EXTERNAL_ADDRESS\"]}]" + + if [ -n "$ACME_ENDPOINT" ]; then + jq_write ".apps.tls.automation.policies[0].issuers[0].ca = \"${ACME_ENDPOINT}\"" + fi + else + echo "ERROR: EXTERNAL_ADDRESS needed for automatic HTTPS / cert generation" + exit 1 + fi + fi fi -exec "$@" \ No newline at end of file +mv "$base" "$config" + +exec "$@"