diff --git a/base/overlay.bu b/base/overlay.bu index e928dd6..80d42c1 100644 --- a/base/overlay.bu +++ b/base/overlay.bu @@ -41,3 +41,19 @@ systemd: contents: | [Service] ExecStartPost=/bin/bash -c 'if [ -f /var/lib/private/sshd/ssh_host_%i_key ]; then cp -Z /var/lib/private/sshd/ssh_host_%i_key{,.pub} /etc/ssh/; elif [ -f /etc/ssh/ssh_host_%i_key ]; then cp -a /etc/ssh/ssh_host_%i_key{,.pub} /var/lib/private/sshd/; fi' +passwd: + users: + - name: core + should_exist: false + - name: itix-svc + uid: 10000 + gecos: ITIX Misc. Services + home_dir: /tmp + primary_group: itix-svc + groups: + - name: core + should_exist: false + - name: itix + gid: 1000 + - name: itix-svc + gid: 10000 diff --git a/nextcloud/Makefile b/nextcloud/Makefile index 832dcfa..b55c155 100644 --- a/nextcloud/Makefile +++ b/nextcloud/Makefile @@ -2,7 +2,7 @@ ## Makefile for PostgreSQL quadlet ## -DEPENDENCIES = postgresql +DEPENDENCIES = postgresql traefik # Nextcloud quadlet is mapped to the 10008 user (nextcloud) and 10000 group (itix-svc) PROJECT_UID = 10008 @@ -32,9 +32,13 @@ $(TARGET_CHROOT)/etc/quadlets/nextcloud/collabora-seccomp-profile.json: install-config: $(TARGET_CHROOT)/var/lib/quadlets/nextcloud/redis $(TARGET_CHROOT)/var/lib/quadlets/nextcloud/data $(TARGET_CHROOT)/var/lib/quadlets/nextcloud/config $(TARGET_CHROOT)/etc/quadlets/nextcloud/collabora-seccomp-profile.json -install-examples: $(TARGET_CHROOT)/etc/quadlets/postgresql/init.d/nextcloud.sql +install-examples: $(TARGET_CHROOT)/etc/quadlets/postgresql/init.d/nextcloud.sql $(TARGET_CHROOT)/etc/quadlets/traefik/conf.d/nextcloud.yaml $(TARGET_CHROOT)/etc/quadlets/traefik/conf.d/collabora.yaml + $(TARGET_CHROOT)/etc/quadlets/postgresql/init.d/nextcloud.sql: other/nextcloud.sql - install -m 0644 -o 10004 -g 10000 $< $@ + install -m 0600 -o 10004 -g 10000 $< $@ + +$(TARGET_CHROOT)/etc/quadlets/traefik/conf.d/nextcloud.yaml $(TARGET_CHROOT)/etc/quadlets/traefik/conf.d/collabora.yaml: $(TARGET_CHROOT)/etc/quadlets/traefik/conf.d/%.yaml: other/traefik-%.yaml + install -m 0644 -o 10001 -g 10000 $< $@ test: @run() { echo $$*; "$$@"; }; \ diff --git a/nextcloud/config/examples/collabora.env b/nextcloud/config/examples/collabora.env index 374e4c4..26727f0 100644 --- a/nextcloud/config/examples/collabora.env +++ b/nextcloud/config/examples/collabora.env @@ -16,7 +16,7 @@ dictionaries=fr_FR en_US en_GB # When this environment variable is set (is not “”), then its value will be used # as server name in /etc/coolwsd/coolwsd.xml. Without this, CODE is not delivering # a correct host for the websocket connection in case of a proxy in front of it. -server_name=localhost +server_name=collabora # You can pass extra command line parameters to coolwsd via this environment # variable. For example, if you want to start coolwsd without SSL, when you @@ -27,7 +27,7 @@ extra_params=--o:ssl.enable=false --o:ssl.termination=false # By default Collabora Online enables the first WOPI host that tries to connect. # You can define the allowed WOPI hosts by passing environment variables. -aliasgroup1=http://localhost:9980 +aliasgroup1=http://collabora # When this environment variable is set (is not “”), then startup script will # not generate a new SSL certificate signed by a dummy CA. It is useful, if diff --git a/nextcloud/config/examples/config.env b/nextcloud/config/examples/config.env index a7774d3..eb05ea1 100644 --- a/nextcloud/config/examples/config.env +++ b/nextcloud/config/examples/config.env @@ -8,10 +8,11 @@ REDIS_MAJOR=8 NGINX_MAJOR=1.20 # Nextcloud domain configuration -NEXTCLOUD_TRUSTED_DOMAINS=localhost -OVERWRITEHOST=localhost +NEXTCLOUD_TRUSTED_DOMAINS=nextcloud localhost +OVERWRITEHOST=nextcloud OVERWRITEPROTOCOL=http -OVERWRITECLIURL=http://localhost +OVERWRITECLIURL=http://localhost:8080 +TRUSTED_PROXIES=127.0.0.1 # Nextcloud admin credentials NEXTCLOUD_ADMIN_USER=admin diff --git a/nextcloud/config/nginx.conf b/nextcloud/config/nginx.conf index 54fb0eb..f58cb8a 100644 --- a/nextcloud/config/nginx.conf +++ b/nextcloud/config/nginx.conf @@ -18,6 +18,11 @@ http { include /etc/nginx/mime.types; default_type application/octet-stream; + + types { + # Add missing types for Nextcloud + application/javascript js mjs; + } log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' @@ -56,13 +61,13 @@ http { gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; # HTTP response headers borrowed from Nextcloud `.htaccess` - add_header Referrer-Policy "no-referrer" always; - add_header X-Content-Type-Options "nosniff" always; - add_header X-Download-Options "noopen" always; - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-Permitted-Cross-Domain-Policies "none" always; - add_header X-Robots-Tag "none" always; - add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "no-referrer" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Download-Options "noopen" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Permitted-Cross-Domain-Policies "none" always; + add_header X-Robots-Tag "noindex,nofollow" always; + add_header X-XSS-Protection "1; mode=block" always; # Remove X-Powered-By, which is an information leak fastcgi_hide_header X-Powered-By; diff --git a/nextcloud/nextcloud-app.container b/nextcloud/nextcloud-app.container index 26d2b49..7956f66 100644 --- a/nextcloud/nextcloud-app.container +++ b/nextcloud/nextcloud-app.container @@ -30,6 +30,10 @@ GIDMap=+82:10000:1 Network=host AddCapability=CAP_NET_BIND_SERVICE +# Allow Nextcloud to talk to itself through the same hostname as used by clients +AddHost=nextcloud:127.0.0.1 +AddHost=collabora:127.0.0.1 + # Environment variables from secrets and config EnvironmentFile=/etc/quadlets/nextcloud/config.env diff --git a/nextcloud/nextcloud-collabora.container b/nextcloud/nextcloud-collabora.container index edafb48..39fa068 100644 --- a/nextcloud/nextcloud-collabora.container +++ b/nextcloud/nextcloud-collabora.container @@ -29,6 +29,10 @@ SeccompProfile=/etc/quadlets/nextcloud/collabora-seccomp-profile.json # Network configuration Network=host +# Collabora needs to connect to Nextcloud through the same hostname as used by clients +AddHost=nextcloud:127.0.0.1 +AddHost=collabora:127.0.0.1 + # Environment variables from secrets and config EnvironmentFile=/etc/quadlets/nextcloud/collabora.env diff --git a/nextcloud/nextcloud-cron.container b/nextcloud/nextcloud-cron.container index bf8fc6a..63c2997 100644 --- a/nextcloud/nextcloud-cron.container +++ b/nextcloud/nextcloud-cron.container @@ -26,6 +26,10 @@ GIDMap=+82:10000:1 # Network configuration Network=host +# Allow Nextcloud to talk to itself through the same hostname as used by clients +AddHost=nextcloud:127.0.0.1 +AddHost=collabora:127.0.0.1 + # Environment variables from config EnvironmentFile=/etc/quadlets/nextcloud/config.env diff --git a/nextcloud/nextcloud-cron.timer b/nextcloud/nextcloud-cron.timer index 19ea3fa..ec5eead 100644 --- a/nextcloud/nextcloud-cron.timer +++ b/nextcloud/nextcloud-cron.timer @@ -4,7 +4,7 @@ Documentation=https://hub.docker.com/_/nextcloud/ PartOf=nextcloud.target [Timer] -OnActiveSec=15min +OnActiveSec=5min RandomizedDelaySec=15s DeferReactivation=true diff --git a/nextcloud/other/traefik-collabora.yaml b/nextcloud/other/traefik-collabora.yaml new file mode 100644 index 0000000..c5438ac --- /dev/null +++ b/nextcloud/other/traefik-collabora.yaml @@ -0,0 +1,13 @@ +http: + routers: + collabora: + rule: "Host(`collabora`)" + entryPoints: + - http + middlewares: + service: "collabora" + services: + collabora: + loadBalancer: + servers: + - url: "http://127.0.0.1:9980" diff --git a/nextcloud/other/traefik-nextcloud.yaml b/nextcloud/other/traefik-nextcloud.yaml new file mode 100644 index 0000000..96d9c31 --- /dev/null +++ b/nextcloud/other/traefik-nextcloud.yaml @@ -0,0 +1,13 @@ +http: + routers: + nextcloud: + rule: "Host(`nextcloud`)" + entryPoints: + - http + middlewares: + service: "nextcloud" + services: + nextcloud: + loadBalancer: + servers: + - url: "http://127.0.0.1:8080" diff --git a/nextcloud/overlay.bu b/nextcloud/overlay.bu new file mode 100644 index 0000000..8d219c4 --- /dev/null +++ b/nextcloud/overlay.bu @@ -0,0 +1,9 @@ +variant: fcos +version: 1.4.0 +passwd: + users: + - name: nextcloud + uid: 10008 + gecos: Nextcloud + home_dir: /var/lib/quadlets/nextcloud + primary_group: itix-svc diff --git a/nginx/nginx-server.container b/nginx/nginx-server.container index 5659e3a..8ae4dfd 100644 --- a/nginx/nginx-server.container +++ b/nginx/nginx-server.container @@ -15,6 +15,7 @@ PartOf=nginx.target [Container] ContainerName=nginx-server Image=docker.io/library/nginx:mainline-alpine +AutoUpdate=registry # Network configuration Network=host diff --git a/postgresql/overlay.bu b/postgresql/overlay.bu new file mode 100644 index 0000000..81accda --- /dev/null +++ b/postgresql/overlay.bu @@ -0,0 +1,9 @@ +variant: fcos +version: 1.4.0 +passwd: + users: + - name: postgresql + uid: 10004 + gecos: PostgreSQL + home_dir: /var/lib/quadlets/postgresql + primary_group: itix-svc diff --git a/postgresql/postgresql-server.container b/postgresql/postgresql-server.container index 7018bde..8fbd867 100644 --- a/postgresql/postgresql-server.container +++ b/postgresql/postgresql-server.container @@ -18,6 +18,7 @@ PartOf=postgresql.target [Container] ContainerName=postgresql-server Image=docker.io/library/postgres:${PG_MAJOR}-alpine +AutoUpdate=registry # Network configuration Network=host diff --git a/traefik/Makefile b/traefik/Makefile new file mode 100644 index 0000000..b7feca3 --- /dev/null +++ b/traefik/Makefile @@ -0,0 +1,23 @@ +## +## Makefile for Traefik quadlet +## + +# Traefik quadlet is mapped to the 10001 user (traefik) and 10000 group (itix-svc) +PROJECT_UID = 10001 +PROJECT_GID = 10000 + +TOP_LEVEL_DIR := .. +include $(TOP_LEVEL_DIR)/Makefile.common + +$(TARGET_CHROOT)/etc/quadlets/traefik/conf.d: + install -m 0755 -o $(PROJECT_UID) -g $(PROJECT_GID) -d $@ + +$(TARGET_CHROOT)/etc/quadlets/traefik/conf.d/itix-middlewares.yaml $(TARGET_CHROOT)/etc/quadlets/traefik/conf.d/ping.yaml: $(TARGET_CHROOT)/etc/quadlets/traefik/conf.d/%: other/% + install -m 0644 -o $(PROJECT_UID) -g $(PROJECT_GID) $< $@ + +$(TARGET_CHROOT)/etc/quadlets/traefik/itix-admins.txt $(TARGET_CHROOT)/etc/quadlets/traefik/itix-users.txt: $(TARGET_CHROOT)/etc/quadlets/traefik/%: other/% + install -m 0600 -o $(PROJECT_UID) -g $(PROJECT_GID) $< $@ + +install-config: $(TARGET_CHROOT)/etc/quadlets/traefik/conf.d + +install-examples: $(TARGET_CHROOT)/etc/quadlets/traefik/conf.d/itix-middlewares.yaml $(TARGET_CHROOT)/etc/quadlets/traefik/itix-admins.txt $(TARGET_CHROOT)/etc/quadlets/traefik/itix-users.txt diff --git a/traefik/config/traefik.yaml b/traefik/config/traefik.yaml new file mode 100644 index 0000000..5e0fc66 --- /dev/null +++ b/traefik/config/traefik.yaml @@ -0,0 +1,34 @@ +api: + dashboard: true + debug: false +ping: + manualRouting: true + +log: + level: "INFO" + +accesslog: false + +global: + sendanonymoususage: false + checknewversion: false + +entryPoints: + http: + address: ":80" + https: + address: ":443" + +certificatesResolvers: + le: + acme: + email: "nicolas.masse@itix.fr" + keyType: "EC384" + httpChallenge: + # used during the challenge + entryPoint: http + storage: "/var/lib/traefik/acme.json" +providers: + file: + directory: /etc/traefik/conf.d/ + watch: true diff --git a/traefik/other/itix-admins.txt b/traefik/other/itix-admins.txt new file mode 100644 index 0000000..e69de29 diff --git a/traefik/other/itix-middlewares.yaml b/traefik/other/itix-middlewares.yaml new file mode 100644 index 0000000..999593d --- /dev/null +++ b/traefik/other/itix-middlewares.yaml @@ -0,0 +1,16 @@ +http: + middlewares: + ## Password hashes can be generated with: + # + # htpasswd -n -B -C 10 + # + itix-admins: + basicAuth: + realm: "ITIX" + headerField: "X-WebAuth-User" + usersFile: "/etc/traefik/itix-admins.txt" + itix-users: + basicAuth: + realm: "ITIX" + headerField: "X-WebAuth-User" + usersFile: "/etc/traefik/itix-users.txt" diff --git a/traefik/other/itix-users.txt b/traefik/other/itix-users.txt new file mode 100644 index 0000000..e69de29 diff --git a/traefik/other/ping.yaml b/traefik/other/ping.yaml new file mode 100644 index 0000000..44ffb33 --- /dev/null +++ b/traefik/other/ping.yaml @@ -0,0 +1,15 @@ +http: + routers: + traefik-ping: + rule: Host(`ping`) + entryPoints: + - http + service: "ping@internal" + middlewares: + - localhost-only + services: {} + middlewares: + localhost-only: + ipAllowList: + sourceRange: + - "127.0.0.1/32" diff --git a/traefik/overlay.bu b/traefik/overlay.bu new file mode 100644 index 0000000..9800ec4 --- /dev/null +++ b/traefik/overlay.bu @@ -0,0 +1,9 @@ +variant: fcos +version: 1.4.0 +passwd: + users: + - name: traefik + uid: 10001 + gecos: Traefik + home_dir: /var/lib/quadlets/traefik + primary_group: itix-svc diff --git a/traefik/traefik.container b/traefik/traefik.container new file mode 100644 index 0000000..83e1f93 --- /dev/null +++ b/traefik/traefik.container @@ -0,0 +1,43 @@ +[Unit] +Description=Traefik reverse proxy +Documentation=https://github.com/traefik/traefik-library-image +After=local-fs.target network.target +Before=traefik.target + +# Start/stop this unit when the target is started/stopped +PartOf=traefik.target + +[Container] +ContainerName=traefik + +# Image +Image=docker.io/library/traefik:v3.4 +AutoUpdate=registry + +# No need for root privileges +User=10001 +Group=10000 +AddCapability=CAP_NET_BIND_SERVICE + +# Storage +Volume=/var/lib/quadlets/traefik:/var/lib/traefik:z +Volume=/etc/quadlets/traefik:/etc/traefik:z + +# Network +Network=host + +# Health check +HealthCmd=wget -q -O /dev/null --header 'Host: ping' http://127.0.0.1/ +HealthInterval=30s +HealthTimeout=10s +HealthStartPeriod=10s +HealthRetries=3 + +[Service] +Restart=always +RestartSec=10 +TimeoutStartSec=120 +TimeoutStopSec=30 + +[Install] +WantedBy=traefik.target diff --git a/traefik/traefik.target b/traefik/traefik.target new file mode 100644 index 0000000..1da4858 --- /dev/null +++ b/traefik/traefik.target @@ -0,0 +1,11 @@ +[Unit] +Description=PostgreSQL Service Target +Documentation=man:systemd.target(5) +Requires=traefik.service +After=traefik.service + +# Allow isolation - can stop/start this target independently +AllowIsolate=yes + +[Install] +WantedBy=multi-user.target