;; SPDX-License-Identifier: CC0-1.0 ;; Copyright (C) 2022-2024 W. Kosior ;; ;; Available under the terms of Creative Commons Zero v1.0 Universal. (use-modules ((srfi srfi-1) #:select (append-map)) ((srfi srfi-26) #:select (cut)) ((ice-9 format) #:select (format)) ((ice-9 match) #:select (match-lambda)) ((gnu bootloader) #:select (bootloader-configuration)) ((gnu bootloader grub) #:select (grub-bootloader)) ((gnu packages) #:select (specifications->packages)) ((gnu packages admin) #:select (shadow)) ((gnu packages dns) #:select (knot-resolver)) ((gnu packages koszko-services) #:prefix ks:) ((gnu packages python) #:select (guix-pythonpath-search-path)) ((gnu packages web) #:select (httpd mod-wsgi)) ((gnu packages version-control) #:select (git cgit)) ((gnu services) #:select (activation-service-type service simple-service)) ((gnu services base) #:select (guix-service-type guix-extension %base-services)) ((gnu services ssh) #:select (openssh-service-type openssh-configuration)) ((gnu services networking) #:select (dhcp-client-service-type)) ((gnu services overlayfs) #:select (overlayfs-service-type overlayfs-mount-configuration)) ((gnu services web) #:prefix web:) ((gnu services cgit) #:select (cgit-configuration serialize-cgit-configuration)) ((gnu services certbot) #:prefix cb:) ((gnu services dns) #:prefix dns:) ((gnu services mail) #:prefix mail:) ((gnu services version-control) #:prefix vc:) ((gnu services databases) #:select (postgresql-service-type postgresql-configuration)) ((gnu system) #:select (%base-packages operating-system)) ((gnu system file-systems) #:prefix fs:) ((gnu system keyboard) #:select (keyboard-layout)) ((gnu system shadow) #:prefix gss:) ((guix build-system python) #:select (default-python)) ((guix gexp) #:select (file-append gexp local-file mixed-text-file plain-file program-file scheme-file)) ((guix modules) #:select (source-module-closure)) ((guix packages) #:select (package-transitive-target-inputs package-version)) ((guix search-paths) #:select (search-path-specification->sexp))) (define %services %base-services) (define-syntax-rule (prepend list item) (define list (cons item list))) (prepend %services (simple-service 'always-forbid-root-login activation-service-type #~(system "/run/setuid-programs/passwd -l root > /dev/null"))) (prepend %services (service dhcp-client-service-type)) (prepend %services (simple-service 'guix-authorize-key guix-service-type (guix-extension (authorized-keys (list (local-file "guix-signing-key.pub")))))) (prepend %services (service web:httpd-service-type (web:httpd-configuration (config (web:httpd-config-file (server-name "koszko.org") (listen '("80" "443")) (error-log "/var/log/httpd/error.log") (modules (append (map (lambda (name) (let ((filename (format #f "/modules/mod_~a.so" name))) (web:httpd-module (name (string-append name "_module")) (file (file-append httpd filename))))) (list "cgid" "env" "logio" "rewrite" "ssl" "proxy" "proxy_http" "headers")) (list (web:httpd-module (name "wsgi_module") (file (file-append mod-wsgi "/modules/mod_wsgi.so")))) web:%default-httpd-modules)) (extra-config (list "\ LogFormat \"%>s %u %t \\\"%r\\\" %I in %O out \ \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\"\" combined CustomLog /var/log/httpd/access.log combined ScriptSock /var/run/cgid.sock "))))))) (prepend %services (simple-service 'acme-challenge-http-virtualhost web:httpd-service-type (map (match-lambda ((main-domain . other-domains) (web:httpd-virtualhost "*:80" `("\ ServerName " ,main-domain " " ,(format #f "~{ServerAlias ~a~^~%~}" other-domains) " ServerAdmin webmaster@koszko.org Redirect permanent / http://" ,main-domain "/ # # Redirect permanent / https://" ,main-domain "/ # DocumentRoot /var/empty Alias /.well-known/acme-challenge \ /var/certbot-validation/.well-known/acme-challenge ")))) '(("koszko.org" "www.koszko.org" "koszkonutek-tmp.pl.eu.org" "www.koszkonutek-tmp.pl.eu.org") ("git.koszko.org" "www.git.koszko.org" "git.koszkonutek-tmp.pl.eu.org" "www.git.koszkonutek-tmp.pl.eu.org") ("haketilo.koszko.org" "www.haketilo.koszko.org") ("hydrilla.koszko.org") ("hydrillabugs.koszko.org" "www.hydrillabugs.koszko.org" "hachettebugs.koszko.org" "www.hachettebugs.koszko.org") ("imap.koszko.org") ("pray.koszko.org" "www.pray.koszko.org") ("sheets.koszko.org" "www.sheets.koszko.org") ("smtp.koszko.org"))))) (define %cgitrc (serialize-cgit-configuration (cgit-configuration (about-filter (file-append cgit "/lib/cgit/filters/about-formatting.sh")) (cache-root "/var/cache/cgit") (cache-size 1000) ;; Snapshots are likely to get pretty big and there seems to be no simple ;; way to limit cache size by bytes rather than a number of entries. Let's ;; disable snapshot cache entirely to avoid running out of disk space. (cache-snapshot-ttl 0) (css "/cgit-static/cgit.css") (enable-index-links? #t) (enable-index-owner? #f) (favicon "/cgit-static/favicon.ico") (footer (plain-file "cgit-footer" "\ ")) (logo "https://git.koszko.org/cgit-static/cgit.png") (max-blob-size 100) ;; Owners are not used in this setup but cgit nevertheless tries to deduce ;; them from the filesystem&passwd and presents "Gitolite user" as the owner ;; in the UI. This hack hides that string from visitors. (owner-filter (program-file "dummy-format-owner" #~(noop))) (project-list "/var/lib/gitolite/projects.list") (remove-suffix? #t) (repository-directory "/var/lib/gitolite/repositories") (root-desc "Repositories of Wojtek") (snapshots '("tar.gz" "zip")) (extra-options (append ;; Setting `js` option to an empty list removes JS from the ;; UI. '("js=") ;; TODO: improve Guix' cgit-configuration to allow passing ;; multiple CSS files. '("css=/cgit-static/better-cgit-markdown-heading-color.css") (append-map (lambda (file-name) (map (cut string-append "readme=:" file-name <>) '(".md" ".mkd" ".rst" ".html" ".htm" ".txt" ""))) '("readme" "README" "install" "INSTALL"))))))) (define %better-cgit-markdown-heading-color.css (mixed-text-file "better-cgit-markdown-heading-color.css" (format #f "~{.markdown-body h~a a~^,~%~} " (iota 6 1)) "{ color: #555 !important; }")) (define %httpd-gitconfig (plain-file "gitconfig" "\ [safe] directory=* ")) (prepend %services (simple-service 'cgit-http-virtualhost web:httpd-service-type (list (web:httpd-virtualhost "*:443" `("\ ServerName git.koszko.org ServerAlias www.git.koszko.org ServerAlias git.koszkonutek-tmp.pl.eu.org ServerAlias www.git.koszkonutek-tmp.pl.eu.org ServerAdmin webmaster@koszko.org Redirect permanent / https://git.koszko.org/ SetEnv GIT_CONFIG_GLOBAL " ,%httpd-gitconfig " Require all granted SetEnv GIT_PROJECT_ROOT /var/lib/gitolite/repositories ScriptAliasMatch \ ^/(.*/(HEAD|info/refs|objects/info/[^/]+|git-upload-pack))$ \ " ,(file-append git "/libexec/git-core/git-http-backend/$1") " Alias /cgit-static/better-cgit-markdown-heading-color.css \ " ,%better-cgit-markdown-heading-color.css " Alias /cgit-static " ,(file-append cgit "/share/cgit") " SetEnv CGIT_CONFIG " ,(mixed-text-file "cgitrc" %cgitrc) " ScriptAlias / " ,(file-append cgit "/lib/cgit/cgit.cgi/") " Options +ExecCGI SSLEngine on SSLCertificateFile /etc/certs/git.koszko.org/fullchain.pem SSLCertificateKeyFile /etc/certs/git.koszko.org/privkey.pem "))))) (prepend %services (simple-service 'prepare-cgit-cache-dir activation-service-type #~(begin (mkdir-p "/var/cache/cgit") (chown "/var/cache/cgit" -1 (group:gid (getgr "httpd"))) (chmod "/var/cache/cgit" #o770)))) (prepend %services (simple-service 'httpd-virtualhost-koszko web:httpd-service-type (list (let ((wsgi (file-append ks:koszko-org-website "/share/koszko-org-website/wsgi.py"))) (web:httpd-virtualhost "*:443" `("\ ServerName koszko.org ServerAlias www.koszko.org ServerAlias koszkonutek-tmp.pl.eu.org ServerAlias www.koszkonutek-tmp.pl.eu.org ServerAdmin koszko@koszko.org Redirect permanent / https://koszko.org/ DocumentRoot /srv/http/koszko.org Alias /sideload /srv/http/koszko.org Alias /sl /srv/http/koszko.org Require all granted WSGIScriptAlias / " ,wsgi " SSLEngine on SSLCertificateFile /etc/certs/koszko.org/fullchain.pem SSLCertificateKeyFile /etc/certs/koszko.org/privkey.pem ")))))) (prepend %services (simple-service 'httpd-virtualhost-haketilo web:httpd-service-type (list (let ((wsgi (file-append ks:hydrilla-website "/share/hydrilla-website/wsgi.py"))) (web:httpd-virtualhost "*:443" `("\ ServerName haketilo.koszko.org ServerAlias www.haketilo.koszko.org ServerAdmin koszko@koszko.org Redirect permanent / https://haketilo.koszko.org/ Require all granted WSGIScriptAlias / " ,wsgi " SSLEngine on SSLCertificateFile /etc/certs/haketilo.koszko.org/fullchain.pem SSLCertificateKeyFile /etc/certs/haketilo.koszko.org/privkey.pem ")))))) (define %python-path-spec-sexp (search-path-specification->sexp (guix-pythonpath-search-path (package-version (default-python))))) (define %hydrilla-pythonpath-inputs (cons ks:hydrilla-without-haketilo (map cadr (package-transitive-target-inputs ks:hydrilla-without-haketilo)))) (define %hydrilla-pythonpath-gexp (with-imported-modules (source-module-closure '((guix search-paths))) #~(begin (use-modules ((guix search-paths) #:prefix sp:)) (let ((evaluated-list (sp:evaluate-search-paths (list (sp:sexp->search-path-specification '#$%python-path-spec-sexp)) '#$%hydrilla-pythonpath-inputs))) (cdar evaluated-list))))) (prepend %services (simple-service 'httpd-virtualhost-hydrilla web:httpd-service-type (list (web:httpd-virtualhost "*:443" `("\ ServerName hydrilla.koszko.org ServerAdmin webmaster@koszko.org Redirect permanent / https://hydrilla.koszko.org/ Alias /downloads /srv/http/hydrilla.koszko.org/downloads Alias /schemas \ " ,(file-append ks:hydrilla-json-schemas-all "/share/hydrilla-json-schemas") " DocumentRoot /var/lib/hydrilla/malcontent_dirs ForceType application/json SetEnvIf Request_URI ^/(api_v[0-9]+)/ \ MALCONENT_DIR=/var/lib/hydrilla/malcontent_dirs/$1 SetEnvIf Request_URI ^/api_v[0-9]+/ \ HYDRILLA_GUIX_PYTHONPATH=" ,%hydrilla-pythonpath-gexp " WSGIScriptAliasMatch \ ^/api_v[^/]+/((resource|mapping)/[^/]+[.]json|query|list_all)$ \ " ,(local-file "hydrilla-wsgi.py") "/$1 SSLEngine on SSLCertificateFile \ /etc/certs/hydrilla.koszko.org/fullchain.pem SSLCertificateKeyFile \ /etc/certs/hydrilla.koszko.org/privkey.pem "))))) (prepend %services (simple-service 'httpd-virtualhost-hydrillabugs web:httpd-service-type (list (web:httpd-virtualhost "*:443" `("\ ServerName hydrillabugs.koszko.org ServerAlias www.hydrillabugs.koszko.org ServerAlias hachettebugs.koszko.org ServerAlias www.hachettebugs.koszko.org ServerAdmin webmaster@koszko.org Redirect permanent / https://hydrillabugs.koszko.org/ DocumentRoot /srv/http/hydrillabugs.koszko.org RewriteEngine On RewriteCond %{REQUEST_METHOD} !GET RewriteRule ^(.*)$ /hydrillabugs-archived.html [L,R=301,QSD] Alias /hydrillabugs-archived.html \ " ,(local-file "hydrillabugs-archived.html") " RewriteRule /uri-map.txt /dummy [L,F] # Serve wget-archived website. RewriteMap add-ext \ txt:/srv/http/hydrillabugs.koszko.org/uri-map.txt ForceType application/javascript # Make `hachette' project display under the name `haketilo'. RewriteRule ^/projects/haketilo(.*)$ /projects/hachette$1 RewriteCond %{QUERY_STRING} !^$ RewriteCond ${add-ext:$1?%{QUERY_STRING}|NONE} !NONE RewriteRule ^(.*)$ ${add-ext:$1?%{QUERY_STRING}} [L,R=301] RewriteCond %{QUERY_STRING} !^$ RewriteRule ^(.*)$ $1?%{QUERY_STRING}? [L,QSL] RewriteCond %{QUERY_STRING} ^$ RewriteCond ${add-ext:$1|NONE} !NONE RewriteRule ^(.*)$ ${add-ext:$1} [L,R=301] SSLEngine on SSLCertificateFile \ /etc/certs/hydrillabugs.koszko.org/fullchain.pem SSLCertificateKeyFile \ /etc/certs/hydrillabugs.koszko.org/privkey.pem "))))) (prepend %services (simple-service 'httpd-virtualhost-sheets web:httpd-service-type (map (lambda (what) (define website-files (file-append ks:sheets-websites (format #f "/share/~a-website" what))) (web:httpd-virtualhost "*:443" `("\ ServerName " ,what ".koszko.org ServerAlias www." ,what ".koszko.org ServerAdmin koszko@koszko.org Redirect permanent / https://" ,what ".koszko.org/ DocumentRoot " ,website-files " SSLEngine on SSLCertificateFile /etc/certs/" ,what ".koszko.org/fullchain.pem SSLCertificateKeyFile /etc/certs/" ,what ".koszko.org/privkey.pem "))) '("pray" "sheets")))) (define %certbot-deploy-hook (program-file "httpd-deploy-hook" #~(for-each (lambda (pidfile) (false-if-exception (kill (call-with-input-file pidfile read) SIGHUP))) '("/var/run/httpd" "/var/spool/exim/exim-daemon.pid" "/var/run/dovecot/master.pid")))) (prepend %services (service cb:certbot-sans-nginx-service-type (cb:certbot-configuration (webroot "/var/certbot-validation") (certificates (map (match-lambda ((domains . key-read-group) (cb:certificate-configuration (name (car domains)) (domains domains) (deploy-hook %certbot-deploy-hook) (key-read-group key-read-group)))) '((("koszko.org" "www.koszko.org" "koszkonutek-tmp.pl.eu.org" "www.koszkonutek-tmp.pl.eu.org") . #f) (("git.koszko.org" "www.git.koszko.org" "git.koszkonutek-tmp.pl.eu.org" "www.git.koszkonutek-tmp.pl.eu.org") . #f) (("haketilo.koszko.org" "www.haketilo.koszko.org") . #f) (("hydrilla.koszko.org") . #f) (("hydrillabugs.koszko.org" "www.hydrillabugs.koszko.org" "hachettebugs.koszko.org" "www.hachettebugs.koszko.org") . #f) (("imap.koszko.org") . #f) (("pray.koszko.org" "www.pray.koszko.org") . #f) (("sheets.koszko.org" "www.sheets.koszko.org") . #f) (("smtp.koszko.org") . "smtp-certificates")))) (email "koszko@koszko.org") (rsa-key-size 4096) (service-reload '()) (service-requirement '(httpd))))) (prepend %services (simple-service 'prepare-certbot-tokens-dir activation-service-type #~(mkdir-p "/var/certbot-validation"))) (define (make-koszko-zone-entries domain) (dns:define-zone-entries entries ;; nameservers ("@" "" "IN" "NS" (format #f "ns2.~a." domain)) ("@" "" "IN" "NS" "ns0.1984.is.") ("@" "" "IN" "NS" "ns1.1984.is.") ("@" "" "IN" "NS" "ns2.1984.is.") ("@" "" "IN" "NS" "ns1.1984hosting.com.") ("@" "" "IN" "NS" "ns2.1984hosting.com.") ;; domain->IP assignments ("@" "" "IN" "A" "188.68.237.248") ("antioch" "" "IN" "A" "188.68.236.76") ("*.antioch" "" "IN" "CNAME" "antioch") ("salamina" "" "IN" "A" "188.68.237.248") ("*.salamina" "" "IN" "CNAME" "salamina") ("www" "" "IN" "CNAME" "@") ("git" "" "IN" "CNAME" "salamina") ("www.git" "" "IN" "CNAME" "git") ("hachettebugs" "" "IN" "CNAME" "hydrillabugs") ("www.hachettebugs" "" "IN" "CNAME" "hachettebugs") ("haketilo" "" "IN" "CNAME" "salamina") ("www.haketilo" "" "IN" "CNAME" "haketilo") ("hydrilla" "" "IN" "CNAME" "salamina") ("hydrillabugs" "" "IN" "CNAME" "salamina") ("www.hydrillabugs" "" "IN" "CNAME" "hydrillabugs") ("imap" "" "IN" "CNAME" "salamina") ("ns1" "" "IN" "CNAME" "antioch") ("ns2" "" "IN" "CNAME" "salamina") ("pray" "" "IN" "CNAME" "salamina") ("www.pray" "" "IN" "CNAME" "pray") ("sheets" "" "IN" "CNAME" "salamina") ("www.sheets" "" "IN" "CNAME" "sheets") ("smtp" "" "IN" "CNAME" "salamina") ;; mail ("@" "" "IN" "MX 10" "smtp.koszko.org.") ;; dmarc ("@" "" "IN" "TXT" "\"v=spf1 ip4:188.68.236.76 ip4:188.68.237.248 -all\"") ("_dmarc" "" "IN" "TXT" (format #f "\"~{~{~a=~a~}~^;~}\"" '(("v" "DMARC1") ("p" "reject") ("rua" "mailto:dmarc@koszko.org") ("ruf" "mailto:dmarc@koszko.org") ("rf" "afrf") ("pct" "100")))) ("mail-salamina._domainkey" "" "IN" "TXT" "( \"k=rsa;t=s;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsC9HfcId\" \"QGcBzYtBzv/VF7vPrRAASwfeAJ3pr0tddpYtFMGGm8kWPAKykX2LvP4epQPdzGL9\" \"W9qLJk3R+iJSlUYLgP1s4Tr5qiUm/PEHPnjzwC8kqtJ/CrQs7WLfiuE3SElWI3iQ\" \"FMRCD1Kk07axSOCgLbshthEAxZQqScYiZT7Irl4SHm9SpTUJkqh5xcCTqsW1yuG/\" \"YWbfERH4PFp2pA85ZZvVEHqd+e8LMLh9EKMYRbhp9AVKMaESIegkO0RZv0KGp4aU\" \"vzquG1FYQ9VOJ6KiviZANuEL4cpNrqmFb4Wbuwc6w7drWaHjFT/j1mS/eB0DN3el\" \"nPQnWMTJZPhqnFZKzG37P5VkIDjbcotevsBGV28gx3VJNf1ye1x/P9f4fJw2s7EY\" \"izNZx9/xfmAiOpP3KhrpE+cccBWnMHYyp0X7ClgBIQTDiLOBX3KhH+GOuADMcum1\" \"4y8SHw2xSYGGk46YI0fBf8kaJsAA9wBQvuBFSj1MjxjdWt0GF0OLnGB1RsPZx+nU\" \"Q+EZz0PDuv16ofN7MaOfY+kD6XoBCDjj4FDqLOgBgoLS7LfLyYTElbuItj12Yqph\" \"ZPY4I+Lmn9nzl3tEnUqtRNu5LPhIR9mj/4BLp2UPgkVnqTAcA+qxoJghm8hh9AS9\" \"2pelav0CarwwvUo09rcvujksuwy8TulK8ckCAwEAAQ==\" )") ((string-append domain "._report._dmarc") "" "IN" "TXT" "\"v=DMARC1\"")) entries) (prepend %services (service dns:knot-service-type (dns:knot-configuration (acls (list (dns:knot-acl-configuration (id "allow-axfr-from-1984") (address '("93.95.224.6")) (action '(transfer))))) (remotes (list (dns:knot-remote-configuration (id "1984-axfr-remote") (address '("93.95.224.6"))))) (zones (map (lambda (domain) (dns:knot-zone-configuration (domain domain) (zone (dns:zone-file (origin domain) (entries (make-koszko-zone-entries domain)) (ns "ns2") (mail "koszko") (serial 2024070301))) (acl '("allow-axfr-from-1984")) (semantic-checks? #t) (notify '("1984-axfr-remote")))) '("koszko.org" "koszkonutek-tmp.pl.eu.org")))))) (define %root.keys-path "/var/cache/knot-resolver/root.keys") (prepend %services (simple-service 'knot-resolver-root-keys-activation activation-service-type #~(let* ((filename #$%root.keys-path) (filename-tmp (format #f "~a-new" filename)) (passwd (getpwnam "knot-resolver"))) (mkdir-p (dirname filename)) (copy-file #$(file-append knot-resolver "/etc/knot-resolver/root.keys") filename-tmp) (chown filename-tmp (passwd:uid passwd) (passwd:gid passwd)) (rename-file filename-tmp filename)))) (prepend %services (service dns:knot-resolver-service-type (dns:knot-resolver-configuration (kresd-config-file (mixed-text-file "kresd.conf" "\ net.listen('0.0.0.0', 5353) modules = { 'view' } trust_anchors.add_file('" %root.keys-path "') local_dnames = policy.todnames({'koszko.org', 'koszkonutek-tmp.pl.eu.org'}) policy.add(policy.suffix(policy.STUB('127.0.0.1'), local_dnames)) for _, mask in ipairs({'10.8.0.0/24', '127.0.0.1/32'}) do view:addr(mask, policy.all(policy.PASS)) end view:addr('0.0.0.0/0', policy.all(policy.DENY)) "))))) (prepend %services (service mail:mail-aliases-service-type '(("root" "admin")))) (prepend %services (service mail:exim-service-type (mail:exim-configuration (config-file (local-file "./exim.conf")) (setuid-user 0)))) (prepend %services (simple-service 'make-maildirs-directory activation-service-type #~(begin (mkdir-p "/var/vmail") (let ((initial-umask (umask))) (umask #o007) (mkdir-p "/var/vmail/maildirs") (chown "/var/vmail/maildirs" -1 (group:gid (getgr "vmail"))) (umask initial-umask))))) (prepend %services (service mail:dovecot-service-type (mail:dovecot-configuration (services (list (mail:service-configuration (kind "imap-login") (listeners (list (mail:inet-listener-configuration (protocol "imaps") (port 993) (ssl? #t)))) ;; '1' is more secure than '0' because each ;; connection is handled in a separate process. (service-count 1)) (mail:service-configuration (kind "imap")) (mail:service-configuration (kind "auth") (listeners (list (mail:unix-listener-configuration (path "auth-userdb")))) ;; Dovecot requires process-limit to be 1 here. (service-count 0) (process-limit 1)))) (ssl-cert "packages '("file" "bind:utils" "net-tools" "man-pages-posix" "emacs" "minetest")) %base-packages)) (services %services)) ;;; Local Variables: ;;; eval: (put 'prepend 'scheme-indent-function 1) ;;; End: