aboutsummaryrefslogtreecommitdiff
;; SPDX-License-Identifier: CC0-1.0

;; Copyright (C) 2022-2024 W. Kosior <koszko@koszko.org>
;;
;; 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 tls) #:select (openssl))
             ((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 sysctl) #:select (sysctl-service-type))
             ((gnu services networking) #:prefix net:)
             ((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 services vpn) #:prefix vpn:)

             ((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 'allow-ip-forwarding sysctl-service-type
                  '(("net.ipv4.ip_forward" . "1"))))


(prepend %services
  (simple-service 'always-forbid-root-login activation-service-type
    #~(system "/run/setuid-programs/passwd -l root > /dev/null")))


(prepend %services
  (service net: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
               <If \"%{HTTP_HOST} != '" ,main-domain "'\">
                   Redirect permanent / http://" ,main-domain "/
               </If>
               #<ElseIf \"true && (%{REQUEST_URI} !~ \
               #                   m#^/[.]well-known/acme-challenge/.*#)\">
               #    Redirect permanent / https://" ,main-domain "/
               #</ElseIf>
               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"
                        "\
                         <div class=footer>generated by
                             <a href=\"https://git.zx2c4.com/cgit/about/\">
                                 cgit
                             </a>
                         </div>
                        "))
    (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

             <If \"%{HTTP_HOST} != 'git.koszko.org'\">
                 Redirect permanent / https://git.koszko.org/
             </If>

             SetEnv GIT_CONFIG_GLOBAL " ,%httpd-gitconfig "

             <Directory " ,(file-append git "/libexec/git-core") ">
                 Require all granted
                 SetEnv GIT_PROJECT_ROOT /var/lib/gitolite/repositories
             </Directory>
             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/") "
             <Directory " ,(file-append cgit "/lib/cgit/") ">
                 Options +ExecCGI
             </Directory>

             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

                <If \"%{HTTP_HOST} != 'koszko.org'\">
                    Redirect permanent / https://koszko.org/
                </If>

                DocumentRoot /srv/http/koszko.org
                Alias /sideload /srv/http/koszko.org
                Alias /sl /srv/http/koszko.org
                <Files " ,wsgi ">
                    Require all granted
                </Files>
                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

                <If \"%{HTTP_HOST} != 'haketilo.koszko.org'\">
                    Redirect permanent / https://haketilo.koszko.org/
                </If>

                <Files " ,wsgi ">
                    Require all granted
                </Files>
                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

             <If \"%{HTTP_HOST} != 'hydrilla.koszko.org'\">
                 Redirect permanent / https://hydrilla.koszko.org/
             </If>

             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

             <Location ~ ^/api_v[^/]+/(resource|mapping)/>
                 ForceType application/json
             </Location>

            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

             <If \"%{HTTP_HOST} != 'hydrillabugs.koszko.org'\">
                 Redirect permanent / https://hydrillabugs.koszko.org/
             </If>

             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

             <Location /javascripts>
                 ForceType application/javascript
             </Location>

             # 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

               <If \"%{HTTP_HOST} != '" ,what ".koszko.org'\">
                   Redirect permanent / https://" ,what ".koszko.org/
               </If>

               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")))


(prepend %services
  (service net:iptables-service-type
           (net:iptables-configuration
            (ipv4-rules (plain-file "iptables.rules" (format #f "\
*filter

:INPUT ACCEPT
:FORWARD ACCEPT
:OUTPUT ACCEPT

COMMIT

*nat

~:{-A ~a -p ~a --destination 10.8.0.1 --dport 53 \
    -j DNAT --to-destination 10.8.0.1:5353~%~}\

COMMIT
" '((OUTPUT udp) (OUTPUT tcp) (PREROUTING udp) (PREROUTING tcp))))))))


(define %salamina-v4-addr
  "188.68.237.248")

(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" %salamina-v4-addr)

    ("salamina"   "" "IN" "A" %salamina-v4-addr)
    ("*.salamina" "" "IN" "CNAME" "salamina")

    ("www"              "" "IN" "CNAME" "@")
    ("git"              "" "IN" "CNAME" "@")
    ("www.git"          "" "IN" "CNAME" "git")
    ("hachettebugs"     "" "IN" "CNAME" "hydrillabugs")
    ("www.hachettebugs" "" "IN" "CNAME" "hachettebugs")
    ("haketilo"         "" "IN" "CNAME" "@")
    ("www.haketilo"     "" "IN" "CNAME" "haketilo")
    ("hydrilla"         "" "IN" "CNAME" "salamina")
    ("hydrillabugs"     "" "IN" "CNAME" "@")
    ("www.hydrillabugs" "" "IN" "CNAME" "hydrillabugs")
    ("imap"             "" "IN" "CNAME" "salamina")
    ("ns2"              "" "IN" "CNAME" "salamina")
    ("pray"             "" "IN" "CNAME" "@")
    ("www.pray"         "" "IN" "CNAME" "pray")
    ("sheets"           "" "IN" "CNAME" "@")
    ("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 2024091500)))
                    (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 "</etc/certs/imap.koszko.org/fullchain.pem")
     (ssl-key "</etc/certs/imap.koszko.org/privkey.pem")
     (auth-mechanisms '("plain" "login"))
     (passdbs (list (mail:passdb-configuration
                     (driver "passwd-file")
                     (args '("scheme=SHA256-CRYPT"
                             "username_format=%u"
                             "/var/vmail/passwd")))))
     (userdbs (list (mail:userdb-configuration
                     (driver "static")
                     (args '("home=/var/vmail/maildirs/%n")))))
     (mail-uid "vmail")
     (mail-gid "vmail")
     (mail-location "maildir:/var/vmail/maildirs/%n"))))


(prepend %services
  (service net:ntp-service-type))


(prepend %services
  (service openssh-service-type
    (openssh-configuration
     (permit-root-login 'prohibit-password)
     (authorized-keys `(("root" ,(local-file "koszko.pub"))))
     (port-number 10022))))


(define %openvpn-cert-subj
  (format #f "~{/~{~a=~a~}~}" '((C PL)
                                (ST PL)
                                (L Krakow)
                                (O koszko.org)
                                (OU koszko.org)
                                (CN koszko.org)
                                (emailAddress koszko@koszko.org))))

(prepend %services
  (simple-service 'prepare-openvpn-certs activation-service-type
    #~(let ((openssl #$(file-append openssl "/bin/openssl"))
            (initial-umask (umask)))
        (dynamic-wind

          (lambda ()
            (umask #o077))

          (lambda ()
            (mkdir-p "/etc/openvpn"))

          (lambda ()
            (umask initial-umask)))

        (with-directory-excursion "/etc/openvpn"
          (unless (and-map file-exists? '("ca.crt" "server.crt" "server.key"))
            (with-output-to-file "x509.ext"
              (lambda ()
                (display "\
[ ca ]
# X509 extensions for a ca
keyUsage                = critical, cRLSign, keyCertSign
basicConstraints        = CA:TRUE, pathlen:0
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid:always,issuer:always

[ server ]
# X509 extensions for a server
keyUsage                = critical,digitalSignature,keyEncipherment
extendedKeyUsage        = serverAuth,clientAuth
basicConstraints        = critical,CA:FALSE
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid,issuer:always
")))

            (invoke/quiet
             openssl "genpkey" "-genparam" "-algorithm" "ec"
             "-pkeyopt" "ec_paramgen_curve:P-384" "-out" "ecparam.pem")

            (invoke/quiet
             openssl "req" "-new" "-sha256" "-nodes" "-newkey" "ec:ecparam.pem"
             "-keyout" "server.key" "-out" "server.csr"
             "-subj" #$%openvpn-cert-subj)

            (invoke/quiet
             openssl "x509" "-req" "-sha256" "-extfile" "x509.ext"
             "-extensions" "ca" "-in" "server.csr" "-signkey" "server.key"
             "-days" "10095" "-out" "server.crt")

            (unless (file-exists? "dh4096.pem")
              (invoke/quiet openssl "dhparam" "-out" "dh4096.pem" "4096")))))))

(prepend %services
  (service vpn:openvpn-server-service-type
    (vpn:openvpn-server-configuration
     (ca "/etc/openvpn/ca.crt")
     (cert "/etc/openvpn/server.crt")
     (key "/etc/openvpn/server.key")
     (comp-lzo? #f)
     (port 1195)
     (server "10.8.0.0 255.255.255.0")
     (dh "/etc/openvpn/dh4096.pem")
     (redirect-gateway? #t)
     (client-to-client? #t)
     (client-config-dir (list (vpn:openvpn-ccd-configuration
                               (name "koszko.org-pafos-client")
                               (iroute "10.8.0.36 255.255.255.255")
                               (ifconfig-push "10.8.0.36 10.8.0.1")))))))


(prepend %services
  (service vc:gitolite-service-type
    (vc:gitolite-configuration
     (admin-pubkey (local-file "koszko.pub"))
     (rc-file (vc:gitolite-rc-file
               (umask #o027)
               (git-config-keys ".*")
               (enable '("help"
                         "desc"
                         "info"
                         "perms"
                         "writable"
                         "ssh-authkeys"
                         "git-config"
                         "daemon"
                         "gitweb"
                         "cgit")))))))


(prepend %services
  (service postgresql-service-type
    (postgresql-configuration (port 15432))))


(operating-system
  (host-name "salamina")
  (timezone "Europe/Warsaw")
  (locale "en_US.utf8")

  (keyboard-layout (keyboard-layout "pl"))

  (bootloader (bootloader-configuration
               (bootloader grub-bootloader)
               (targets '("/dev/vda"))))

  (file-systems (cons* (fs:file-system
                         (device (fs:file-system-label "salamina-root"))
                         (mount-point "/")
                         (type "ext4"))
                       fs:%base-file-systems))

  (swap-devices
   (list (fs:swap-space
          (target "/swapfile")
          (dependencies (filter (fs:file-system-mount-point-predicate "/swap")
				file-systems)))))

  (users (cons* (gss:user-account
                 (name "vmail")
                 (group "vmail")
                 (system? #t)
                 (comment "Email access user")
                 (home-directory "/var/empty")
                 (shell (file-append shadow "/sbin/nologin")))
                (gss:user-account
                 (name "minetest")
                 (group "minetest")
                 (system? #t)
                 (comment "Minetest server")
                 (home-directory "/var/minetest")
                 (shell (file-append shadow "/sbin/nologin")))
                gss:%base-user-accounts))

  (groups (cons* (gss:user-group
                  (name "vmail")
                  (system? #t))
                 (gss:user-group
                  (name "minetest")
                  (system? #t))
                 (gss:user-group
                  (name "smtp-certificates")
                  (system? #t))
                 gss:%base-groups))

  (extra-groups (list (gss:user-extra-groups
                       (user "exim")
                       (groups '("smtp-certificates")))
                      (gss:user-extra-groups
                       (user "httpd")
                       (groups '("git")))))

  (packages (append (specifications->packages
                     '("file"
                       "bind:utils"
                       "net-tools"
                       "man-pages-posix"
                       "emacs"
                       "minetest"))
                    %base-packages))

  (services %services))

;;; Local Variables:
;;; eval: (put 'prepend 'scheme-indent-function 1)
;;; eval: (put 'service 'scheme-indent-function 1)
;;; eval: (put 'simple-service 'scheme-indent-function 2)
;;; End: