diff options
author | EuAndreh <eu@euandre.org> | 2022-11-25 16:28:56 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2022-11-25 16:28:56 -0300 |
commit | 1a83a3ba00e11a6508880174129a144dec3f1cc0 (patch) | |
tree | dd1461effd6c9b03dd1c154ad64f57fe5e175070 | |
parent | rm -rf aux/ (diff) | |
download | toph-1a83a3ba00e11a6508880174129a144dec3f1cc0.tar.gz toph-1a83a3ba00e11a6508880174129a144dec3f1cc0.tar.xz |
src/infrastructure/guix/system.scm: Add working version of toph OS
-rw-r--r-- | Makefile | 16 | ||||
-rw-r--r-- | src/infrastructure/guix/channels.scm | 20 | ||||
-rw-r--r-- | src/infrastructure/guix/system.scm | 1905 |
3 files changed, 1931 insertions, 10 deletions
@@ -1,22 +1,18 @@ .POSIX: -check-guile: - guile tests/unit-tests.scm -check-shellcheck: - sh aux/assert-shellcheck.sh +all: -check-todos: - sh aux/workflow/assert-todos.sh -check: check-guile check-shellcheck check-todos +check: clean: - rm -rf public/ *.log + rm -rf public/ dev-check: check public: - sh aux/workflow/public.sh Servers servers public-inbox -.PHONY: check clean dev-check +deploy: + scp src/infrastructure/guix/system.scm toph:/etc/guix/ + ssh toph reconfigure diff --git a/src/infrastructure/guix/channels.scm b/src/infrastructure/guix/channels.scm new file mode 100644 index 0000000..eb9ef5e --- /dev/null +++ b/src/infrastructure/guix/channels.scm @@ -0,0 +1,20 @@ +(append + (list + (channel + (name 'org-arrobaponto) + (url "git://arrobaponto.org/package-repository") + (branch "main") + (introduction + (make-channel-introduction + "d749e053e6db365069cb9b2ef47a78b06f9e7361" + (openpgp-fingerprint + "5BDA E9B8 B2F6 C6BC BB0D 6CE5 81F9 0EC3 CD35 6060")))) + (channel + (name 'nonguix) + (url "https://gitlab.com/nonguix/nonguix") + (introduction + (make-channel-introduction + "897c1a470da759236cc11798f4e0a5f7d4d59fbc" + (openpgp-fingerprint + "2A39 3FFF 68F4 EF7A 3D29 12AF 6F51 20A0 22FB B2D5"))))) + %default-channels) diff --git a/src/infrastructure/guix/system.scm b/src/infrastructure/guix/system.scm new file mode 100644 index 0000000..e506de8 --- /dev/null +++ b/src/infrastructure/guix/system.scm @@ -0,0 +1,1905 @@ +(use-modules + ((guix licenses) #:prefix license:) + ((srfi srfi-1) #:prefix srfi-1:) + ((xyz euandreh heredoc) #:prefix heredoc:) + (gnu) + (gnu services mail) + (gnu services shepherd) + (gnu packages admin) + (gnu packages cyrus-sasl) + (gnu packages dbm) + (gnu packages m4) + (gnu packages mail) + (gnu packages messaging) + (gnu packages onc-rpc) + (gnu packages perl) + (gnu packages tls) + (gnu packages version-control) + (gnu system setuid) + (guix build-system gnu) + (guix build-system trivial) + (guix download) + (guix packages) + (guix records) + (guix utils) + (xyz euandreh queue)) +(use-package-modules + web) +(use-service-modules + certbot + cgit + messaging + networking + ssh + version-control + vpn + web) +(heredoc:enable-syntax) + +(define ssh-pubkey + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDnUv7iWOejQNa3fZ6v4lkHT6qFRp2+NuzIpFJ2Vy7eP58XZoiz6HJPcCU8Hf95JXwaXEwS4S7mXdw1x60hd8JIe058Ek6MZSSVQmlLfocGsAYj1wTrLmnQ8+PV0IeQlNj1aytBI1fL+v3IPt+JdLt6b+g3vwcEUU9efzxx2E0KZ5GIpb2meiCQ6ha+tcd7XqegB53eQj/h/coE2zLJodpaJ3xbj894pE/OJCNC0+4d0Sv7oHhY7QoLYldTQbSgPyhyfl4iZpJf6OEPZxK2cJaB+cbeoBB6aGNyU+CIJToM+uAJJ7H7EpvxfcnfJQ1PuY5szTdvFbW820euiUEKEW69mW4uaFNPSc6D4Z8tZ5hXQIqBD40irULhF0CYNkIILmyNV/KJIZ5HkbQ1q+UrCFHJyvuH/3aCTjj9OSfE7xHPQ3xd3Xw8vvj0Mjie09xFbbcklBTw5WRzH7cw8c+Q0O69kZZ8b+ykcdzWTeZeWNdnzptNqnMjfheig90rUIJ7DN0c+53jCUcGpWJxJhcYF9Uk1RNHmSE5+VzK1y+20t0grVFX90nApm4Tl35QPrX7Qxp9C81cWiUB8xCAE6jYrmd4x+P/3wSQfc1Xg0Eg3QjJB+6JD7cbyDJpzDR3ja+CLZCAr9I0B4rDKD2d6et/z67iXPnZUWMyZ8RVVZPFbBMOTw== openpgp:0xF727046D") + +(define rc.sh (plain-file "rc.sh" #"- + #!/bin/sh + + # shellcheck source=/dev/null + . /etc/profile + + export XDG_PREFIX=~/.usr + export XDG_CACHE_HOME="$XDG_PREFIX"/var/cache + export XDG_CONFIG_HOME="$XDG_PREFIX"/etc + export XDG_DATA_HOME="$XDG_PREFIX"/share + export XDG_STATE_HOME="$XDG_PREFIX"/state + export XDG_LOG_HOME="$XDG_PREFIX"/var/log + + mkdir -p \ + "$XDG_CONFIG_HOME" \ + "$XDG_CACHE_HOME" \ + "$XDG_DATA_HOME" \ + "$XDG_STATE_HOME" \ + "$XDG_LOG_HOME" + + GUIX_PROFILE="$XDG_CONFIG_HOME"/guix/current + if [ -r "$GUIX_PROFILE"/etc/profile ]; then + # shellcheck source=/dev/null + . "$GUIX_PROFILE"/etc/profile + fi + + export ENV=~/.profile + export HISTSIZE=-1 + export HISTCONTROL=ignorespace:ignoredups + export EDITOR=vi + export VISUAL="$EDITOR" + export PAGER='less -R' + + export EXINIT=' + " set number + " set autoindent + set ruler + set showmode + set showmatch + ' + + export HISTFILE="$XDG_STATE_HOME"/bash-history + export LESSHISTFILE="$XDG_STATE_HOME"/lesshst + export RLWRAP_HOME="$XDG_CACHE_HOME"/rlwrap + export GUILE_HISTORY="$XDG_STATE_HOME"/guile-history + + HOSTNAME="$(hostname)" + export BORG_REPO="16686@ch-s010.rsync.net:borg/$HOSTNAME" + export BORG_REMOTE_PATH='borg1' + export BORG_PASSCOMMAND='cat /opt/secrets/borg-passphrase.txt' + + export GIT_CONFIG_GLOBAL=/etc/gitconfig + + unalias -a + alias l='ls -lahF --color' + alias grep='grep --color' + alias less='less -R' + alias e='vi' + alias tree='tree -aC' + + error_marker() { + STATUS=$? + if [ "$STATUS" != 0 ]; then + printf ' (!! %s !!) ' "$STATUS" + fi + } + export PS1='`error_marker`\T \w/ + \u@\H\$ ' + "#)) + +(define backup.sh #"- + #!/bin/sh + # shellcheck source=/dev/null + . /etc/rc + set -eu + + usage() { + cat <<-'EOF' + Usage: + backup [-q] [-C COMMENT] [ARCHIVE_TAG] + backup -h + EOF + } + + help() { + cat <<-'EOF' + + Options: + -q disable verbose move, useful for + for batch sessions + -C COMMENT the comment text to be attached to the archive + -h, --help show this message + + ARCHIVE_TAG the tag used to create the new + backup (default: "manual") + + + The repository is expected to have been created with: + + $ borg init -e repokey-blake2 + + The following environment variables are expected to be exported: + + $BORG_PASSCOMMAND + $BORG_REPO + $BORG_REMOTE_PATH + + Password-less SSH access is required, usually done via adding + ~/.ssh/id_rsa.pub to the-ssh-remote:.ssh/authorized_keys. + + Root permission is also required. + + + Examples: + + Run backup from cronjob: + + $ backup -q cronjob + + + Create backup with a comment, a tag, and verbose mode active: + + $ backup -C 'The backup has a comment' + EOF + } + + + for flag in "$@"; do + case "$flag" in + --) + break + ;; + --help) + usage + help + exit + ;; + *) + ;; + esac + done + + VERBOSE_FLAGS='--verbose --progress' + COMMENT='' + while getopts 'qC:h' flag; do + case "$flag" in + q) + VERBOSE_FLAGS='' + ;; + C) + COMMENT="$OPTARG" + ;; + h) + usage + help + exit + ;; + *) + usage >&2 + exit 2 + ;; + esac + done + shift $((OPTIND - 1)) + + ARCHIVE_TAG="${1:-manual}" + + + run() { + set -x + # shellcheck disable=2086 + sudo -i borg create \ + $VERBOSE_FLAGS \ + --comment " $COMMENT" \ + --stats \ + --compression lzma,9 \ + "::$(hostname)-{now}-$ARCHIVE_TAG" \ + /root/ \ + /home/ \ + /etc/ \ + /var/ \ + /srv/ \ + /opt/ + STATUS=$? + set +x + + if [ "$STATUS" = 0 ]; then + return 0 + elif [ "$STATUS" = 1 ]; then + printf 'WARNING, but no ERROR.\n' >&2 + return 0 + else + return "$STATUS" + fi + } + + run + exit $? + "#) + +(define cronjob.sh #"- + #!/bin/sh + set -eu + + usage() { + cat <<-'EOF' + Usage: + cronjob COMMAND... + cronjob -h + EOF + } + + help() { + cat <<-'EOF' + + Options: + -h, --help show this message + + COMMAND the command to be executed + + + Execute the given command, and send the output to email, with + special treatment to the status code. + + It loads the appropriate files, so that the actual cron + declaration is smaller. + + + Examples: + + Run a backup: + + $ cronjob backup -q cron + EOF + } + + + for flag in "$@"; do + case "$flag" in + --) + break + ;; + --help) + usage + help + exit + ;; + *) + ;; + esac + done + + while getopts 'h' flag; do + case "$flag" in + h) + usage + help + exit + ;; + *) + usage >&2 + exit 2 + ;; + esac + done + shift $((OPTIND - 1)) + + + set +eu + # shellcheck source=/dev/null + . /etc/rc + set -eu + + CMD="$*" + with-email -s "$CMD" -- "$@" 1>> /var/log/cronjob.log 2>&1 + "#) + +(define reconfigure.sh #"- + #!/bin/sh + # shellcheck source=/dev/null + . /etc/rc + set -eu + + + usage() { + cat <<-'EOF' + Usage: + reconfigure [-U] + reconfigure -h + EOF + } + + help() { + cat <<-'EOF' + + Options: + -U pull the latest channels before reconfiguring + -h, --help show this message + + + Run a "guix system reconfigure". If the -U flag is given, + perform a "guix pull" prior to the reconfigure. + + + Examples: + + Just do the deploy: + + $ reconfigure + + + Update and upgrade: + + $ reconfigure -U + EOF + } + + + for flag in "$@"; do + case "$flag" in + --) + break + ;; + --help) + usage + help + exit + ;; + *) + ;; + esac + done + + UPDATE=false + while getopts 'Uh' flag; do + case "$flag" in + U) + UPDATE=true + ;; + h) + usage + help + exit + ;; + *) + usage >&2 + exit 2 + ;; + esac + done + shift $((OPTIND - 1)) + + if [ "$UPDATE" = true ]; then + guix pull -v3 + fi + + + sudo -E guix system -v3 reconfigure /etc/guix/system.scm + "#) + +(define with-email.sh #"- + #!/bin/sh + set -eu + + usage() { + cat <<-'EOF' + Usage: + with-email [-s SUBJECT] COMMAND... + with-email -h + EOF + } + + help() { + cat <<-'EOF' + + Options: + -s SUBJECT set the subject of the email + -h, --help show this message + + COMMAND the command to be wrapped + + + Examples: + + Send email with default subject: + + $ with-email echo 123 + + + Use custom subject and explicit separation of command: + + $ with-email -s 'Something' -- do-something.sh + EOF + } + + now() { + date '+%Y-%m-%dT%H:%M:%S%Z' + } + + uuid() { + od -xN20 /dev/urandom | + head -n1 | + awk '{OFS="-"; print $2$3,$4,$5,$6,$7$8$9}' + } + + mkstemp() { + name="${TMPDIR:-/tmp}/uuid-tmpname with spaces.$(uuid)" + touch "$name" + echo "$name" + } + + + for flag in "$@"; do + case "$flag" in + --) + break + ;; + --help) + usage + help + exit + ;; + *) + ;; + esac + done + + SUBJECT='NO SUBJECT' + while getopts 's:h' flag; do + case "$flag" in + s) + SUBJECT="$OPTARG" + ;; + h) + usage + help + exit + ;; + *) + usage >&2 + exit 2 + ;; + esac + done + shift $((OPTIND - 1)) + + if [ -z "$1" ]; then + printf 'Missing COMMAND.\n\n' >&2 + usage >&2 + exit 2 + fi + + STATUS=0 + OUT="$(mkstemp)" + { + printf 'Running command: %s\n' "$*" + printf 'Starting at: %s\n' "$(now)" + printf '\n' + + "$@" || STATUS=$? + + printf '\n' + printf 'Finished at: %s\n' "$(now)" + } 1>"$OUT" 2>&1 + + HOSTNAME="$(hostname)" + mail \ + -a 'Content-Type: text/plain; charset=UTF-8' \ + -a "From:cron@$HOSTNAME" \ + -s "(exit status: $STATUS) - $SUBJECT" \ + "dev@$HOSTNAME" < "$OUT" || cat "$OUT" + "#) + + +(define (script name content) + (package + (name name) + (version "latest") + (source #f) + (build-system trivial-build-system) + (arguments + (list + #:modules '((guix build utils)) + #:builder + #~(begin + (use-modules (guix build utils)) + (let* ((bin (string-append %output "/bin")) + (prog (string-append bin "/" #$name))) + (mkdir-p bin) + (call-with-output-file prog + (lambda (port) + (format port "~a" #$content))) + (chmod prog #o755))))) + (home-page #f) + (synopsis #f) + (description #f) + (license #f))) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;; /etc/shadow ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + +(define-record-type* <shadow-group-configuration> + shadow-group-configuration + make-shadow-group-configuration + shadow-group-configuration? + (group shadow-group-configuration-group (default "shadow"))) + +(define (shadow-group-activation config) + (match-record config <shadow-group-configuration> + (group) + #~(begin + (use-modules (guix build utils)) + (format (current-error-port) + "Setting ownership and permission for \"/etc/passwd\".~%") + (chown "/etc/shadow" + (passwd:uid (getpwnam "root")) + (group:gid (getgrnam #$group))) + (chmod "/etc/shadow" #o640)))) + +(define (shadow-group-accounts config) + (match-record config <shadow-group-configuration> + (group) + (list + (user-group + (name group) + (system? #t))))) + +(define shadow-group-service-type + (service-type + (name 'shadow-group) + (extensions + (list + (service-extension activation-service-type + shadow-group-activation) + (service-extension account-service-type + shadow-group-accounts))) + (default-value (shadow-group-configuration)) + (description + #"- + FIXME:DOCUMENTATION + "#))) + + + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Cyrus SASL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + +(define-record-type* <cyrus-service-configuration> + cyrus-service-configuration + make-cyrus-service-configuration + cyrus-service-configuration? + (name cyrus-service-configuration-name) + (authmech cyrus-service-configuration-authmech (default "saslauthd"))) + +(define-record-type* <cyrus-sasl-configuration> + cyrus-sasl-configuration + make-cyrus-sasl-configuration + cyrus-sasl-configuration? + (cyrus-sasl cyrus-sasl-configuration-cyrus-sasl (default cyrus-sasl)) + (authmech cyrus-sasl-configuration-authmech (default "shadow")) + (services cyrus-sasl-configuration-services (default '())) + (state-dir cyrus-sasl-configuration-state-dir (default "/var/lib/saslauthd"))) + +(define (cyrus-sasl-etc-files config) + (match-record config <cyrus-sasl-configuration> + (services state-dir) + `(("sasl2" + ,(file-union + "cyrus-sasl" + (map (lambda (service-config) + (match-record service-config <cyrus-service-configuration> + (name authmech) + `(,name ,(plain-file + name + (format #f + #"- + pwcheck_method: ~a + saslauthd_path: ~a/mux + log_level: 7 + "# + authmech + state-dir))))) + services)))))) + +(define (cyrus-sasl-activation config) + (match-record config <cyrus-sasl-configuration> + (state-dir) + #~(begin + (use-modules (guix build utils)) + (format (current-error-port) + "Creating Cyrus SASL socket directory: \"~a\".~%" #$state-dir) + (mkdir-p #$state-dir)))) + +(define (cyrus-sasl-shepherd-service config) + (match-record config <cyrus-sasl-configuration> + (cyrus-sasl authmech state-dir) + (list + (shepherd-service + (provision '(cyrus-sasl)) + (documentation + #"- + FIXME:DOCUMENTATION Run the Postfix MTA. + + This is the entrypoint for starting the "master" process. Then the + "master" process itself takes responsability of starting all the + required daemons and commands."#) + (start #~(make-forkexec-constructor + (list + #$(file-append cyrus-sasl "/sbin/saslauthd") + "-a" + #$authmech + "-d" + "-m" + #$state-dir) + #:user "root" + #:user "root")) + (stop #~(make-kill-destructor)))))) + +(define cyrus-sasl-service-type + (service-type + (name 'cyrus-sasl) + (extensions + (list + (service-extension etc-service-type + cyrus-sasl-etc-files) + (service-extension activation-service-type + cyrus-sasl-activation) + (service-extension profile-service-type + (compose list cyrus-sasl-configuration-cyrus-sasl)) + (service-extension shepherd-root-service-type + cyrus-sasl-shepherd-service))) + (compose srfi-1:concatenate) + (extend (lambda (config services) + (cyrus-sasl-configuration + (inherit config) + (services + (append + (cyrus-sasl-configuration-services config) + services))))) + (default-value (cyrus-sasl-configuration)) + (description + #"- + FIXME:DOCUMENTATION Run the Postfix MTA. + + This is the top-level system service for Postfix. + + It includes: + - populating /etc/postfix/ with read-only configuration files; + - the user and groups used by Postfix when handling email delivery; + - the special setgid binaries for daily usage, such as "sendmail"; + - the Shepherd service for starting, stopping and *reloading* the + service without restarting it; + - the activation script for creating the required directories and + configuring them with the correct permissions; + - the binaries in the system profile so that one doesn't need to explicilty + include the package when the service is already enabled. + + An extension to the log-rotation service isn't included: the default + rottlog configuration already includes /var/log/maillog in its routine, + so it is kept out. + + The defaults of <postfix-configuration> provide sane default values for + most things, such as group names, data and queue directories, etc. When + used as-is, it creates a Postfix server that sends email from local users + of the domain provided by "/etc/hostname"."#))) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; dkimproxy ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + +(define-record-type* <dkimproxyout-configuration> + dkimproxyout-configuration + make-dkimproxyout-configuration + dkimproxyout-configuration? + (dkimproxy dkimproxyout-configuration-dkimproxy (default dkimproxy)) + (user dkimproxyout-configuration-user (default "dkimproxyout")) + (group dkimproxyout-configuration-group (default "dkimproxyout")) + (listen dkimproxyout-configuration-listen (default "127.0.0.1:10027")) + (relay dkimproxyout-configuration-relay (default "127.0.0.1:10028")) + (domains dkimproxyout-configuration-domains (default (list (gethostname)))) + (selector dkimproxyout-configuration-selector (default "dkimproxy")) + (key-size dkimproxyout-configuration-key-size (default 2048)) + (data-directory dkimproxyout-configuration-data-directory (default "/var/lib/dkimproxyout"))) + +(define (generate-out.cf config) + (match-record config <dkimproxyout-configuration> + (listen relay domains selector data-directory) + (format #f + #"- + listen ~a + relay ~a + + domain ~a + selector ~a + + signature dkim(c=relaxed/relaxed) + + # FIXME:DOCUMENTATION add this to the service documentation + # the corresponding public key is available at: + # ~a/public.key + keyfile ~a/private.key + "# + listen + relay + (string-join domains ",") + selector + data-directory + data-directory))) + +(define (dkimproxyout-etc-files config) + (match-record config <dkimproxyout-configuration> + () + `(("dkimproxy_out.conf" ,(plain-file "dkimproxy_out.conf" (generate-out.cf config)))))) + +(define (dkimproxyout-accounts config) + (match-record config <dkimproxyout-configuration> + (user group) + (list + (user-account + (name user) + (group group) + (comment "DKIMproxy-out signing system user") + (home-directory "/var/empty") + (shell (file-append shadow "/sbin/nologin")) + (system? #t)) + (user-group + (name group) + (system? #t))))) + +(define (dkimproxyout-activation config) + (match-record config <dkimproxyout-configuration> + (user group data-directory key-size) + #~(begin + (use-modules (guix build utils)) + (let ((uid (passwd:uid (getpwnam #$user))) + (gid (group:gid (getgrnam #$group)))) + (format (current-error-port) + "Creating DKIMproxy-out data directory: \"~a\".~%" #$data-directory) + (mkdir-p #$data-directory) + (chown #$data-directory uid gid) + (chmod #$data-directory #o755) + (let ((private-key (string-append #$data-directory "/private.key")) + (public-key (string-append #$data-directory "/public.key"))) + (unless (file-exists? private-key) + (cond + ((zero? (system* #$(file-append openssl "/bin/openssl") + "genrsa" + "-out" + private-key + (number->string #$key-size))) + (format (current-error-port) + "DKIMproxy private key file created: \"~a\".~%" private-key)) + (else + (format (current-error-port) + "Failed to create DKIMproxy private key file: \"~a\".~%" private-key)))) + (invoke #$(file-append openssl "/bin/openssl") + "rsa" + "-in" + private-key + "-pubout" + "-out" + public-key) + (format (current-error-port) + "Setting permissions for the public/private DKIMproxy keypair: \"~a/{public,private}.key\".~%" #$data-directory) + (chown private-key uid gid) + (chown public-key uid gid) + (chmod private-key #o400) + (chmod public-key #o644)))))) + +(define (dkimproxyout-shepherd-service config) + (match-record config <dkimproxyout-configuration> + (dkimproxy user group) + (list + (shepherd-service + (provision '(dkimproxy)) + (documentation + #"- + FIXME:DOCUMENTATION Run the Postfix MTA. + + This is the entrypoint for starting the "master" process. Then the + "master" process itself takes responsability of starting all the + required daemons and commands."#) + (start #~(make-forkexec-constructor + (list + #$(file-append dkimproxy "/bin/dkimproxy.out") + "--conf_file=/etc/dkimproxy_out.conf") + #:user #$user + #:group #$group)) + (stop #~(make-kill-destructor)) + (actions + (list + (shepherd-action + (name 'configuration) + (documentation + #"- + FIXME:DOCUMENTATION + "#) + (procedure + #~(lambda _ + (format #t "/etc/dkimproxy_out.conf~%")))))))))) + +(define dkimproxyout-service-type + (service-type + (name 'dkimproxy) + (extensions + (list + (service-extension etc-service-type + dkimproxyout-etc-files) + (service-extension account-service-type + dkimproxyout-accounts) + (service-extension activation-service-type + dkimproxyout-activation) + (service-extension profile-service-type + (compose list dkimproxyout-configuration-dkimproxy)) + (service-extension shepherd-root-service-type + dkimproxyout-shepherd-service))) + (default-value (dkimproxyout-configuration)) + (description + #"- + FIXME:DOCUMENTATION Run the Postfix MTA. + + This is the top-level system service for Postfix. + + It includes: + - populating /etc/postfix/ with read-only configuration files; + - the user and groups used by Postfix when handling email delivery; + - the special setgid binaries for daily usage, such as "sendmail"; + - the Shepherd service for starting, stopping and *reloading* the + service without restarting it; + - the activation script for creating the required directories and + configuring them with the correct permissions; + - the binaries in the system profile so that one doesn't need to explicilty + include the package when the service is already enabled. + + An extension to the log-rotation service isn't included: the default + rottlog configuration already includes /var/log/maillog in its routine, + so it is kept out. + + The defaults of <postfix-configuration> provide sane default values for + most things, such as group names, data and queue directories, etc. When + used as-is, it creates a Postfix server that sends email from local users + of the domain provided by "/etc/hostname"."#))) + + + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Postfix ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + +(define-record-type* <postfix-configuration> + postfix-configuration + make-postfix-configuration + postfix-configuration? + (postfix postfix-configuration-postfix (default postfix)) + (set-sendmail? postfix-configuration-set-sendmail? (default #t)) + (master.cf-file postfix-configuration-master.cf-file (default #f)) + (main.cf-file postfix-configuration-main.cf-file (default #f)) + (master.cf-extra postfix-configuration-master.cf-extra (default "")) + (main.cf-extra postfix-configuration-main.cf-extra (default "")) + (data-directory postfix-configuration-data-directory (default "/var/lib/postfix")) + (queue-directory postfix-configuration-queue-directory (default "/var/spool/postfix")) + (user postfix-configuration-user (default "postfix")) + (group postfix-configuration-group (default "postfix")) + (setgid-group postfix-configuration-setgid-group (default "postdrop")) + (root-aliases postfix-configuration-root-aliases (default '("abuse" "admin" "hostmaster" "postmaster"))) + (cert-file postfix-configuration-cert-file (default #f)) + (key-file postfix-configuration-key-file (default #f)) + (hostname postfix-configuration-hostname (default (gethostname)))) + +; FIXME: hardcoded value of dkimproxy listen and relay +(define (generate-master.cf config) + (match-record config <postfix-configuration> + (master.cf-extra) + (format #f + #"- + # ============================================================================================================ + # service type private unpriv chroot wakeup maxproc command + args + # (yes) (yes) (no) (never) (100) + # ============================================================================================================= + + + anvil unix - - n - 1 anvil + bounce unix - - n - 0 bounce + cleanup unix n - n - 0 cleanup + defer unix - - n - 0 bounce + discard unix - - n - - discard + error unix - - n - - error + flush unix n - n 1000? 0 flush + lmtp unix - - n - - lmtp + local unix - n n - - local + pickup unix n - n 60 1 pickup + -o content_filter=dksign:[127.0.0.1]:10027 + proxymap unix - - n - - proxymap + proxywrite unix - - n - 1 proxymap + qmgr unix n - n 300 1 qmgr + relay unix - - n - - smtp -o syslog_name=postfix/relay + retry unix - - n - - error + rewrite unix - - n - - trivial-rewrite + scache unix - - n - 1 scache + showq unix n - n - - showq + smtp inet n - n - - smtpd -v -o syslog_name=postfix/smtp + smtp unix - - n - - smtp + submission inet n - n - - smtpd -v -o syslog_name=postfix/submission + -o smtpd_tls_security_level=encrypt + -o content_filter=dksign:[127.0.0.1]:10027 + tlsmgr unix - - n 1000? 1 tlsmgr + trace unix - - n - 0 bounce + verify unix - - n - 1 verify + virtual unix - n n - - virtual + postlog unix-dgram n - n - 1 postlogd + + # FIXME: doesn't work for sendmail -t in localhost + dksign unix - - n - - smtp + -o syslog_name=postfix/dkimproxyout-listen + -o smtp_send_xforward_command=yes + -o smtp_discard_ehlo_keywords=8bitmime,starttls + 127.0.0.1:10028 inet n - n - - smtpd -v + -o syslog_name=postfix/dkimproxyout-relay + -o content_filter= + -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks + -o smtpd_helo_restrictions= + -o smtpd_client_restrictions= + -o smtpd_sender_restrictions= + -o smtpd_recipient_restrictions=permit_mynetworks,reject + -o mynetworks=127.0.0.0/8 + -o smtpd_authorized_xforward_hosts=127.0.0.0/8 + ~a + "# + master.cf-extra))) + +(define (generate-main.cf config) + (match-record config <postfix-configuration> + (postfix queue-directory data-directory user setgid-group cert-file key-file hostname main.cf-extra) + (format #f + #"- + compatibility_level = 3.6 + + queue_directory = ~a + data_directory = ~a + mail_owner = ~a + setgid_group = ~a + + myhostname = ~a + mydestination = $myhostname, $mydomain, localhost.$mydomain, localhost + + alias_maps = hash:/etc/aliases + + home_mailbox = Mail/Inbox/ + + header_checks = regexp:{ { /^Received:.*/ IGNORE }, { /^X-Originating-IP:.*/ IGNORE } } + + smtpd_use_tls = yes + smtpd_tls_cert_file = ~a + smtpd_tls_key_file = ~a + smtp_use_tls = $smtpd_use_tls + smtp_tls_cert_file = $smtpd_tls_cert_file + smtp_tls_key_file = $smtpd_tls_key_file + + smtpd_tls_security_level = may + smtp_tls_security_level = may + smtpd_tls_auth_only = yes + + smtpd_relay_restrictions = $smtpd_recipient_restrictions + smtpd_recipient_restrictions = permit_mynetworks, + permit_sasl_authenticated, reject_unauth_destination + smtpd_sasl_auth_enable = yes + cyrus_sasl_config_path = /etc/sasl2 + debug_peer_list = 127.0.0.1 + + milter_default_action = accept + smtpd_milters = ? + ~a + "# + queue-directory + data-directory + user + setgid-group + hostname + + (or cert-file (format #f "/etc/letsencrypt/live/~a/fullchain.pem" hostname)) + (or key-file (format #f "/etc/letsencrypt/live/~a/privkey.pem" hostname)) + main.cf-extra))) + +(define (postfix-etc-files config) + (match-record config <postfix-configuration> + (master.cf-file main.cf-file) + `(("postfix" + ,(file-union + "postfix" + `(("master.cf" ,(plain-file "master.cf" (or master.cf-file (generate-master.cf config)))) + ("main.cf" ,(plain-file "main.cf" (or main.cf-file (generate-main.cf config)))))))))) + +(define (postfix-accounts config) + (match-record config <postfix-configuration> + (user group setgid-group) + (list + (user-account + (name user) + (group group) + (comment "Postfix system user") + (home-directory "/var/empty") + (shell (file-append shadow "/sbin/nologin")) + (system? #t)) + (user-group + (name group) + (system? #t)) + (user-group + (name setgid-group) + (system? #t))))) + +(define (postfix-setuid-programs config) + (match-record config <postfix-configuration> + (postfix setgid-group set-sendmail?) + (append + (list + (setuid-program + (program (file-append postfix "/sbin/postdrop")) + (setuid? #f) + (setgid? #t) + (group setgid-group)) + (setuid-program + (program (file-append postfix "/sbin/postqueue")) + (setuid? #f) + (setgid? #t) + (group setgid-group))) + (if set-sendmail? + (list + (setuid-program + (program (file-append postfix "/sbin/sendmail")) + (setuid? #f) + (setgid? #t) + (group setgid-group))) + '())))) + +(define (postfix-activation config) + (match-record config <postfix-configuration> + (queue-directory) + #~(begin + (use-modules (guix build utils)) + (let ((user (getpwnam "root"))) + (format (current-error-port) + "Creating Postfix queue directory: \"~a\".~%" #$queue-directory) + (mkdir-p #$queue-directory) + (chown #$queue-directory (passwd:uid user) (passwd:gid user)) + (chmod #$queue-directory #o755) + (format (current-error-port) + "Creating email spool director: \"/var/mail\".~%") + (mkdir-p "/var/mail") + (format (current-error-port) + "Updating /etc/aliases: FIXME.~%") +#; + (invoke #$(file-append postfix "/bin/newaliases")))))) + +(define (postfix-shepherd-service config) + (match-record config <postfix-configuration> + (postfix) + (list + (shepherd-service + (provision '(postfix)) + (documentation + #"- + Run the Postfix MTA. + + This is the entrypoint for starting the "master" process. Then the + "master" process itself takes responsability of starting all the + required daemons and commands."#) + (start #~(make-forkexec-constructor + (list + #$(file-append postfix "/sbin/postfix") + "start-fg") + #:pid-file "/var/lib/postfix/master.lock")) + (stop #~(make-kill-destructor)) + (actions + (list + (shepherd-action + (name 'configuration) + (documentation + #"- + FIXME:DOCUMENTATION + "#) + (procedure + #~(lambda _ + (format #t "/etc/postfix/master.cf~%") + (format #t "/etc/postfix/main.cf~%")))) + (shepherd-action + (name 'reload) + (documentation + #"- + Re-read the "master.cf" and "main.cf" configuration files. + + Daemon processes terminate when possible, and when restarted + use the values of the new configuration files. + + This live-reload option is usually preferable over a stop/start + cycle, as it incurs in no interruption of the running service."#) + (procedure + #~(lambda _ + (invoke #$(file-append postfix "/sbin/postfix") "reload")))))))))) + +(define (postfix-aliases config) + (match-record config <postfix-configuration> + (root-aliases) + (map (lambda (alias) + `(,alias "root")) + root-aliases))) + +(define (postfix-certificates config) + (match-record config <postfix-configuration> + (hostname) + (list + (certificate-configuration + (domains (list hostname)) + (deploy-hook + (program-file + "postfix-certbot-deploy-hook" + #~(with-shepherd-action 'postfix ('reload) result result))))))) + +(define postfix-service-type + (service-type + (name 'postfix) + (extensions + (list + (service-extension etc-service-type + postfix-etc-files) + (service-extension account-service-type + postfix-accounts) + (service-extension setuid-program-service-type + postfix-setuid-programs) + (service-extension activation-service-type + postfix-activation) + (service-extension mail-aliases-service-type + postfix-aliases) + (service-extension certbot-service-type + postfix-certificates) + (service-extension profile-service-type + (compose list postfix-configuration-postfix)) + (service-extension shepherd-root-service-type + postfix-shepherd-service))) + (default-value (postfix-configuration)) + (description + #"- + Run the Postfix MTA. + + This is the top-level system service for Postfix. + + It includes: + - populating /etc/postfix/ with read-only configuration files; + - the user and groups used by Postfix when handling email delivery; + - the special setgid binaries for daily usage, such as "sendmail"; + - the Shepherd service for starting, stopping and *reloading* the + service without restarting it; + - the activation script for creating the required directories and + configuring them with the correct permissions; + - the binaries in the system profile so that one doesn't need to explicilty + include the package when the service is already enabled. + + An extension to the log-rotation service isn't included: the default + rottlog configuration already includes /var/log/maillog in its routine, + so it is kept out. + + The defaults of <postfix-configuration> provide sane default values for + most things, such as group names, data and queue directories, etc. When + used as-is, it creates a Postfix server that sends email from local users + of the domain provided by "/etc/hostname"."#))) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Dovecot ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + +(define-record-type* <dovecot2-configuration> + dovecot2-configuration + make-dovecot2-configuration + dovecot2-configuration? + (dovecot2 dovecot2-configuration-dovecot2 (default dovecot)) + (raw-file dovecot2-configuration-raw-file (default #f)) + (extra-content dovecot2-configuration-extra-content (default "")) + (config-name dovecot2-configuration-config-name (default "dovecot2.conf")) + (user dovecot2-configuration-user (default "dovecot2")) + (group dovecot2-configuration-group (default "dovecot2")) + (untrusted-user dovecot2-configuration-untrusted-user (default "dovenull2")) + (untrusted-group dovecot2-configuration-untrusted-group (default "dovenull2")) + (base-dir dovecot2-configuration-base-dir (default "/var/run/dovecot2")) + (state-dir dovecot2-configuration-state-dir (default "/var/lib/dovecot2")) + (hostname dovecot2-configuration-hostname (default (gethostname)))) + +(define (generate-dovecot-config config) + (match-record config <dovecot2-configuration> + (user group untrusted-user hostname base-dir state-dir extra-content) + (format #f + #"- + protocols = imap + + default_internal_user = ~a + default_internal_group = ~a + default_login_user = ~a + auth_mechanisms = plain login + auth_username_format = %n + + passdb { + driver = shadow + } + userdb { + driver = passwd + } + service auth-worker { + group = shadow + } + + + ssl = required + ssl_cert = </etc/letsencrypt/live/~a/fullchain.pem + ssl_key = </etc/letsencrypt/live/~a/privkey.pem + ssl_dh = <~a/dhparam.pem + + base_dir = ~a + state_dir = ~a + + verbose_proctitle = yes + + mail_location = maildir:~~/Mail:INBOX=~~/Mail/Inbox:LAYOUT=fs + + + namespace inbox { + inbox = yes + mailbox Drafts { + special_use = \Drafts + auto = subscribe + } + mailbox Sent { + special_use = \Sent + auto = subscribe + } + mailbox Archive { + special_use = \Archive + auto = subscribe + } + mailbox Spam { + special_use = \Junk + auto = subscribe + autoexpunge = 30d + } + mailbox Trash { + special_use = \Trash + auto = subscribe + } + } + ~a + "# + user + group + untrusted-user + hostname + hostname + state-dir + base-dir + state-dir + extra-content))) + +(define (dovecot2-etc-files config) + (match-record config <dovecot2-configuration> + (raw-file config-name) + `(("dovecot2" + ,(file-union + "dovecot2" + `((,config-name ,(plain-file config-name + (or raw-file + (generate-dovecot-config config)))))))))) + +(define (dovecot2-accounts config) + (match-record config <dovecot2-configuration> + (user group untrusted-user untrusted-group) + (list + (user-account + (name user) + (group group) + (comment "Dovecot system user") + (home-directory "/var/empty") + (shell (file-append shadow "/sbin/nologin")) + (system? #t)) + (user-account + (name untrusted-user) + (group untrusted-group) + (comment "Dovecot user for untrusted logins") + (home-directory "/var/empty") + (shell (file-append shadow "/sbin/nologin")) + (system? #t)) + (user-group + (name group) + (system? #t)) + (user-group + (name untrusted-group) + (system? #t))))) + +(define (dovecot2-activation config) + (match-record config <dovecot2-configuration> + (base-dir state-dir) + #~(begin + (use-modules (guix build utils)) + (let ((user (getpwnam "root"))) + (format (current-error-port) + "Creating Dovecot base_dir directory: \"~a\".~%" #$base-dir) + (mkdir-p #$base-dir) + (let ((dhparam.pem (string-append #$state-dir "/dhparam.pem"))) + (unless (file-exists? dhparam.pem) + (cond + ((zero? (system* (string-append #$openssl "/bin/openssl") + "dhparam" "-out" dhparam.pem "2048")) + (format (current-error-port) + "Dovecot2 dhparam.pem file created: \"~a\".~%" dhparam.pem)) + (else + (format (current-error-port) + "Failed to create dhparam.pem file: \"~a\".~%" dhparam.pem))))))))) + +(define (dovecot2-shepherd-service config) + (match-record config <dovecot2-configuration> + (dovecot2 config-name) + (let ((config-file (string-append "/etc/dovecot2/" config-name))) + (list + (shepherd-service + (provision '(dovecot2)) + (documentation + #"- + FIXME:DOCUMENTATION Run the Postfix MTA. + + This is the entrypoint for starting the "master" process. Then the + "master" process itself takes responsability of starting all the + required daemons and commands."#) + (start #~(make-forkexec-constructor + (list + #$(file-append dovecot2 "/sbin/dovecot") + "-F" + "-c" + #$config-file))) + (stop #~(make-kill-destructor)) + (actions + (list + (shepherd-action + (name 'configuration) + (documentation + #"- + FIXME:DOCUMENTATION + "#) + (procedure + #~(lambda _ + (format #t "~a~%" #$config-file)))) + (shepherd-action + (name 'reload) + (documentation + #"- + FIXME:DOCUMENTATION Re-read the "master.cf" and "main.cf" configuration files. + + Daemon processes terminate when possible, and when restarted + use the values of the new configuration files. + + This live-reload option is usually preferable over a stop/start + cycle, as it incurs in no interruption of the running service."#) + (procedure + #~(lambda _ + (invoke #$(file-append dovecot "/bin/doveadm") + "-c" + #$config-file + "reload"))))))))))) + +(define (dovecot2-certificates config) + (match-record config <dovecot2-configuration> + (hostname) + (list + (certificate-configuration + (domains (list hostname)) + (deploy-hook + (program-file + "dovecot2-certbot-deploy-hook" + #~(invoke #$(file-append dovecot "/bin/doveadm") "reload"))))))) + +(define dovecot2-service-type + (service-type + (name 'dovecot2) + (extensions + (list + (service-extension etc-service-type + dovecot2-etc-files) + (service-extension account-service-type + dovecot2-accounts) + (service-extension activation-service-type + dovecot2-activation) + (service-extension certbot-service-type + dovecot2-certificates) + (service-extension profile-service-type + (compose list dovecot2-configuration-dovecot2)) + (service-extension shepherd-root-service-type + dovecot2-shepherd-service))) + (default-value (dovecot2-configuration)) + (description + #"- + FIXME:DOCUMENTATION Run the Postfix MTA. + + This is the top-level system service for Postfix. + + It includes: + - populating /etc/postfix/ with read-only configuration files; + - the user and groups used by Postfix when handling email delivery; + - the special setgid binaries for daily usage, such as "sendmail"; + - the Shepherd service for starting, stopping and *reloading* the + service without restarting it; + - the activation script for creating the required directories and + configuring them with the correct permissions; + - the binaries in the system profile so that one doesn't need to explicilty + include the package when the service is already enabled. + + An extension to the log-rotation service isn't included: the default + rottlog configuration already includes /var/log/maillog in its routine, + so it is kept out. + + The defaults of <postfix-configuration> provide sane default values for + most things, such as group names, data and queue directories, etc. When + used as-is, it creates a Postfix server that sends email from local users + of the domain provided by "/etc/hostname"."#))) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;; nanolist ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + +(define-record-type* <nanolist-list-configuration> + nanolist-list-configuration + make-nanolist-list-configuration + nanolist-list-configuration? + (address nanolist-list-configuration-address) + (name nanolist-list-configuration-name) + (description nanolist-list-configuration-description) + (bcc nanolist-list-configuration-bcc (default '())) + (posters nanolist-list-configuration-posters (default '())) + (hidden? nanolist-list-configuration-hidden? (default #f)) + (subscribers-only? nanolist-list-configuration-subscribers-only? (default #f))) + +;; FIXME: where do the logs go to? +(define-record-type* <nanolist-configuration> + nanolist-configuration + make-nanolist-configuration + nanolist-configuration? + (nanolist nanolist-configuration-nanolist (default postfix #;nanolist)) + (user nanolist-configuration-user (default "nanolist")) + (group nanolist-configuration-group (default "nanolist")) + (alias-entry nanolist-configuration-alias-entry (default "nanolist")) + (config-name nanolist-configuration-config-name (default "nanolist.conf")) + (lists nanolist-configuration-lists (default '())) + (state-directory nanolist-configuration-state-directory (default "/var/lib/nanolist")) ) + +(define (generate-nanolist-conf config) + (match-record config <nanolist-configuration> + (state-directory lists) + (format #f + #"- + { + "state-directory": "~a", + "lists": [ + ~a + ] + } + "# + state-directory + (map (lambda (list-config) + (match-record list-config <nanolist-list-configuration> + (name description bcc posters hidden? subscribers-only?) + (format #f + #"- + { + "name": ~a, + "description": ~a, + "bcc": ~a, + "posters": ~a, + "hidden?": ~a, + "subscribers-only?": ~a + } + "# + name + description + bcc + posters + (if hidden? "true" "false") + (if subscribers-only? "true" "false")))) + lists)))) + +(define (nanolist-etc-files config) + (match-record config <nanolist-configuration> + (config-name) + `((,config-name ,(plain-file config-name (generate-nanolist-conf config)))))) + +(define (nanolist-accounts config) + (match-record config <nanolist-configuration> + (user group) + (list + (user-account + (name user) + (group group) + (comment "nanolist system user") + (home-directory "/var/empty") + (shell (file-append shadow "/sbin/nologin")) + (system? #t)) + (user-group + (name group) + (system? #t))))) + +(define (nanolist-activation config) + (match-record config <nanolist-configuration> + (user group state-directory) + #~(begin + (use-modules (guix build utils)) + (format (current-error-port) + "Creating nanolist's subscriptions directory: \"~a\".~%" #$state-directory) + (mkdir-p #$state-directory) + (chown #$state-directory + (passwd:uid (getpwnam #$user)) + (group:gid (getgrnam #$group))) + (chmod #$state-directory #o750)))) + +(define (nanolist-aliases config) + (match-record config <nanolist-configuration> + (nanolist alias-entry lists) + (append + (map (lambda (list) + `(,alias ,alias-entry)) + lists) + `((,alias-entry ,(string-append "|" "/tmp/nanotmp" "/bin/nanolist")))))) + +(define nanolist-service-type + (service-type + (name 'nanolist) + (extensions + (list + (service-extension etc-service-type + nanolist-etc-files) + (service-extension account-service-type + nanolist-accounts) + (service-extension activation-service-type + nanolist-activation) + (service-extension mail-aliases-service-type + nanolist-aliases) + (service-extension profile-service-type + (compose list nanolist-configuration-nanolist)))) + (default-value (nanolist-configuration)) + (description + #"- + FIXME:DOCUMENTATION Run the Postfix MTA. + + This is the top-level system service for Postfix. + + It includes: + - populating /etc/postfix/ with read-only configuration files; + - the user and groups used by Postfix when handling email delivery; + - the special setgid binaries for daily usage, such as "sendmail"; + - the Shepherd service for starting, stopping and *reloading* the + service without restarting it; + - the activation script for creating the required directories and + configuring them with the correct permissions; + - the binaries in the system profile so that one doesn't need to explicilty + include the package when the service is already enabled. + + An extension to the log-rotation service isn't included: the default + rottlog configuration already includes /var/log/maillog in its routine, + so it is kept out. + + The defaults of <postfix-configuration> provide sane default values for + most things, such as group names, data and queue directories, etc. When + used as-is, it creates a Postfix server that sends email from local users + of the domain provided by "/etc/hostname"."#))) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;; OS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + +(define host-name "toph") +(define tld "arrobaponto.org") +(define mail.tld (string-append "mail." tld)) +(define whoami "andreh") +(define me "eu") + +(define gitconfig (plain-file "gitconfig" (format #f #"- + [init] + defaultBranch = main + [user] + email = ci@~a + name = "~a CI" + [advice] + detachedHead = false + "# + host-name + host-name))) + +(operating-system + (locale "fr_FR.UTF-8") + (timezone "America/Sao_Paulo") + (host-name host-name) + (users + (append + (list + (user-account + (name whoami) + (comment "EuAndreh") + (group "users") + (supplementary-groups '("wheel"))) + (user-account + (name "git") + (group "git") + (comment "Public Git user") + (home-directory "/srv/git") + ;; FIXME: disable interactive SSH + #; (shell (file-append shadow "/sbin/nologin")))) + %base-user-accounts)) + (groups + (append + (list + (user-group + (name "git"))) + %base-groups)) + (sudoers-file + (plain-file "sudoers" #"- + root ALL=(ALL) ALL + %wheel ALL=NOPASSWD: ALL + "#)) + (packages + (append + (map (compose list specification->package+output symbol->string) + '(nss-certs + borg + curl + entr + git-minimal + guile-heredoc-latest + ranger + rlwrap + rsync + strace + trash-cli + tree + + dkimproxy + prosody + cgit)) + (list (script "rc" rc.sh) + (script "backup" backup.sh) + (script "cronjob" cronjob.sh) + (script "reconfigure" reconfigure.sh) + (script "with-email" with-email.sh)) + %base-packages)) + (services + (append + (list + (service dhcp-client-service-type) + (service ntp-service-type) + (service shadow-group-service-type) + (service nanolist-service-type) + (service dkimproxyout-service-type + (dkimproxyout-configuration + (domains (list tld)))) + (service git-daemon-service-type + (git-daemon-configuration + (export-all? #t))) + (service cgit-service-type + (cgit-configuration + (nginx '()) + (source-filter (file-append cgit "/lib/cgit/filters/syntax-highlighting.py")) + (about-filter (file-append cgit "/lib/cgit/filters/about-formatting.sh")) + (virtual-root "/git/") + (remove-suffix? #t) + (nocache? #t) + (enable-commit-graph? #t) + (enable-follow-links? #t) + (enable-index-owner? #t) + (enable-log-filecount? #t) + (enable-log-linecount? #t) + (enable-remote-branches? #t) + (enable-subject-links? #t) + (snapshots '("tar.gz" "tar.xz")) + (root-desc "Patches welcome!") + (root-title "EuAndreh repositories") + (logo "/git/static/cgit.png") + (favicon "/git/static/favicon.ico") + (css "/git/static/cgit.css") + (extra-options + '(#"- + enable-blame=1 + readme=:README.md + readme=:README + "#)))) + (service nginx-service-type + (nginx-configuration + (server-blocks + (list + (nginx-server-configuration + (server-name (list tld)) + (listen '("[::]:443 ssl http2" "443 ssl http2")) + (root "/srv/www") + (ssl-certificate (format #f "/etc/letsencrypt/live/~a/fullchain.pem" tld)) + (ssl-certificate-key (format #f "/etc/letsencrypt/live/~a/privkey.pem" tld)) + (locations + (list + (nginx-location-configuration + (uri "/git/static/") + (body + (list + (list "alias " cgit "/share/cgit/;")))) + (nginx-location-configuration + (uri "/git/") + (body + (list + (list "fastcgi_param SCRIPT_FILENAME " cgit "/lib/cgit/cgit.cgi;") + #"- + fastcgi_param SCRIPT_FILENAME ~a/lib/cgit/cgit.cgi; + fastcgi_param PATH_INFO $uri; + fastcgi_param QUERY_STRING $args; + fastcgi_param HTTP_HOST $server_name; + fastcgi_pass 127.0.0.1:9000; + rewrite /git(.*) $1 break; + "#)))))))))) + (simple-service 'create-/srv/www activation-service-type + #~(begin + (use-modules (guix build utils)) + (let ((user (getpwnam #$whoami)) + (git (getpwnam "git"))) + (format (current-error-port) "Creating \"/srv/www\".~%") + (mkdir-p "/srv/www") + (chown "/srv/www" (passwd:uid user) (passwd:gid user)) + (chmod "/srv/www" #o755) + (format (current-error-port) "Setting permissions for \"/srv/git\".~%") + (mkdir-p "/srv/git") + (chown "/srv/git" (passwd:uid git) (passwd:gid git)) + (chmod "/srv/git" #o755)))) + (service prosody-service-type + (prosody-configuration + (modules-enabled + (append + '("groups" "mam") + %default-modules-enabled)) + (admins + (list + (format #f "~a@~a" me whoami))) +#; + (ssl + (ssl-configuration + (certificate "/etc/prosody/certs/arrobaponto.org.crt") + (key "/etc/prosody/certs/arrobaponto.org.key"))) + (c2s-require-encryption? #t) + (s2s-require-encryption? #t) + (s2s-secure-auth? #t) + (authentication "internal_hashed") + (virtualhosts + (list + (virtualhost-configuration + (domain tld)))))) + (service wireguard-service-type + (wireguard-configuration + (addresses '("10.0.0.0/32")) + (peers + (list + (wireguard-peer + (name "velhinho") + (public-key "Mhv8KxB/QXQpNKNtqD57PoFv43TXJ1lg52PJd6TmtwI=") + (allowed-ips '("10.0.0.0/24")) + (keep-alive 25)))))) + (service dovecot2-service-type + (dovecot2-configuration + (hostname mail.tld))) + (service certbot-service-type + (certbot-configuration + (email "eu@euandre.org") + (certificates + (list + (certificate-configuration + (domains (list tld)) + (deploy-hook + (program-file + "prosody-certbot-deploy-hook" + #~(begin + (format (current-error-port) + "Importing new TLS certificates for \"~a\" to Prosody via prosodyctl(8).~%" + #$tld) + (invoke #$(file-append prosody "/bin/prosodyctl") + "--root" + "cert" + "import" + "/etc/letsencrypt/live"))))))))) + (service cyrus-sasl-service-type + (cyrus-sasl-configuration + (services + (list + (cyrus-service-configuration + (name "smtpd.conf")))))) + (service postfix-service-type + (postfix-configuration + (hostname mail.tld) + (main.cf-extra + (format #f + #"- + canonical_maps = inline:{ ~a=~a@~a } + "# + whoami + me + tld)))) + (service mail-aliases-service-type + `(("root" ,whoami) + (,me ,whoami))) + (simple-service 'extra-etc-file etc-service-type + `(("rc" ,rc.sh) + ("gitconfig" ,gitconfig))) + (service openssh-service-type + (openssh-configuration + (password-authentication? #f) + (authorized-keys + `((,whoami ,(plain-file "id_rsa.pub" ssh-pubkey)) + ("git" ,(plain-file "id_rsa.pub" ssh-pubkey)))) + (extra-content #"- + ClientAliveInterval 30 + ClientAliveCountMax 20 + MaxSessions 20 + "#)))) + %base-services)) + (bootloader + (bootloader-configuration + (bootloader grub-bootloader) + (targets '("/dev/vda")) )) + (swap-devices + (list + (swap-space + (target + (uuid "94b47d91-3542-438a-84a9-859fe347ce09"))))) + (file-systems + (append + (list + (file-system + (mount-point "/") + (device + (uuid "4c36d5ad-f996-413e-a55c-c05b7e1876f2" 'btrfs)) + (type "btrfs"))) + %base-file-systems))) |