(define-module (tw system lud) #:use-module (gnu) #:use-module (gnu bootloader grub) #:use-module (gnu services) #:use-module (gnu system locale) #:use-module (gnu system nss) #:use-module (guix gexp) #:use-module (tw packages git) #:use-module (tw services dns) #:use-module (tw services games) #:use-module (tw services nextcloud) #:use-module (tw services matrix) #:use-module (tw services media) #:use-module (tw services paperless) #:use-module (tw services restic) #:use-module (tw services secrets) #:use-module (tw services web) #:use-module (tw system)) (use-package-modules acl admin bash certs databases guile-xyz linux man python python-xyz rsync shells tor version-control video) (use-service-modules certbot cgit databases dbus desktop docker file-sharing mcron monitoring networking pm ssh syncthing version-control vpn web) (define efi-system-partition ; /dev/sda1 (uuid "51F3-FB71" 'fat32)) (define guixsd-root-partition ; /dev/sda2 (uuid "c63af3e6-3c2b-43d2-b1e6-944f09a10e0f" 'btrfs)) (define backups-partition ; LABEL="backups" (uuid "8ae64f58-24cd-4309-9553-cc722d3c7d4a" 'btrfs)) (define data-partition ; LABEL="data" (uuid "5556c63c-ba63-42b8-850d-f86460e8759a" 'btrfs)) (define-public %lud-system (operating-system (host-name "lud.twilken.net") (timezone "Europe/Berlin") (locale "en_GB.utf8") (locale-definitions (list (locale-definition (name "en_GB.utf8") (source "en_GB")) (locale-definition (name "de_DE.utf8") (source "de_DE")) (locale-definition (name "fr_FR.utf8") (source "fr_FR")) (locale-definition (name "pt_BR.utf8") (source "pt_BR")) (locale-definition (name "en_US.utf8") (source "en_US")))) ;; Allow resolution of '.local' host names with mDNS. (name-service-switch %mdns-host-lookup-nss) ;; Choose UK English console keyboard layout. (keyboard-layout %british-keyboard) ;; Below is the list of system services. To search for available ;; services, run 'guix system search KEYWORD' in a terminal. (services (append (list (simple-service 'extra-access openssh-service-type `(("ira" ; for Duplicity backups ,(local-file "files/kitchen-pc.pub") ,(local-file "files/wilken-laptop.pub")) ("git" ,(local-file "files/timo.pub") ,(local-file "files/timo-phone-gpg.pub") ,(local-file "files/timo-phone-password-store.pub")))) ;; For video downloader. (service tor-service-type) (simple-service 'video-downloader-packages profile-service-type (list ffmpeg tor)) (service thermald-service-type (thermald-configuration (adaptive? #t))) (service mythic-dynamic-dns-service-type (mythic-dynamic-dns-configuration (host-name host-name) (ipv6? #f))) ; currently broken (service dbus-root-service-type) ; for Docker (service elogind-service-type) ; for Docker (service containerd-service-type) ; for Docker (service docker-service-type) ; for Paperless (service redis-service-type) ; for Paperless (service paperless-service-type (paperless-configuration (domain "paper.wilkenfamily.de") (data-path "/var/data/paperless") (secret-key-file "/etc/paperless/secret-key"))) (service minecraft-server-service-type) ;; Allow anonymous git access via Wireguard, e.g. to this channel's git repo. ;; Repos are only published if they contain a `git-daemon-export-ok' file. (service git-daemon-service-type (git-daemon-configuration (listen (list (server-wireguard-address host-name #:ipv6? #t) (server-wireguard-address host-name))) ;; git://lud.wg/~user/repo.git => /home/user/src/repo.git (user-path "src"))) ;; Serve public-access git repos over HTTPS and private ones over SSH only. (service cgit-service-type (cgit-configuration (branch-sort "age") (root-title "Timo Wilken's Git repositories") (root-desc "Projects written by me, contributed to by me, or \ that I just want to host somewhere.") ;; (root-readme "/srv/git/README.md") ; TODO (repository-directory "/srv/git") (strict-export "git-daemon-export-ok") ; require presence of this file to show repo (remove-suffix? #t) ; remove trailing .git in name and URL (clone-prefix '("https://git.twilken.net" "ssh://git@git.twilken.net:22022/~")) ;; Cgit is a "dumb" git remote, so Guix cannot clone from it. (enable-http-clone? #f) (noplainemail? #t) ; hide email addresses in web interface (enable-index-owner? #f) ; everything is owned by the "git" user (enable-index-links? #t) ; add summary/commit/tree links in index (source-filter (program-file "cgit-source-filter" #~(begin (use-modules (ice-9 popen)) (define pygmentize #$(file-append python-pygments "/bin/pygmentize")) (define file-name (cadr (command-line))) (define pipe (open-pipe* OPEN_READ pygmentize "-N" file-name)) (define lexer (read pipe)) (close-pipe pipe) (display "") (force-output) ; prevent subsequent output from overwriting the tag (apply execl pygmentize "pygmentize" "-f" "html" (if (eq? lexer 'text) (list "-g") (list "-l" (symbol->string lexer))))))) ;; See also /gnu/store/...-cgit-1.2.3/lib/cgit/filters/. (readme ":README.md") (extra-options ; alternative readmes '("readme=:README.org" "readme=:README.html" "readme=:README")) (about-filter (program-file "cgit-about-filter" (with-extensions (list guile-commonmark) #~(begin (use-modules ((ice-9 textual-ports) #:select (get-string-all)) ((commonmark) #:select (commonmark->sxml)) ((sxml simple) #:select (sxml->xml))) (define file-name (cadr (command-line))) (cond ((string-suffix? ".md" file-name) (sxml->xml (commonmark->sxml))) ((string-suffix? ".html" file-name) (display (get-string-all))) (else (sxml->xml `((pre ,(get-string-all)))))))))) (nginx (list (nginx-server-configuration (inherit %cgit-configuration-nginx) (server-name '("cgit.twilken.net")) (listen '("443 ssl http2")) (ssl-certificate "/etc/letsencrypt/live/git.twilken.net/fullchain.pem") (ssl-certificate-key "/etc/letsencrypt/live/git.twilken.net/privkey.pem")) ;; Also run `git-http-backend' to serve git repos over HTTP properly. ;; Cgit can only serve as a "dumb" remote, from which Guix can't pull a channel. (nginx-server-configuration (server-name '("git.twilken.net")) (listen '("443 ssl http2")) (ssl-certificate "/etc/letsencrypt/live/git.twilken.net/fullchain.pem") (ssl-certificate-key "/etc/letsencrypt/live/git.twilken.net/privkey.pem") (locations (list (nginx-location-configuration (inherit (git-http-nginx-location-configuration (git-http-configuration (package git/unsafe-directories) (uri-path "/")))) ;; Fix location URI -- `git-http-nginx-location-configuration' ;; adds a double slash in the beginning if `uri-path' is "/". (uri "~ (/.*)")) ;; Cgit used to be hosted at git.twilken.net. ;; Redirect to the new host only for the homepage. (nginx-location-configuration (uri "= /") (body '("return 301 https://cgit.twilken.net/;")))))))))) (simple-service 'cgit-repo-access activation-service-type #~(system* #$(file-append acl "/bin/setfacl") "-m" "u:fcgiwrap:rx" "/srv/git")) (simple-service 'cgit-certificate certbot-service-type (list (certificate-configuration (domains '("git.twilken.net" "cgit.twilken.net")) (deploy-hook %nginx-cert-deploy-hook)))) (simple-service 'git-backups restic-backup-service-type (list (restic-scheduled-backup (schedule #~"0 3 * * *") (paths '("/srv/git")) (repo (restic-local-repository (path "/var/backups/git"))) (password (restic-password-source (type 'file) (name "/etc/restic/lud-git")))))) (simple-service 'git-backups-cleanup restic-cleanup-service-type (list (restic-scheduled-cleanup (schedule #~"0 4 * * *") (repo (restic-local-repository (path "/var/backups/git"))) (password (restic-password-source (type 'file) (name "/etc/restic/lud-git"))) (keep-daily 30) (keep-monthly -1)))) (simple-service 'git-secrets secrets-service-type (list (secret (encrypted-file (local-file "files/restic/lud-git.enc")) (destination "/etc/restic/lud-git")))) ;; Transmission (torrents) (service transmission-daemon-service-type (transmission-daemon-configuration (download-dir "/var/data/bt") (incomplete-dir "/var/data/bt/incomplete") (incomplete-dir-enabled? #t) (speed-limit-up-enabled? #t) (speed-limit-up 512) ; KiB/s (encryption 'require-encrypted-connections) ;; Don't try to configure port forwarding automatically. (port-forwarding-enabled? #f) ;; Make RPC interface only accessible via WireGuard. (rpc-bind-address (server-wireguard-address host-name)) (rpc-whitelist-enabled? #t) (rpc-whitelist '("127.0.0.1" "::1" "10.0.0.*" "fc00::*")) (rpc-host-whitelist-enabled? #t) (rpc-host-whitelist '("lud.wg")))) ;; TODO: Streama ;; Syncthing (service syncthing-service-type (syncthing-configuration (user "syncthing") (group "syncthing"))) (service yt-dlp-service-type (yt-dlp-configuration (yt-dlp #f) ; use yt-dlp from media-directory (media-directory "/var/data/syncthing/Videos") (user "syncthing"))) (service get-iplayer-service-type (get-iplayer-configuration (config-directory "/var/data/syncthing/Audiobooks/.get_iplayer") (user "syncthing"))) ;; certbot for Synapse + Apache/Nextcloud ;; This also installs a nginx server on port 80, redirecting to port 443. (service certbot-service-type (certbot-configuration (email "letsencrypt@twilken.net"))) ;; For Nextcloud (and Streama) (service mysql-service-type (mysql-configuration (extra-content "\ [mysqld] character-set-server = utf8mb4 collation-server = utf8mb4_general_ci # https://wiki.archlinux.org/title/Nextcloud skip_networking transaction_isolation = READ-COMMITTED # https://docs.nextcloud.com/server/stable/admin_manual/installation/server_tuning.html#using-mariadb-mysql-instead-of-sqlite innodb_buffer_pool_size = 1G innodb_io_capacity = 4000 "))) ;; TODO: JSON exporter (Nextcloud) ;; TODO: Syncthing exporter ;; TODO: Transmission exporter (service secrets-service-type (secrets-configuration (secrets (list (secret (encrypted-file (local-file "files/paperless-secret-key.enc")) (destination "/etc/paperless/secret-key")) (secret (encrypted-file (local-file "files/restic/lud-paperless.enc")) (destination "/etc/restic/lud-paperless")) (secret (encrypted-file (local-file "files/mythic-dns.scm.enc")) (destination "/etc/mythic-dns.scm")) (secret (encrypted-file (local-file "files/nextcloud-database-password.enc")) (destination "/etc/nextcloud-database-password.enc")) (secret (encrypted-file (local-file "files/restic/lud-nextcloud.enc")) (destination "/etc/restic/lud-nextcloud")))))) ;; Only this server has SSDs, not vin. (simple-service 'fstrim mcron-service-type (list #~(job "0 4 * * *" ; after guix gc (string-append #$(file-append util-linux "/sbin/fstrim") " --fstab --verbose"))))) %nextcloud-services %matrix-services (server-base-services host-name))) ;; The list of user accounts ('root' is implicit). (users (cons* (user-account (name "ira") (comment "Ira Wilken") (group "users") (home-directory "/home/ira")) (user-account (system? #t) (name "syncthing") (comment "Syncthing service") (group "syncthing") (home-directory "/var/data/syncthing")) (user-account (system? #t) (name "git") (comment "Git hosting service") (group "git") (home-directory "/srv/git") (shell (file-append git "/bin/git-shell"))) %server-base-user-accounts)) ;; Groups are NOT implict from the user-accounts that mention them. (groups (cons* (user-group (system? #t) (name "syncthing")) (user-group (system? #t) (name "git")) %base-groups)) ;; Use the UEFI variant of GRUB with the EFI System Partition mounted ;; on /boot/efi. (bootloader (bootloader-configuration (bootloader grub-efi-bootloader) (targets '("/boot/efi")) (keyboard-layout keyboard-layout))) ;; The list of file systems that get "mounted". The unique ;; file system identifiers there ("UUIDs") can be obtained ;; by running 'blkid' in a terminal. (file-systems (cons* (file-system (mount-point "/") (device guixsd-root-partition) (flags '(no-atime)) (options (alist->file-system-options '("ssd" ("compress" . "zstd")))) (type "btrfs")) (file-system (mount-point "/boot/efi") (device efi-system-partition) (flags '(no-atime)) (type "vfat")) (file-system (mount-point "/var/backups") (create-mount-point? #t) (device backups-partition) (flags '(no-atime)) (options (alist->file-system-options '("ssd" ("compress" . "zstd")))) (type "btrfs")) (file-system (mount-point "/var/data") (create-mount-point? #t) (device data-partition) (flags '(no-atime)) (options (alist->file-system-options '("ssd" ("compress" . "zstd")))) (type "btrfs")) %base-file-systems)))) %lud-system