From 7044c9b52f9c6b7aa2a006f09198fe98addcfc9d Mon Sep 17 00:00:00 2001 From: Timo Wilken Date: Sat, 18 Feb 2023 00:27:17 +0100 Subject: Extract common service sets into separate modules Common service sets (NextCloud, Matrix, WireGuard) should be in their own modules to make things neater, instead of being interleaved with operating system declarations. --- tw/services.scm | 6 ++ tw/services/files/nextcloud-backup | 68 ++++++++++++++++ tw/services/matrix.scm | 40 +++++++++ tw/services/nextcloud.scm | 125 ++++++++++++++++++++++++++++ tw/services/wireguard.scm | 107 ++++++++++++++++++++++++ tw/system.scm | 126 +++-------------------------- tw/system/files/nextcloud-backup | 68 ---------------- tw/system/lap.scm | 1 + tw/system/lud.scm | 162 +------------------------------------ 9 files changed, 364 insertions(+), 339 deletions(-) create mode 100644 tw/services.scm create mode 100755 tw/services/files/nextcloud-backup create mode 100644 tw/services/matrix.scm create mode 100644 tw/services/nextcloud.scm create mode 100644 tw/services/wireguard.scm delete mode 100755 tw/system/files/nextcloud-backup diff --git a/tw/services.scm b/tw/services.scm new file mode 100644 index 00000000..2ef11298 --- /dev/null +++ b/tw/services.scm @@ -0,0 +1,6 @@ +(define-module (tw services) + #:use-module (guix gexp)) + +(define-public %httpd-cert-deploy-hook + (program-file "httpd-cert-deploy-hook" + #~(kill (call-with-input-file "/var/run/httpd" read) SIGHUP))) diff --git a/tw/services/files/nextcloud-backup b/tw/services/files/nextcloud-backup new file mode 100755 index 00000000..4c533758 --- /dev/null +++ b/tw/services/files/nextcloud-backup @@ -0,0 +1,68 @@ +#!/bin/sh -e +# Nextcloud backup script, to run nightly. +# Documentation on backups: +# https://docs.nextcloud.com/server/latest/admin_manual/maintenance/backup.html +# https://docs.nextcloud.com/server/latest/admin_manual/maintenance/restore.html +# https://git.mdns.eu/nextcloud/passwords/-/wikis/Administrators/Backups + +. /etc/default/nextcloud-backup +: "${DATABASE_PASSWORD:?You must pass the MySQL database password as DATABASE_PASSWORD}" + +php_ini=$1 +backup_dir=/var/backups/nextcloud/$(date -u '+%Y-%m-%d') +nextcloud_dir=/var/www/nextcloud +nextcloud_data_partition=/var/data # mountpoint of the partition containing Nextcloud data dir +nextcloud_data_path=nextcloud # relative to $nextcloud_data_partition +snapshot=$nextcloud_data_partition/tmp-nextcloud-backup + +nc_maintenance () { + # Enable (--on) or disable (--off) Nextcloud's maintenance mode. + sudo -nu httpd php ${php_ini:+-c "$php_ini"} "$nextcloud_dir/occ" maintenance:mode "$@" +} + +# If there is a previous backup, compare against it later (so we don't have to +# transfer every file). +last_backup_dir=$(ls -1d "$(dirname "$backup_dir")"/????-??-?? | LC_ALL=C sort | tail -1) +[ -d "$last_backup_dir" ] || unset last_backup_dir + +# Don't overwrite existing backups. mkdir will fail if $backup_dir exists. +mkdir -m 750 "$backup_dir" + +# Always turn off maintenance mode and clean up the temporary snapshot on exit, +# whether or not the backup succeeded. +cleanup () { + nc_maintenance --off || : + if [ -d "$snapshot" ]; then + btrfs subvolume delete -c "$snapshot" || : + fi +} +trap cleanup EXIT HUP INT TERM # can't trap KILL + +# Turn Nextcloud off temporarily so the data doesn't change during the backup. +nc_maintenance --on + +# Backup the database. This can only be done offline. +mysqldump --single-transaction --default-character-set=utf8mb4 \ + -u nextcloud -p"$DATABASE_PASSWORD" nextcloud > "$backup_dir/nextcloud.sql" + +# These shouldn't be copied while Nextcloud is online. +rsync -AUXHavx ${last_backup_dir+--link-dest="$last_backup_dir"} \ + "$nextcloud_dir/config" "$nextcloud_dir/themes" "$backup_dir" + +# Make sure everything is synced to disk so it's in our snapshot. +btrfs filesystem sync "$nextcloud_data_partition/$nextcloud_data_path" +btrfs subvolume snapshot -r "$nextcloud_data_partition" "$snapshot" + +# At this point, the data directory is in the snapshot, so Nextcloud can be +# turned on again. +nc_maintenance --off + +# --link-dest is brittle (it only hardlinks to the old file if no metadata has +# changed). Reflinks would be better, but rsync doesn't seem to support them. +# We don't need files under preview/, as those are thumbnails from the Previews +# "app" and can be regenerated using `php -f occ preview:pre-generate`. +rsync -AUXHavx --exclude='appdata_*/preview' --exclude='appdata_*/passwords/*Cache' \ + ${last_backup_dir+--link-dest="$last_backup_dir/data"} \ + "$snapshot/$nextcloud_data_path/" "$backup_dir/data" +# Make sure everything is written out to the backup disk before we exit. +btrfs filesystem sync "$backup_dir" diff --git a/tw/services/matrix.scm b/tw/services/matrix.scm new file mode 100644 index 00000000..db21f172 --- /dev/null +++ b/tw/services/matrix.scm @@ -0,0 +1,40 @@ +(define-module (tw services matrix) + #:use-module (gnu services) + #:use-module (gnu services certbot) + #:use-module (gnu services web) + #:use-module (tw services)) + +(define-public %matrix-services + (list (simple-service 'synapse-certificates certbot-service-type + (list (certificate-configuration + (domains '("matrix.twilken.net")) + (deploy-hook %httpd-cert-deploy-hook)))) + + (simple-service 'synapse-https-proxy httpd-service-type + ;; Synapse can't access certbot certs, but Apache/httpd + ;; can, so proxy HTTPS access through. It's good to have + ;; Synapse available on port 443 anyway. + (list (httpd-virtualhost "*:443" (list "\ +# Redirect to Synapse, to avoid having to specify its port number in Matrix clients. +ServerName matrix.twilken.net +SSLEngine on +SSLCertificateFile \"/etc/letsencrypt/live/matrix.twilken.net/fullchain.pem\" +SSLCertificateKeyFile \"/etc/letsencrypt/live/matrix.twilken.net/privkey.pem\" +ProxyPass \"/\" \"https://127.0.0.1:48448/\" +")))) + + ;; TODO: Postgres for Synapse + ;; (service postgresql-service-type + ;; (postgresql-configuration + ;; (postgresql postgresql-15) + ;; (data-directory "/var/lib/postgresql/data"))) + + ;; (service postgresql-role-service-type + ;; (postgresql-role-configuration + ;; (roles (list (postgresql-role + ;; (name "synapse") ; TODO + ;; (create-database? #t)))))) + + ;; TODO: Matrix/Synapse + ;; TODO: Matrix bridges + )) diff --git a/tw/services/nextcloud.scm b/tw/services/nextcloud.scm new file mode 100644 index 00000000..ca68cf77 --- /dev/null +++ b/tw/services/nextcloud.scm @@ -0,0 +1,125 @@ +(define-module (tw services nextcloud) + #:use-module (gnu) + #:use-module (gnu packages php) + #:use-module (gnu services certbot) + #:use-module (gnu services mcron) + #:use-module (gnu services web) + #:use-module (guix gexp) + #:use-module (tw services)) + +(define-public %nextcloud-php.ini + (computed-file "nextcloud-php.ini" + #~(begin + (use-modules (ice-9 popen) (ice-9 rdelim)) + (let* ((php-config #$(file-append php "/bin/php-config")) + (pipe (open-pipe* OPEN_READ php-config "--extension-dir")) + (php-extdir (read-line pipe))) + (unless (zero? (status:exit-val (close-pipe pipe))) + (error "Failed to get PHP extension dir")) + (with-output-to-file #$output + ;; Guix's PHP comes with the following extensions built-in, + ;; so no extension= line necessary: + ;; pdo_mysql, bcmath, bz2, exif, gd, iconv, intl + (lambda () (display (string-append "\ +memory_limit=512M +extension_dir=/run/current-system/profile/lib/php/extensions/" (basename php-extdir) " +; Caching extensions for Nextcloud +extension=apcu +apc.enable_cli=1 +zend_extension=opcache +; https://www.php.net/manual/en/opcache.configuration.php +opcache.enable=1 +opcache.interned_strings_buffer=32 +opcache.max_accelerated_files=10000 +opcache.memory_consumption=128 +opcache.save_comments=1 +; It will take up to revalidate_freq seconds for changes to config.php to be applied. +opcache.revalidate_freq=120 +")))))))) + +(define-public %nextcloud-services + (list (simple-service 'nextcloud-https-server httpd-service-type + ;; The certbot service redirects everything on port 80 to + ;; port 443 by default, modulo its own /.well-known paths. + (list (httpd-virtualhost "*:443" (list "\ +# For Nextcloud. +ServerName cloud.wilkenfamily.de +DocumentRoot /var/www/nextcloud +SSLEngine on +SSLCertificateFile \"/etc/letsencrypt/live/cloud.wilkenfamily.de/fullchain.pem\" +SSLCertificateKeyFile \"/etc/letsencrypt/live/cloud.wilkenfamily.de/privkey.pem\" +Header always set Strict-Transport-Security \"max-age=15552000\" + +# Don't check for .htaccess files above DocumentRoot. + + AllowOverride None + + + + Options +FollowSymlinks + AllowOverride All + + Dav off + + SetEnv HOME /var/www/nextcloud + SetEnv HTTP_HOME /var/www/nextcloud + + +# Redirect to local php-fpm if mod_php is not available + + + # Enable http authorization headers + + SetEnvIfNoCase ^Authorization$ \"(.+)\" HTTP_AUTHORIZATION=$1 + + + + SetHandler \"proxy:unix:/var/run/php-fpm.sock|fcgi://localhost/\" + + + # Deny access to raw PHP sources and files without filename (e.g. '.php') + + Require all denied + + + +")))) + + (service php-fpm-service-type + (php-fpm-configuration + (user "httpd") + (group "httpd") + (socket "/var/run/php-fpm.sock") + (socket-user "httpd") + (socket-group "httpd") + (php-ini-file %nextcloud-php.ini))) + + (simple-service 'nextcloud-certificates certbot-service-type + (list (certificate-configuration + (domains '("cloud.wilkenfamily.de")) + (deploy-hook %httpd-cert-deploy-hook)))) + + ;; Nextcloud cron + (simple-service 'nextcloud-cron mcron-service-type + (list #~(job "*/5 * * * *" + (lambda () + (chdir "/var/www/nextcloud") + ;; `setgid' first while we're still root + (setgid (group:gid (getgr "httpd"))) + (setuid (passwd:uid (getpw "httpd"))) + (execl #$(file-append php "/bin/php") "php" + "-c" #$%nextcloud-php.ini "cron.php")) + (string-append + #$(file-append php "/bin/php") + " -c " #$%nextcloud-php.ini + " /var/www/nextcloud/cron.php")) + + ;; Nextcloud backups + ;; Requires: sudo, php, btrfs, mysqldump, rsync + (let ((backup-script (local-file "files/nextcloud-backup" #:recursive? #t))) + #~(job "0 6 * * *" + (lambda () + ;; Pass through the php.ini file that allows us to + ;; use Nextcloud's occ script. + (execl #$backup-script "nextcloud-backup" #$%nextcloud-php.ini)) + (string-append #$backup-script " " #$%nextcloud-php.ini))))))) diff --git a/tw/services/wireguard.scm b/tw/services/wireguard.scm new file mode 100644 index 00000000..3d35cd2e --- /dev/null +++ b/tw/services/wireguard.scm @@ -0,0 +1,107 @@ +(define-module (tw services wireguard) + #:use-module (ice-9 regex) + #:use-module ((srfi srfi-1) #:select (append-map every)) + #:use-module ((srfi srfi-26) #:select (cut)) + #:use-module (gnu services) + #:use-module (gnu services base) + #:use-module (gnu services configuration) + #:use-module (gnu services vpn) + #:export (%wireguard-peers + tw-wireguard-configuration + tw-wireguard-service-type)) + +(define %wireguard-peers + `(("lap.twilken.net" . + ,(wireguard-peer + (name "lap.wg") + (public-key "lap/DvCb8xXLUCqcaPEx8kCRcoeV4ScTMVZW5hvvNzA=") + (preshared-key "/etc/wireguard/lap.psk") + (allowed-ips '("10.0.0.1/32" "fc00::1/128")))) + ("lud.twilken.net" . + ,(wireguard-peer + (name "lud.wg") + (endpoint "lud.twilken.net:58921") + (public-key "lud/9sbXVdOYXxOkRgAB+b/17QxbwllfJY/pbA3/MkE=") + (preshared-key "/etc/wireguard/lud.psk") + (allowed-ips '("10.0.0.2/32" "fc00::2/128")))) + ("vin.twilken.net" . + ,(wireguard-peer + (name "vin.wg") + (endpoint "vin.twilken.net:58921") + (public-key "vin/Im+sOszZFE01UF1+QlyxLP1PsPXJgTz4KmgvL3Y=") + (preshared-key "/etc/wireguard/vin.psk") + (allowed-ips '("10.0.0.3/32" "fc00::3/128")))) + ("fp4.twilken.net" . + ,(wireguard-peer + (name "fp4.wg") + (public-key "fp4/aLAVBADTy+UGmNh011w1CFOOwq70Df6EWlZRkAs=") + (preshared-key "/etc/wireguard/fp4.psk") + (allowed-ips '("10.0.0.4/32" "fc00::4/128")))) + ("pi3.twilken.net" . + ,(wireguard-peer + (name "pi3.wg") + (endpoint "pi3.twilken.net:58922") + (public-key "pi3/ThUH4qDTuyvNQIiiyy2dbziF/xLRTwO0+vcUoVY=") + (preshared-key "/etc/wireguard/pi3.psk") + (allowed-ips '("10.0.0.5/32" "fc00::5/128")))))) + +(define (wireguard-peers-list? object) + (and (list? object) + (every (compose string? car) object) + (every (compose wireguard-peer? cdr) object))) + +(define-configuration/no-serialization tw-wireguard-configuration + (this-host + (string) + "The host name of the machine being configured.") + (peers + (wireguard-peers-list %wireguard-peers) + "An alist of WireGuard peers to install.")) + +(define (tw-wireguard-service config) + "Create a full WireGuard config from the personal network CONFIG." + (let ((own-peer (assoc-ref (tw-wireguard-configuration-peers config) + (tw-wireguard-configuration-this-host config)))) + (wireguard-configuration + (addresses + (map (lambda (cidr) + (let ((ipv4 (string-match "/32$" cidr)) + (ipv6 (string-match "/128$" cidr))) + (cond + (ipv4 (regexp-substitute #f ipv4 'pre "/24")) + (ipv6 (regexp-substitute #f ipv6 'pre "/64")) + (#t cidr)))) + (wireguard-peer-allowed-ips own-peer))) + (port + (let ((endpoint (wireguard-peer-endpoint own-peer))) + (if endpoint + (string->number (cadr (string-split endpoint #\:))) + 58921))) + (private-key "/etc/wireguard/private.key") + (peers (delq own-peer (map cdr (tw-wireguard-configuration-peers config))))))) + +(define (peer->ips peer) + "Extract IP addresses assigned to the given `wireguard-peer' PEER." + (map (compose car (cut string-split <> #\/)) + (wireguard-peer-allowed-ips peer))) + +(define (tw-wireguard-hosts config) + "Generate a hosts file entries from the personal WireGuard network CONFIG." + (append-map (lambda (peer) + (map (cut host <> (wireguard-peer-name peer)) + (peer->ips peer))) + (map cdr (tw-wireguard-configuration-peers config)))) + +(define tw-wireguard-service-type + (service-type + (name 'tw-wireguard) + (description "Set up my personal WireGuard network.") + (extensions + (cons* (service-extension hosts-service-type tw-wireguard-hosts) + ;; FIXME: `wireguard-service-type' cannot be extended, so copy its + ;; service-extensions directly. + (map (lambda (ext) + (service-extension (service-extension-target ext) + (compose (service-extension-compute ext) + tw-wireguard-service))) + (service-type-extensions wireguard-service-type)))))) diff --git a/tw/system.scm b/tw/system.scm index 92eadeba..c9904e24 100644 --- a/tw/system.scm +++ b/tw/system.scm @@ -1,18 +1,15 @@ (define-module (tw system) - #:use-module (ice-9 format) - #:use-module (ice-9 regex) #:use-module (ice-9 string-fun) - #:use-module ((srfi srfi-1) #:select (append-map every)) - #:use-module ((srfi srfi-26) #:select (cut)) #:use-module (gnu) #:use-module (gnu services) #:use-module (gnu system) #:use-module (gnu system keyboard) - #:use-module (guix gexp)) + #:use-module (guix gexp) + #:use-module (tw services wireguard)) (use-package-modules admin avahi certs curl disk file-systems linux lsof man moreutils python rsync search shells version-control vpn) -(use-service-modules configuration mcron monitoring networking ssh vpn) +(use-service-modules mcron monitoring networking ssh vpn) (define-public %base-system-packages (cons* acpi btrfs-progs cpupower curl efibootmgr exfat-utils git glibc-locales @@ -29,6 +26,16 @@ "keypad:oss" "kpdl:kposs"))) +(define-public %server-base-user-accounts + (cons* (user-account + (name "timo") + (comment "Timo Wilken") + (group "users") + (home-directory "/home/timo") + (supplementary-groups '("wheel" "netdev" "audio" "video")) + (shell (file-append zsh "/bin/zsh"))) + %base-user-accounts)) + ;; This is used for the servers, and also by (tw home) to generate the ;; appropriate ~/.ssh/config. (define-public %ssh-ports @@ -83,110 +90,3 @@ (inherit config) (motd (plain-file "no-motd" "")) (allow-empty-passwords? #f)))))) - -(define-public %server-base-user-accounts - (cons* (user-account - (name "timo") - (comment "Timo Wilken") - (group "users") - (home-directory "/home/timo") - (supplementary-groups '("wheel" "netdev" "audio" "video")) - (shell (file-append zsh "/bin/zsh"))) - %base-user-accounts)) - -(define-public %wireguard-peers - `(("lap.twilken.net" . - ,(wireguard-peer - (name "lap.wg") - (public-key "lap/DvCb8xXLUCqcaPEx8kCRcoeV4ScTMVZW5hvvNzA=") - (preshared-key "/etc/wireguard/lap.psk") - (allowed-ips '("10.0.0.1/32" "fc00::1/128")))) - ("lud.twilken.net" . - ,(wireguard-peer - (name "lud.wg") - (endpoint "lud.twilken.net:58921") - (public-key "lud/9sbXVdOYXxOkRgAB+b/17QxbwllfJY/pbA3/MkE=") - (preshared-key "/etc/wireguard/lud.psk") - (allowed-ips '("10.0.0.2/32" "fc00::2/128")))) - ("vin.twilken.net" . - ,(wireguard-peer - (name "vin.wg") - (endpoint "vin.twilken.net:58921") - (public-key "vin/Im+sOszZFE01UF1+QlyxLP1PsPXJgTz4KmgvL3Y=") - (preshared-key "/etc/wireguard/vin.psk") - (allowed-ips '("10.0.0.3/32" "fc00::3/128")))) - ("fp4.twilken.net" . - ,(wireguard-peer - (name "fp4.wg") - (public-key "fp4/aLAVBADTy+UGmNh011w1CFOOwq70Df6EWlZRkAs=") - (preshared-key "/etc/wireguard/fp4.psk") - (allowed-ips '("10.0.0.4/32" "fc00::4/128")))) - ("pi3.twilken.net" . - ,(wireguard-peer - (name "pi3.wg") - (endpoint "pi3.twilken.net:58922") - (public-key "pi3/ThUH4qDTuyvNQIiiyy2dbziF/xLRTwO0+vcUoVY=") - (preshared-key "/etc/wireguard/pi3.psk") - (allowed-ips '("10.0.0.5/32" "fc00::5/128")))))) - -(define (wireguard-peers-list? object) - (and (list? object) - (every (compose string? car) object) - (every (compose wireguard-peer? cdr) object))) - -(export tw-wireguard-configuration) -(define-configuration/no-serialization tw-wireguard-configuration - (this-host - (string) - "The host name of the machine being configured.") - (peers - (wireguard-peers-list %wireguard-peers) - "An alist of WireGuard peers to install.")) - -(define (tw-wireguard-service config) - "Create a full WireGuard config from the personal network CONFIG." - (let ((own-peer (assoc-ref (tw-wireguard-configuration-peers config) - (tw-wireguard-configuration-this-host config)))) - (wireguard-configuration - (addresses - (map (lambda (cidr) - (let ((ipv4 (string-match "/32$" cidr)) - (ipv6 (string-match "/128$" cidr))) - (cond - (ipv4 (regexp-substitute #f ipv4 'pre "/24")) - (ipv6 (regexp-substitute #f ipv6 'pre "/64")) - (#t cidr)))) - (wireguard-peer-allowed-ips own-peer))) - (port - (let ((endpoint (wireguard-peer-endpoint own-peer))) - (if endpoint - (string->number (cadr (string-split endpoint #\:))) - 58921))) - (private-key "/etc/wireguard/private.key") - (peers (delq own-peer (map cdr (tw-wireguard-configuration-peers config))))))) - -(define (peer->ips peer) - "Extract IP addresses assigned to the given `wireguard-peer' PEER." - (map (compose car (cut string-split <> #\/)) - (wireguard-peer-allowed-ips peer))) - -(define (tw-wireguard-hosts config) - "Generate a hosts file entries from the personal WireGuard network CONFIG." - (append-map (lambda (peer) - (map (cut host <> (wireguard-peer-name peer)) - (peer->ips peer))) - (map cdr (tw-wireguard-configuration-peers config)))) - -(define-public tw-wireguard-service-type - (service-type - (name 'tw-wireguard) - (description "Set up my personal WireGuard network.") - (extensions - (cons* (service-extension hosts-service-type tw-wireguard-hosts) - ;; FIXME: `wireguard-service-type' cannot be extended, so copy its - ;; service-extensions directly. - (map (lambda (ext) - (service-extension (service-extension-target ext) - (compose (service-extension-compute ext) - tw-wireguard-service))) - (service-type-extensions wireguard-service-type)))))) diff --git a/tw/system/files/nextcloud-backup b/tw/system/files/nextcloud-backup deleted file mode 100755 index 4c533758..00000000 --- a/tw/system/files/nextcloud-backup +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/sh -e -# Nextcloud backup script, to run nightly. -# Documentation on backups: -# https://docs.nextcloud.com/server/latest/admin_manual/maintenance/backup.html -# https://docs.nextcloud.com/server/latest/admin_manual/maintenance/restore.html -# https://git.mdns.eu/nextcloud/passwords/-/wikis/Administrators/Backups - -. /etc/default/nextcloud-backup -: "${DATABASE_PASSWORD:?You must pass the MySQL database password as DATABASE_PASSWORD}" - -php_ini=$1 -backup_dir=/var/backups/nextcloud/$(date -u '+%Y-%m-%d') -nextcloud_dir=/var/www/nextcloud -nextcloud_data_partition=/var/data # mountpoint of the partition containing Nextcloud data dir -nextcloud_data_path=nextcloud # relative to $nextcloud_data_partition -snapshot=$nextcloud_data_partition/tmp-nextcloud-backup - -nc_maintenance () { - # Enable (--on) or disable (--off) Nextcloud's maintenance mode. - sudo -nu httpd php ${php_ini:+-c "$php_ini"} "$nextcloud_dir/occ" maintenance:mode "$@" -} - -# If there is a previous backup, compare against it later (so we don't have to -# transfer every file). -last_backup_dir=$(ls -1d "$(dirname "$backup_dir")"/????-??-?? | LC_ALL=C sort | tail -1) -[ -d "$last_backup_dir" ] || unset last_backup_dir - -# Don't overwrite existing backups. mkdir will fail if $backup_dir exists. -mkdir -m 750 "$backup_dir" - -# Always turn off maintenance mode and clean up the temporary snapshot on exit, -# whether or not the backup succeeded. -cleanup () { - nc_maintenance --off || : - if [ -d "$snapshot" ]; then - btrfs subvolume delete -c "$snapshot" || : - fi -} -trap cleanup EXIT HUP INT TERM # can't trap KILL - -# Turn Nextcloud off temporarily so the data doesn't change during the backup. -nc_maintenance --on - -# Backup the database. This can only be done offline. -mysqldump --single-transaction --default-character-set=utf8mb4 \ - -u nextcloud -p"$DATABASE_PASSWORD" nextcloud > "$backup_dir/nextcloud.sql" - -# These shouldn't be copied while Nextcloud is online. -rsync -AUXHavx ${last_backup_dir+--link-dest="$last_backup_dir"} \ - "$nextcloud_dir/config" "$nextcloud_dir/themes" "$backup_dir" - -# Make sure everything is synced to disk so it's in our snapshot. -btrfs filesystem sync "$nextcloud_data_partition/$nextcloud_data_path" -btrfs subvolume snapshot -r "$nextcloud_data_partition" "$snapshot" - -# At this point, the data directory is in the snapshot, so Nextcloud can be -# turned on again. -nc_maintenance --off - -# --link-dest is brittle (it only hardlinks to the old file if no metadata has -# changed). Reflinks would be better, but rsync doesn't seem to support them. -# We don't need files under preview/, as those are thumbnails from the Previews -# "app" and can be regenerated using `php -f occ preview:pre-generate`. -rsync -AUXHavx --exclude='appdata_*/preview' --exclude='appdata_*/passwords/*Cache' \ - ${last_backup_dir+--link-dest="$last_backup_dir/data"} \ - "$snapshot/$nextcloud_data_path/" "$backup_dir/data" -# Make sure everything is written out to the backup disk before we exit. -btrfs filesystem sync "$backup_dir" diff --git a/tw/system/lap.scm b/tw/system/lap.scm index d9793ab7..efbe19f7 100644 --- a/tw/system/lap.scm +++ b/tw/system/lap.scm @@ -17,6 +17,7 @@ #:use-module (nongnu packages scanner) #:use-module (nongnu system linux-initrd) #:use-module (nonguix licenses) + #:use-module (tw services wireguard) #:use-module (tw system)) (use-package-modules android certs cups disk docker file-systems gnome diff --git a/tw/system/lud.scm b/tw/system/lud.scm index 9986c30a..b770fc90 100644 --- a/tw/system/lud.scm +++ b/tw/system/lud.scm @@ -5,6 +5,8 @@ #:use-module (gnu system nss) #:use-module (guix gexp) #:use-module (tw packages php) + #:use-module (tw services nextcloud) + #:use-module (tw services matrix) #:use-module (tw system)) (use-package-modules admin bash certs databases linux man php python rsync @@ -21,40 +23,6 @@ (define data-partition ; /dev/sdc1 (uuid "4715ae0e-5cef-48f2-a59e-025321153888" 'btrfs)) -(define httpd-cert-deploy-hook - (program-file "httpd-cert-deploy-hook" - #~(kill (call-with-input-file "/var/run/httpd" read) SIGHUP))) - -(define nextcloud-php.ini - (computed-file "nextcloud-php.ini" - #~(begin - (use-modules (ice-9 popen) (ice-9 rdelim)) - (let* ((php-config #$(file-append php "/bin/php-config")) - (pipe (open-pipe* OPEN_READ php-config "--extension-dir")) - (php-extdir (read-line pipe))) - (unless (zero? (status:exit-val (close-pipe pipe))) - (error "Failed to get PHP extension dir")) - (with-output-to-file #$output - ;; Guix's PHP comes with the following extensions built-in, - ;; so no extension= line necessary: - ;; pdo_mysql, bcmath, bz2, exif, gd, iconv, intl - (lambda () (display (string-append "\ -memory_limit=512M -extension_dir=/run/current-system/profile/lib/php/extensions/" (basename php-extdir) " -; Caching extensions for Nextcloud -extension=apcu -apc.enable_cli=1 -zend_extension=opcache -; https://www.php.net/manual/en/opcache.configuration.php -opcache.enable=1 -opcache.interned_strings_buffer=32 -opcache.max_accelerated_files=10000 -opcache.memory_consumption=128 -opcache.save_comments=1 -; It will take up to revalidate_freq seconds for changes to config.php to be applied. -opcache.revalidate_freq=120 -")))))))) - (define httpd-intermediate-ssl-config "\ # SSL configuration. # https://ssl-config.mozilla.org/#server=apache&version=2.4.53&config=intermediate&openssl=1.1.1n&ocsp=false&guideline=5.6 @@ -70,128 +38,6 @@ SSLSessionCache \"shmcb:logs/ssl_scache(65535)\" SSLSessionCacheTimeout 1200 ") -(define nextcloud-services - (list (simple-service 'nextcloud-https-server httpd-service-type - ;; The certbot service redirects everything on port 80 to - ;; port 443 by default, modulo its own /.well-known paths. - (list (httpd-virtualhost "*:443" (list "\ -# For Nextcloud. -ServerName cloud.wilkenfamily.de -DocumentRoot /var/www/nextcloud -SSLEngine on -SSLCertificateFile \"/etc/letsencrypt/live/cloud.wilkenfamily.de/fullchain.pem\" -SSLCertificateKeyFile \"/etc/letsencrypt/live/cloud.wilkenfamily.de/privkey.pem\" -Header always set Strict-Transport-Security \"max-age=15552000\" - -# Don't check for .htaccess files above DocumentRoot. - - AllowOverride None - - - - Options +FollowSymlinks - AllowOverride All - - Dav off - - SetEnv HOME /var/www/nextcloud - SetEnv HTTP_HOME /var/www/nextcloud - - -# Redirect to local php-fpm if mod_php is not available - - - # Enable http authorization headers - - SetEnvIfNoCase ^Authorization$ \"(.+)\" HTTP_AUTHORIZATION=$1 - - - - SetHandler \"proxy:unix:/var/run/php-fpm.sock|fcgi://localhost/\" - - - # Deny access to raw PHP sources and files without filename (e.g. '.php') - - Require all denied - - - -")))) - - (service php-fpm-service-type - (php-fpm-configuration - (user "httpd") - (group "httpd") - (socket "/var/run/php-fpm.sock") - (socket-user "httpd") - (socket-group "httpd") - (php-ini-file nextcloud-php.ini))) - - (simple-service 'nextcloud-certificates certbot-service-type - (list (certificate-configuration - (domains '("cloud.wilkenfamily.de")) - (deploy-hook httpd-cert-deploy-hook)))) - - ;; Nextcloud cron - (simple-service 'nextcloud-cron mcron-service-type - (list #~(job "*/5 * * * *" - (lambda () - (chdir "/var/www/nextcloud") - ;; `setgid' first while we're still root - (setgid (group:gid (getgr "httpd"))) - (setuid (passwd:uid (getpw "httpd"))) - (execl #$(file-append php "/bin/php") "php" - "-c" #$nextcloud-php.ini "cron.php")) - (string-append - #$(file-append php "/bin/php") - " -c " #$nextcloud-php.ini - " /var/www/nextcloud/cron.php")) - - ;; Nextcloud backups - ;; Requires: sudo, php, btrfs, mysqldump, rsync - (let ((backup-script (local-file "files/nextcloud-backup" #:recursive? #t))) - #~(job "0 6 * * *" - (lambda () - ;; Pass through the php.ini file that allows us to - ;; use Nextcloud's occ script. - (execl #$backup-script "nextcloud-backup" #$nextcloud-php.ini)) - (string-append #$backup-script " " #$nextcloud-php.ini))))))) - -(define matrix-services - (list (simple-service 'synapse-certificates certbot-service-type - (list (certificate-configuration - (domains '("matrix.twilken.net")) - (deploy-hook httpd-cert-deploy-hook)))) - - (simple-service 'synapse-https-proxy httpd-service-type - ;; Synapse can't access certbot certs, but Apache/httpd - ;; can, so proxy HTTPS access through. It's good to have - ;; Synapse available on port 443 anyway. - (list (httpd-virtualhost "*:443" (list "\ -# Redirect to Synapse, to avoid having to specify its port number in Matrix clients. -ServerName matrix.twilken.net -SSLEngine on -SSLCertificateFile \"/etc/letsencrypt/live/matrix.twilken.net/fullchain.pem\" -SSLCertificateKeyFile \"/etc/letsencrypt/live/matrix.twilken.net/privkey.pem\" -ProxyPass \"/\" \"https://127.0.0.1:48448/\" -")))) - - ;; TODO: Postgres for Synapse - ;; (service postgresql-service-type - ;; (postgresql-configuration - ;; (postgresql postgresql-15) - ;; (data-directory "/var/lib/postgresql/data"))) - - ;; (service postgresql-role-service-type - ;; (postgresql-role-configuration - ;; (roles (list (postgresql-role - ;; (name "synapse") ; TODO - ;; (create-database? #t)))))) - - ;; TODO: Matrix/Synapse - ;; TODO: Matrix bridges - )) - (define-public %lud-system (operating-system (host-name "lud.twilken.net") @@ -313,8 +159,8 @@ innodb_io_capacity = 4000 ;; TODO: Transmission exporter ) - nextcloud-services - matrix-services + %nextcloud-services + %matrix-services (server-base-services host-name))) ;; The list of user accounts ('root' is implicit). -- cgit v1.2.3