aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2022-11-25 16:28:56 -0300
committerEuAndreh <eu@euandre.org>2022-11-25 16:28:56 -0300
commit1a83a3ba00e11a6508880174129a144dec3f1cc0 (patch)
treedd1461effd6c9b03dd1c154ad64f57fe5e175070
parentrm -rf aux/ (diff)
downloadtoph-1a83a3ba00e11a6508880174129a144dec3f1cc0.tar.gz
toph-1a83a3ba00e11a6508880174129a144dec3f1cc0.tar.xz
src/infrastructure/guix/system.scm: Add working version of toph OS
-rw-r--r--Makefile16
-rw-r--r--src/infrastructure/guix/channels.scm20
-rw-r--r--src/infrastructure/guix/system.scm1905
3 files changed, 1931 insertions, 10 deletions
diff --git a/Makefile b/Makefile
index 6f808d7..114cb13 100644
--- a/Makefile
+++ b/Makefile
@@ -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)))