From 471ccbadc54bd88add1e25c376664e4eece0ab42 Mon Sep 17 00:00:00 2001 From: Tom Wiesing Date: Fri, 7 Oct 2022 19:46:14 +0200 Subject: [PATCH] Move to Traefik --- cmd/system_update.go | 2 +- internal/component/control/control.env | 10 +-- internal/component/control/control.go | 6 +- .../control/control/docker-compose.yml | 23 ++++--- internal/component/info/html/index.html | 1 + .../component/instances/instances/barrel.env | 7 +- .../instances/barrel/docker-compose.yml | 24 +++---- .../component/instances/instances/reserve.env | 5 +- .../instances/reserve/docker-compose.yml | 21 +++--- internal/component/instances/wisski_stack.go | 18 ++--- internal/component/sql/sql.env | 2 + internal/component/sql/sql.go | 7 ++ internal/component/sql/sql/docker-compose.yml | 6 +- internal/component/ssh/ssh.env | 1 + internal/component/ssh/ssh.go | 6 ++ internal/component/ssh/ssh/docker-compose.yml | 5 +- internal/component/stack.go | 7 +- .../component/triplestore/triplestore.env | 1 + internal/component/triplestore/triplestore.go | 6 ++ .../triplestore/docker-compose.yml | 4 +- internal/component/web/web-http.env | 2 - .../component/web/web-http/docker-compose.yml | 31 ++++----- internal/component/web/web-http/global.conf | 4 -- internal/component/web/web-http/proxy.conf | 19 ------ internal/component/web/web-https.env | 2 - .../web/web-https/docker-compose.yml | 65 ++++++++----------- internal/component/web/web-https/global.conf | 4 -- internal/component/web/web-https/proxy.conf | 19 ------ internal/component/web/web.env | 2 + internal/component/web/web.go | 16 +++-- internal/config/config.go | 3 + internal/config/config_template | 3 + internal/config/domains.go | 39 ++++++----- internal/config/template.go | 9 +++ pkg/fsx/touch.go | 8 ++- pkg/unpack/template.go | 2 +- 36 files changed, 200 insertions(+), 190 deletions(-) create mode 100644 internal/component/sql/sql.env create mode 100644 internal/component/ssh/ssh.env create mode 100644 internal/component/triplestore/triplestore.env delete mode 100644 internal/component/web/web-http.env delete mode 100644 internal/component/web/web-http/global.conf delete mode 100644 internal/component/web/web-http/proxy.conf delete mode 100644 internal/component/web/web-https.env delete mode 100644 internal/component/web/web-https/global.conf delete mode 100644 internal/component/web/web-https/proxy.conf create mode 100644 internal/component/web/web.env diff --git a/cmd/system_update.go b/cmd/system_update.go index db79e9d..057cef0 100644 --- a/cmd/system_update.go +++ b/cmd/system_update.go @@ -103,7 +103,7 @@ func (si systemupdate) Run(context wisski_distillery.Context) error { // create the docker network // TODO: Use docker API for this logging.LogMessage(context.IOStream, "Updating Docker Configuration") - si.mustExec(context, "", "docker", "network", "create", "distillery") + si.mustExec(context, "", "docker", "network", "create", dis.Config.DockerNetworkName) // install and update the various stacks! ctx := component.InstallationContext{ diff --git a/internal/component/control/control.env b/internal/component/control/control.env index bfd1823..2b65aa7 100644 --- a/internal/component/control/control.env +++ b/internal/component/control/control.env @@ -1,10 +1,10 @@ -VIRTUAL_HOST=${VIRTUAL_HOST} - -LETSENCRYPT_HOST=${LETSENCRYPT_HOST} -LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL} +HOST_RULE=${HOST_RULE} CONFIG_PATH=${CONFIG_PATH} DEPLOY_ROOT=${DEPLOY_ROOT} GLOBAL_AUTHORIZED_KEYS_FILE=${GLOBAL_AUTHORIZED_KEYS_FILE} SELF_OVERRIDES_FILE=${SELF_OVERRIDES_FILE} -SELF_RESOLVER_BLOCK_FILE=${SELF_RESOLVER_BLOCK_FILE} \ No newline at end of file +SELF_RESOLVER_BLOCK_FILE=${SELF_RESOLVER_BLOCK_FILE} + +DOCKER_NETWORK_NAME=${DOCKER_NETWORK_NAME} +HTTPS_ENABLED=${HTTPS_ENABLED} \ No newline at end of file diff --git a/internal/component/control/control.go b/internal/component/control/control.go index 1d2700a..64198f2 100644 --- a/internal/component/control/control.go +++ b/internal/component/control/control.go @@ -34,9 +34,9 @@ func (control *Control) Stack(env environment.Environment) component.StackWithRe EnvPath: "control.env", EnvContext: map[string]string{ - "VIRTUAL_HOST": control.Config.DefaultHost(), - "LETSENCRYPT_HOST": control.Config.DefaultSSLHost(), - "LETSENCRYPT_EMAIL": control.Config.CertbotEmail, + "DOCKER_NETWORK_NAME": control.Config.DockerNetworkName, + "HOST_RULE": control.Config.DefaultHostRule(), + "HTTPS_ENABLED": control.Config.HTTPSEnabledEnv(), "CONFIG_PATH": control.Config.ConfigPath, "DEPLOY_ROOT": control.Config.DeployRoot, diff --git a/internal/component/control/control/docker-compose.yml b/internal/component/control/control/docker-compose.yml index c95fe95..83c31ed 100644 --- a/internal/component/control/control/docker-compose.yml +++ b/internal/component/control/control/docker-compose.yml @@ -5,15 +5,20 @@ services: build: . restart: always environment: - # port and hostname for this image to use - VIRTUAL_HOST: ${VIRTUAL_HOST} - VIRTUAL_PORT: 8888 - CONFIG_PATH: ${CONFIG_PATH} - - # optional letsencrypt email - LETSENCRYPT_HOST: ${LETSENCRYPT_HOST} - LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL} + labels: + + - "traefik.enable=True" + - "eu.wiss-ki.barrel.distillery=${DOCKER_NETWORK_NAME}" + - "traefik.http.routers.control.rule=${HOST_RULE}" + + - "traefik.http.routers.fallback.rule=HostRegexp(`{catchall:.*}`)" + - "traefik.http.routers.fallback.priority=1" + + - "traefik.http.routers.control.tls=${HTTPS_ENABLED}" + - "traefik.http.routers.control.tls.certresolver=distillery" + - "traefik.http.services.control.loadbalancer.server.port=8888" + volumes: # TODO: Mount docker socket properly! @@ -26,5 +31,5 @@ services: networks: default: - name: distillery + name: ${DOCKER_NETWORK_NAME} external: true diff --git a/internal/component/info/html/index.html b/internal/component/info/html/index.html index 57a2854..bb9fcbb 100644 --- a/internal/component/info/html/index.html +++ b/internal/component/info/html/index.html @@ -10,6 +10,7 @@ Domain: {{.Config.DefaultDomain}}
Legacy Domain(s): {{.Config.SelfExtraDomains}}
HTTPS Email: {{.Config.CertbotEmail}}
+ Docker Network Name: {{.Config.DockerNetworkName}}

Homepage Redirect:{{.Config.SelfRedirect}}

diff --git a/internal/component/instances/instances/barrel.env b/internal/component/instances/instances/barrel.env index 9d93502..fbc862e 100644 --- a/internal/component/instances/instances/barrel.env +++ b/internal/component/instances/instances/barrel.env @@ -1,10 +1,9 @@ DATA_PATH=${DATA_PATH} RUNTIME_DIR=${RUNTIME_DIR} +GLOBAL_AUTHORIZED_KEYS_FILE=${GLOBAL_AUTHORIZED_KEYS_FILE} SLUG=${SLUG} VIRTUAL_HOST=${VIRTUAL_HOST} +DOCKER_NETWORK_NAME=${DOCKER_NETWORK_NAME} -LETSENCRYPT_HOST=${LETSENCRYPT_HOST} -LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL} - -GLOBAL_AUTHORIZED_KEYS_FILE=${GLOBAL_AUTHORIZED_KEYS_FILE} \ No newline at end of file +HTTPS_ENABLED=${HTTPS_ENABLED} \ No newline at end of file diff --git a/internal/component/instances/instances/barrel/docker-compose.yml b/internal/component/instances/instances/barrel/docker-compose.yml index 04dddab..43f109e 100644 --- a/internal/component/instances/instances/barrel/docker-compose.yml +++ b/internal/component/instances/instances/barrel/docker-compose.yml @@ -5,19 +5,19 @@ services: build: . restart: always hostname: ${VIRTUAL_HOST}.wisski - environment: - # port and hostname for this image to use - VIRTUAL_HOST: ${VIRTUAL_HOST} - VIRTUAL_PORT: 8080 - - # optional letsencrypt email - LETSENCRYPT_HOST: ${LETSENCRYPT_HOST} - LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL} - + # label it with the current slug labels: - eu.wiss-ki.barrel.slug: ${SLUG} - eu.wiss-ki.barrel.authfile: /var/www/.ssh/authorized_keys,/var/www/.ssh/global_authorized_keys + - "eu.wiss-ki.barrel.slug=${SLUG}" + - "eu.wiss-ki.barrel.authfile:=/var/www/.ssh/authorized_keys,/var/www/.ssh/global_authorized_keys" + + - "traefik.enable=True" + - "eu.wiss-ki.barrel.distillery=${DOCKER_NETWORK_NAME}" + + - "traefik.http.routers.wisski_${SLUG}.rule=Host(`${VIRTUAL_HOST}`)" + - "traefik.http.routers.wisski_${SLUG}.tls=${HTTPS_ENABLED}" + - "traefik.http.routers.wisski_${SLUG}.tls.certresolver=distillery" + - "traefik.http.services.wisski_${SLUG}.loadbalancer.server.port=8080" # volumes that are mounted volumes: @@ -29,5 +29,5 @@ services: networks: default: - name: distillery + name: ${DOCKER_NETWORK_NAME} external: true diff --git a/internal/component/instances/instances/reserve.env b/internal/component/instances/instances/reserve.env index cd4d7da..b72c195 100644 --- a/internal/component/instances/instances/reserve.env +++ b/internal/component/instances/instances/reserve.env @@ -1,4 +1,5 @@ +SLUG=${SLUG} VIRTUAL_HOST=${VIRTUAL_HOST} -LETSENCRYPT_HOST=${LETSENCRYPT_HOST} -LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL} \ No newline at end of file +DOCKER_NETWORK_NAME=${DOCKER_NETWORK_NAME} +HTTPS_ENABLED=${HTTPS_ENABLED} diff --git a/internal/component/instances/instances/reserve/docker-compose.yml b/internal/component/instances/instances/reserve/docker-compose.yml index 666aa3f..acb2e42 100644 --- a/internal/component/instances/instances/reserve/docker-compose.yml +++ b/internal/component/instances/instances/reserve/docker-compose.yml @@ -4,23 +4,24 @@ services: static: image: tkw01536/gostatic restart: always - environment: - # port and hostname for this image to use - VIRTUAL_HOST: ${VIRTUAL_HOST} - VIRTUAL_PORT: 8043 - - # optional letsencrypt email - LETSENCRYPT_HOST: ${LETSENCRYPT_HOST} - LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL} - ports: - 8043 + labels: + - "traefik.enable=True" + - "eu.wiss-ki.barrel.distillery=${DOCKER_NETWORK_NAME}" + + - "traefik.http.routers.reserve_${SLUG}.rule=Host(`${VIRTUAL_HOST}`)" + - "traefik.http.routers.reserve_${SLUG}.tls=${HTTPS_ENABLED}" + - "traefik.http.routers.reserve_${SLUG}.tls.certresolver=distillery" + - "traefik.http.services.reserve_${SLUG}.loadbalancer.server.port=8043" + + # volumes that are mounted volumes: - ./index.html:/srv/http/index.html:ro networks: default: - name: distillery + name: ${DOCKER_NETWORK_NAME} external: true diff --git a/internal/component/instances/wisski_stack.go b/internal/component/instances/wisski_stack.go index 48f7bbf..cf5812c 100644 --- a/internal/component/instances/wisski_stack.go +++ b/internal/component/instances/wisski_stack.go @@ -25,14 +25,13 @@ func (wisski *WissKI) Barrel() component.StackWithResources { EnvPath: filepath.Join("instances", "barrel.env"), EnvContext: map[string]string{ - "DATA_PATH": filepath.Join(wisski.FilesystemBase, "data"), + "DOCKER_NETWORK_NAME": wisski.instances.Config.DockerNetworkName, - "SLUG": wisski.Slug, - "VIRTUAL_HOST": wisski.Domain(), - - "LETSENCRYPT_HOST": wisski.instances.Config.IfHttps(wisski.Domain()), - "LETSENCRYPT_EMAIL": wisski.instances.Config.IfHttps(wisski.instances.Config.CertbotEmail), + "SLUG": wisski.Slug, + "VIRTUAL_HOST": wisski.Domain(), + "HTTPS_ENABLED": wisski.instances.Config.HTTPSEnabledEnv(), + "DATA_PATH": filepath.Join(wisski.FilesystemBase, "data"), "RUNTIME_DIR": wisski.instances.Config.RuntimeDir(), "GLOBAL_AUTHORIZED_KEYS_FILE": wisski.instances.Config.GlobalAuthorizedKeysFile, }, @@ -109,10 +108,11 @@ func (wisski *WissKI) Reserve() component.StackWithResources { EnvPath: filepath.Join("instances", "reserve.env"), EnvContext: map[string]string{ - "VIRTUAL_HOST": wisski.Domain(), + "DOCKER_NETWORK_NAME": wisski.instances.Config.DockerNetworkName, - "LETSENCRYPT_HOST": wisski.instances.Config.IfHttps(wisski.Domain()), - "LETSENCRYPT_EMAIL": wisski.instances.Config.IfHttps(wisski.instances.Config.CertbotEmail), + "SLUG": wisski.Slug, + "VIRTUAL_HOST": wisski.Domain(), + "HTTPS_ENABLED": wisski.instances.Config.HTTPSEnabledEnv(), }, } } diff --git a/internal/component/sql/sql.env b/internal/component/sql/sql.env new file mode 100644 index 0000000..3089c4c --- /dev/null +++ b/internal/component/sql/sql.env @@ -0,0 +1,2 @@ +DOCKER_NETWORK_NAME=${DOCKER_NETWORK_NAME} +HTTPS_ENABLED=${HTTPS_ENABLED} diff --git a/internal/component/sql/sql.go b/internal/component/sql/sql.go index 199aaf8..acbcbf4 100644 --- a/internal/component/sql/sql.go +++ b/internal/component/sql/sql.go @@ -35,6 +35,7 @@ func (*SQL) Context(parent component.InstallationContext) component.Installation } //go:embed all:sql +//go:embed sql.env var resources embed.FS func (sql *SQL) Stack(env environment.Environment) component.StackWithResources { @@ -42,6 +43,12 @@ func (sql *SQL) Stack(env environment.Environment) component.StackWithResources Resources: resources, ContextPath: "sql", + EnvPath: "sql.env", + EnvContext: map[string]string{ + "DOCKER_NETWORK_NAME": sql.Config.DockerNetworkName, + "HTTPS_ENABLED": sql.Config.HTTPSEnabledEnv(), + }, + MakeDirsPerm: environment.DefaultDirPerm, MakeDirs: []string{ "data", diff --git a/internal/component/sql/sql/docker-compose.yml b/internal/component/sql/sql/docker-compose.yml index a40cc82..5ffb0a5 100644 --- a/internal/component/sql/sql/docker-compose.yml +++ b/internal/component/sql/sql/docker-compose.yml @@ -7,6 +7,8 @@ services: - "./data/:/var/lib/mysql" ports: - 127.0.0.1:3306:3306 + labels: + - "eu.wiss-ki.barrel.distillery=${DOCKER_NETWORK_NAME}" environment: # This combination of environment variables will configure a passwordless root user # that can only connect to the container from 'localhost'. @@ -24,6 +26,8 @@ services: # By default no admin account is created, so initial shell access to make one is needed. ports: - 127.0.0.1:8080:80 + labels: + - "eu.wiss-ki.barrel.distillery=${DOCKER_NETWORK_NAME}" depends_on: - sql restart: always @@ -31,5 +35,5 @@ services: networks: default: - name: distillery + name: ${DOCKER_NETWORK_NAME} external: true diff --git a/internal/component/ssh/ssh.env b/internal/component/ssh/ssh.env new file mode 100644 index 0000000..131c6ab --- /dev/null +++ b/internal/component/ssh/ssh.env @@ -0,0 +1 @@ +DOCKER_NETWORK_NAME=${DOCKER_NETWORK_NAME} diff --git a/internal/component/ssh/ssh.go b/internal/component/ssh/ssh.go index acd7ca6..0c07a0b 100644 --- a/internal/component/ssh/ssh.go +++ b/internal/component/ssh/ssh.go @@ -25,11 +25,17 @@ func (SSH) Context(parent component.InstallationContext) component.InstallationC } //go:embed all:ssh +//go:embed ssh.env var resources embed.FS func (ssh *SSH) Stack(env environment.Environment) component.StackWithResources { return component.MakeStack(ssh, env, component.StackWithResources{ Resources: resources, ContextPath: "ssh", + + EnvPath: "ssh.env", + EnvContext: map[string]string{ + "DOCKER_NETWORK_NAME": ssh.Config.DockerNetworkName, + }, }) } diff --git a/internal/component/ssh/ssh/docker-compose.yml b/internal/component/ssh/ssh/docker-compose.yml index 7e7eb36..3566f74 100644 --- a/internal/component/ssh/ssh/docker-compose.yml +++ b/internal/component/ssh/ssh/docker-compose.yml @@ -9,9 +9,12 @@ services: volumes: - './data/keys:/keys' - '/var/run/docker.sock:/var/run/docker.sock:ro' + labels: + - "eu.wiss-ki.barrel.distillery=${DOCKER_NETWORK_NAME}" restart: always networks: default: - name: distillery + name: ${DOCKER_NETWORK_NAME} external: true + diff --git a/internal/component/stack.go b/internal/component/stack.go index 931977b..932051d 100644 --- a/internal/component/stack.go +++ b/internal/component/stack.go @@ -204,10 +204,11 @@ type StackWithResources struct { CopyContextFiles []string // Files to copy from the installation context - MakeDirsPerm fs.FileMode // permission for diretories, defaults to [environment.DefaultDirCreate] + MakeDirsPerm fs.FileMode // permission for dirctories, defaults to [environment.DefaultDirCreate] MakeDirs []string // directories to ensure that exist - TouchFiles []string // Files to 'touch', i.e. ensure that exist; guaranteed to be run after MakeDirs + TouchFilesPerm fs.FileMode // permission for new files to touch, defaults to [environment.DefaultFileCreate] + TouchFiles []string // Files to 'touch', i.e. ensure that exist; guaranteed to be run after MakeDirs } // InstallationContext is a context to install data in @@ -287,7 +288,7 @@ func (is StackWithResources) Install(io stream.IOStream, context InstallationCon dst := filepath.Join(is.Dir, name) io.Printf("[touch] %s\n", dst) - if err := fsx.Touch(env, dst); err != nil { + if err := fsx.Touch(env, dst, is.TouchFilesPerm); err != nil { return err } } diff --git a/internal/component/triplestore/triplestore.env b/internal/component/triplestore/triplestore.env new file mode 100644 index 0000000..131c6ab --- /dev/null +++ b/internal/component/triplestore/triplestore.env @@ -0,0 +1 @@ +DOCKER_NETWORK_NAME=${DOCKER_NETWORK_NAME} diff --git a/internal/component/triplestore/triplestore.go b/internal/component/triplestore/triplestore.go index 57b6e73..bb52708 100644 --- a/internal/component/triplestore/triplestore.go +++ b/internal/component/triplestore/triplestore.go @@ -32,6 +32,7 @@ func (Triplestore) Context(parent component.InstallationContext) component.Insta } //go:embed all:triplestore +//go:embed triplestore.env var resources embed.FS func (ts *Triplestore) Stack(env environment.Environment) component.StackWithResources { @@ -41,6 +42,11 @@ func (ts *Triplestore) Stack(env environment.Environment) component.StackWithRes CopyContextFiles: []string{"graphdb.zip"}, // TODO: Move into constant? + EnvPath: "triplestore.env", + EnvContext: map[string]string{ + "DOCKER_NETWORK_NAME": ts.Config.DockerNetworkName, + }, + MakeDirs: []string{ filepath.Join("data", "data"), filepath.Join("data", "work"), diff --git a/internal/component/triplestore/triplestore/docker-compose.yml b/internal/component/triplestore/triplestore/docker-compose.yml index e1f2198..5f70431 100644 --- a/internal/component/triplestore/triplestore/docker-compose.yml +++ b/internal/component/triplestore/triplestore/docker-compose.yml @@ -13,10 +13,12 @@ services: # Use 1GB of heap space environment: GDB_HEAP_SIZE: 16G + labels: + - "eu.wiss-ki.barrel.distillery=${DOCKER_NETWORK_NAME}" restart: always networks: default: - name: distillery + name: ${DOCKER_NETWORK_NAME} external: true \ No newline at end of file diff --git a/internal/component/web/web-http.env b/internal/component/web/web-http.env deleted file mode 100644 index 1eed846..0000000 --- a/internal/component/web/web-http.env +++ /dev/null @@ -1,2 +0,0 @@ -DEFAULT_HOST=${DEFAULT_HOST} -HTTPS_METHOD=nohttps \ No newline at end of file diff --git a/internal/component/web/web-http/docker-compose.yml b/internal/component/web/web-http/docker-compose.yml index f4c2113..774f692 100644 --- a/internal/component/web/web-http/docker-compose.yml +++ b/internal/component/web/web-http/docker-compose.yml @@ -1,30 +1,27 @@ version: "3.7" services: - nginx-proxy: - image: ghcr.io/nginx-proxy/nginx-proxy:alpine - environment: - - DEFAULT_HOST=${DEFAULT_HOST} - - HTTPS_METHOD=${HTTPS_METHOD} + reverse-proxy: + image: docker.io/library/traefik:v2.9 + command: + - "--providers.docker" + - "--providers.docker.exposedByDefault=false" + - "--providers.docker.network=${DOCKER_NETWORK_NAME}" + - "--providers.docker.constraints=Label(`eu.wiss-ki.barrel.distillery`,`${DOCKER_NETWORK_NAME}`)" + - "--entrypoints.web.address=:80" + + ## for debugging purposes, the following can be enabled. + # - "--api.insecure=true" ports: - "80:80" + # - "127.0.0.1:8888:8080" volumes: - - "vhost:/etc/nginx/vhost.d" - - "./global.conf:/etc/nginx/conf.d/global.conf:ro" - - "./proxy.conf:/etc/nginx/proxy.conf:ro" - - "htpasswd:/etc/nginx/htpasswd" - - "html:/usr/share/nginx/html" - - "/var/run/docker.sock:/tmp/docker.sock:ro" + - "/var/run/docker.sock:/var/run/docker.sock" restart: always networks: - default -volumes: - vhost: - html: - htpasswd: - networks: default: - name: distillery + name: ${DOCKER_NETWORK_NAME} external: true diff --git a/internal/component/web/web-http/global.conf b/internal/component/web/web-http/global.conf deleted file mode 100644 index 2f73621..0000000 --- a/internal/component/web/web-http/global.conf +++ /dev/null @@ -1,4 +0,0 @@ -# Nginx Configuration File -# These should match with distillery/resources/compose/barrel/conf/wisski.ini. - -client_max_body_size 1000m; diff --git a/internal/component/web/web-http/proxy.conf b/internal/component/web/web-http/proxy.conf deleted file mode 100644 index 59fec40..0000000 --- a/internal/component/web/web-http/proxy.conf +++ /dev/null @@ -1,19 +0,0 @@ -# HTTP 1.1 support -proxy_http_version 1.1; -proxy_buffering off; -proxy_set_header Host $http_host; -proxy_set_header Upgrade $http_upgrade; -proxy_set_header Connection $proxy_connection; -proxy_set_header X-Real-IP $remote_addr; -proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; -proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; -proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; -proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port; - -# Mitigate httpoxy attack (see README for details) -proxy_set_header Proxy ""; - -# Timeouts for the proxy connection - in sync with the appropriate max_execution time. -proxy_connect_timeout 3000s; -proxy_read_timeout 3000s; -proxy_send_timeout 3000s; diff --git a/internal/component/web/web-https.env b/internal/component/web/web-https.env deleted file mode 100644 index 00e84d1..0000000 --- a/internal/component/web/web-https.env +++ /dev/null @@ -1,2 +0,0 @@ -DEFAULT_HOST=${DEFAULT_HOST} -HTTPS_METHOD=redirect \ No newline at end of file diff --git a/internal/component/web/web-https/docker-compose.yml b/internal/component/web/web-https/docker-compose.yml index b11d520..7965e05 100644 --- a/internal/component/web/web-https/docker-compose.yml +++ b/internal/component/web/web-https/docker-compose.yml @@ -1,52 +1,41 @@ version: "3.7" services: - nginx-proxy: - image: ghcr.io/nginx-proxy/nginx-proxy:alpine - environment: - - DEFAULT_HOST=${DEFAULT_HOST} - - HTTPS_METHOD=${HTTPS_METHOD} + reverse-proxy: + image: docker.io/library/traefik:v2.9 + command: + - "--providers.docker" + + - "--providers.docker.exposedByDefault=false" + - "--providers.docker.network=${DOCKER_NETWORK_NAME}" + - "--providers.docker.constraints=Label(`eu.wiss-ki.barrel.distillery`,`${DOCKER_NETWORK_NAME}`)" + + - "--entrypoints.web.address=:80" + - "--entrypoints.web.http.redirections.entryPoint.to=websecure" + - "--entrypoints.web.http.redirections.entryPoint.scheme=https" + - "--entrypoints.websecure.address=:443" + + - "--certificatesresolvers.distillery.acme.httpchallenge=true" + - "--certificatesresolvers.distillery.acme.email=${CERT_EMAIL}" + - "--certificatesresolvers.distillery.acme.storage=/acme.json" + - "--certificatesresolvers.distillery.acme.httpchallenge.entrypoint=web" + + ## for debugging purposes, the following can be enabled. + # - "--api.insecure=true" + # - "--certificatesresolvers.distillery.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory" + ports: - "80:80" - "443:443" + # - "127.0.0.1:8888:8080" volumes: - - "vhost:/etc/nginx/vhost.d" - - "./global.conf:/etc/nginx/conf.d/global.conf:ro" - - "./proxy.conf:/etc/nginx/proxy.conf:ro" - - "htpasswd:/etc/nginx/htpasswd" - - "html:/usr/share/nginx/html" - - "/var/run/docker.sock:/tmp/docker.sock:ro" - - "certs:/etc/nginx/certs" - labels: - com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: true + - "/var/run/docker.sock:/var/run/docker.sock" + - "./acme.json:/acme.json" restart: always networks: - default - letsencrypt-nginx-proxy-companion: - image: docker.io/nginxproxy/acme-companion:latest - volumes: - - "/var/run/docker.sock:/var/run/docker.sock:ro" - - "htpasswd:/etc/nginx/htpasswd" - - "vhost:/etc/nginx/vhost.d" - - "html:/usr/share/nginx/html" - - "/var/run/docker.sock:/tmp/docker.sock:ro" - - "certs:/etc/nginx/certs" - - "acme:/etc/acme.sh" - restart: always - networks: - - default - depends_on: - - nginx-proxy - -volumes: - acme: - vhost: - html: - certs: - htpasswd: - networks: default: - name: distillery + name: ${DOCKER_NETWORK_NAME} external: true diff --git a/internal/component/web/web-https/global.conf b/internal/component/web/web-https/global.conf deleted file mode 100644 index 2f73621..0000000 --- a/internal/component/web/web-https/global.conf +++ /dev/null @@ -1,4 +0,0 @@ -# Nginx Configuration File -# These should match with distillery/resources/compose/barrel/conf/wisski.ini. - -client_max_body_size 1000m; diff --git a/internal/component/web/web-https/proxy.conf b/internal/component/web/web-https/proxy.conf deleted file mode 100644 index 59fec40..0000000 --- a/internal/component/web/web-https/proxy.conf +++ /dev/null @@ -1,19 +0,0 @@ -# HTTP 1.1 support -proxy_http_version 1.1; -proxy_buffering off; -proxy_set_header Host $http_host; -proxy_set_header Upgrade $http_upgrade; -proxy_set_header Connection $proxy_connection; -proxy_set_header X-Real-IP $remote_addr; -proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; -proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; -proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; -proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port; - -# Mitigate httpoxy attack (see README for details) -proxy_set_header Proxy ""; - -# Timeouts for the proxy connection - in sync with the appropriate max_execution time. -proxy_connect_timeout 3000s; -proxy_read_timeout 3000s; -proxy_send_timeout 3000s; diff --git a/internal/component/web/web.env b/internal/component/web/web.env new file mode 100644 index 0000000..33613c0 --- /dev/null +++ b/internal/component/web/web.env @@ -0,0 +1,2 @@ +DOCKER_NETWORK_NAME=${DOCKER_NETWORK_NAME} +CERT_EMAIL=${CERT_EMAIL} \ No newline at end of file diff --git a/internal/component/web/web.go b/internal/component/web/web.go index 7b90a9e..32754c0 100644 --- a/internal/component/web/web.go +++ b/internal/component/web/web.go @@ -37,33 +37,37 @@ func (web Web) Stack(env environment.Environment) component.StackWithResources { } //go:embed all:web-https -//go:embed web-https.env +//go:embed web.env var httpsResources embed.FS func (web *Web) stackHTTPS(env environment.Environment) component.StackWithResources { return component.MakeStack(web, env, component.StackWithResources{ Resources: httpsResources, ContextPath: "web-https", - EnvPath: "web-https.env", + EnvPath: "web.env", EnvContext: map[string]string{ - "DEFAULT_HOST": web.Config.DefaultDomain, + "DOCKER_NETWORK_NAME": web.Config.DockerNetworkName, + "CERT_EMAIL": web.Config.CertbotEmail, }, + TouchFilesPerm: 0600, + TouchFiles: []string{"acme.json"}, }) } //go:embed all:web-http -//go:embed web-http.env +//go:embed web.env var httpResources embed.FS func (web *Web) stackHTTP(env environment.Environment) component.StackWithResources { return component.MakeStack(web, env, component.StackWithResources{ Resources: httpResources, ContextPath: "web-http", - EnvPath: "web-http.env", + EnvPath: "web.env", EnvContext: map[string]string{ - "DEFAULT_HOST": web.Config.DefaultDomain, + "DOCKER_NETWORK_NAME": web.Config.DockerNetworkName, + "CERT_EMAIL": web.Config.CertbotEmail, }, }) } diff --git a/internal/config/config.go b/internal/config/config.go index 2acf4b1..1167bf5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -83,6 +83,9 @@ type Config struct { DisAdminUser string `env:"DIS_ADMIN_USER" default:"admin" parser:"nonempty"` DisAdminPassword string `env:"DIS_ADMIN_PASSWORD" default:"" parser:"nonempty"` + // name of docker network to use + DockerNetworkName string `env:"DOCKER_NETWORK_NAME" default:"distillery" parser:"nonempty"` + // ConfigPath is the path this configuration was loaded from (if any) ConfigPath string } diff --git a/internal/config/config_template b/internal/config/config_template index 12b8c67..d3c22f0 100644 --- a/internal/config/config_template +++ b/internal/config/config_template @@ -2,6 +2,9 @@ # On top of this all real-system space will be created under this directory. DEPLOY_ROOT=${DEPLOY_ROOT} +# The name of the (global) docker network to run the distillery services in. +DOCKER_NETWORK_NAME=${DOCKER_NETWORK_NAME} + # Each created Drupal Instance corresponds to a single domain name. # These domain names should either be a complete domain name or a sub-domain of a default domain. # This setting configures the default domain-name to create subdomains of. diff --git a/internal/config/domains.go b/internal/config/domains.go index f7c637a..05234b0 100644 --- a/internal/config/domains.go +++ b/internal/config/domains.go @@ -1,7 +1,10 @@ package config import ( + "fmt" "strings" + + "github.com/tkw1536/goprogram/lib/collection" ) // This file contains domain related derived configuration values. @@ -11,6 +14,22 @@ func (cfg Config) HTTPSEnabled() bool { return cfg.CertbotEmail != "" } +// HostRequirement returns a traefik rule for the given names +func (Config) HostRule(names ...string) string { + quoted := collection.MapSlice(names, func(name string) string { + return "`" + name + "`" + }) + return fmt.Sprintf("Host(%s)", strings.Join(quoted, ",")) +} + +// HTTPSEnabledEnv returns "true" if https is enabled, and "false" otherwise. +func (cfg Config) HTTPSEnabledEnv() string { + if cfg.HTTPSEnabled() { + return "true" + } + return "false" +} + // IfHttps returns value when the distillery has https enabled, and the empty string otherwise. func (cfg Config) IfHttps(value string) string { if !cfg.HTTPSEnabled() { @@ -19,27 +38,17 @@ func (cfg Config) IfHttps(value string) string { return value } -// DefaultHost returns the default hostname for the distillery. -// +// DefaultHostRule returns the default traefik hostname rule for this distillery. // This consists of the [DefaultDomain] as well as [ExtraDomains]. -// Domain names are concatinated with commas. -func (cfg Config) DefaultHost() string { - var builder strings.Builder - - builder.WriteString(cfg.DefaultDomain) - for _, domain := range cfg.SelfExtraDomains { - builder.WriteRune(',') - builder.WriteString(domain) - } - - return builder.String() +func (cfg Config) DefaultHostRule() string { + return cfg.HostRule(append([]string{cfg.DefaultDomain}, cfg.SelfExtraDomains...)...) } // DefaultSSLHost returns the default hostname for the ssl version of the distillery. // // This is exactly [DefaultHost] when SSL is enabled, and the empty string otherwise. -func (cfg Config) DefaultSSLHost() string { - return cfg.IfHttps(cfg.DefaultHost()) +func (cfg Config) xDefaultSSLHost() string { + panic("not implemented") } // SlugFromHost returns the slug belonging to the appropriate host.' diff --git a/internal/config/template.go b/internal/config/template.go index 1c85f1e..ffbc11a 100644 --- a/internal/config/template.go +++ b/internal/config/template.go @@ -28,6 +28,7 @@ type Template struct { MysqlAdminPassword string `env:"MYSQL_ADMIN_PASSWORD"` DisAdminUsername string `env:"DIS_ADMIN_USER"` DisAdminPassword string `env:"DIS_ADMIN_PASSWORD"` + DockerNetworkName string `env:"DOCKER_NETWORK_NAME"` } // SetDefaults sets defaults on the template @@ -85,6 +86,14 @@ func (tpl *Template) SetDefaults(env environment.Environment) (err error) { } } + if tpl.DockerNetworkName == "" { + tpl.DockerNetworkName, err = password.Password(10) + if err != nil { + return err + } + tpl.DockerNetworkName = `distillery-` + tpl.DockerNetworkName + } + return nil } diff --git a/pkg/fsx/touch.go b/pkg/fsx/touch.go index aefa694..412f2d7 100644 --- a/pkg/fsx/touch.go +++ b/pkg/fsx/touch.go @@ -1,6 +1,7 @@ package fsx import ( + "io/fs" "time" "github.com/FAU-CDI/wisski-distillery/pkg/environment" @@ -11,11 +12,14 @@ import ( // // If the file does not exist exists, it is created using [env.Create]. // If the file does exist, it's access and modification times are updated to the current time. -func Touch(env environment.Environment, path string) error { +func Touch(env environment.Environment, path string, perm fs.FileMode) error { + if perm == 0 { + perm = environment.DefaultFilePerm + } _, err := env.Stat(path) switch { case environment.IsNotExist(err): - f, err := env.Create(path, environment.DefaultFilePerm) + f, err := env.Create(path, perm) if err != nil { return err } diff --git a/pkg/unpack/template.go b/pkg/unpack/template.go index 2e7f86b..98e17d2 100644 --- a/pkg/unpack/template.go +++ b/pkg/unpack/template.go @@ -177,7 +177,7 @@ parseloop: // Check if there were missing template keys. // If so, we sort them and return an appropriate error. if len(missingKeys) != 0 { - keys := maps.Keys(unusedKeys) + keys := maps.Keys(missingKeys) slices.Sort(keys) return MissingTemplateKeyError{ Keys: keys,