by Joshua Branson — April 01, 2022
I am back to working on various records to support <opensmtpd-configuration>
for the opensmtpd-service-type
. I decided about a week ago, to just do some
of the changes in the records that I want to do. Once I am satisfied with the
updates, then I will work on making the code output the smtpd.conf
file. I am
fairly please with some of the changes to the records that I have made.
Feel free to play with the examples below from my repo!
https://notabug.org/jbranso/linode-guix-system-configuration.git
guile -L .
scheme@(guile-user)> ,use (opensmtpd-records)
scheme@(guile-user)> ,m (opensmtpd-records)
(opensmtpd-table (name "table") (values (list "hello" "world")))
Please note, that I am still working on changing the record names, so various things that work as described in the blog post may not work in a week or two.
a pretty useful <opensmtpd-configuration>
gives no errors
(define example-opensmtpd-configuration
(let ([interface "lo"]
[creds-table (opensmtpd-table
(name "creds")
(values
(list
(cons "joshua"
"$some$encrypted$password"))))]
[receive-action (opensmtpd-action-local-delivery-configuration
(name "receive")
(method (opensmtpd-maildir-configuration
(pathname "/home/%{rcpt.user}/Maildir")
(junk #t)))
(virtual (opensmtpd-table
(name "virtual")
(values (list "josh" "jbranso@dismail.de")))))]
[filter-dkimsign (opensmtpd-filter
(name "dkimsign")
(exec #t)
(proc (string-append "/path/to/dkimsign -d gnucode.me -s 2021-09-22 -c relaxed/relaxed -k "
"/path/to/dkimsign-key user nobody group nobody")))]
[smtp.gnucode.me (opensmtpd-pki
(domain "smtp.gnucode.me")
(cert "opensmtpd.scm")
(key "opensmtpd.scm"))])
(opensmtpd-configuration
(mta-max-deferred 50)
(queue
(opensmtpd-queue-configuration
(compression #t)))
(smtp
(opensmtpd-smtp-configuration
(max-message-size "10M")))
(srs
(opensmtpd-srs-configuration
(ttl-delay "5d")))
(listen-ons
(list
(opensmtpd-listen-on
(interface interface)
(port 25)
(secure-connection "tls")
(filters (list (opensmtpd-filter-phase
(name "noFRDNS")
(phase "commit")
(conditions (list (opensmtpd-conditions-configuration
(condition "fcrdns")
(not #t))))
(decision "disconnect")
(message "No FCRDNS"))))
(pki smtp.gnucode.me))
;; this lets local users logged into the system via ssh send email
(opensmtpd-listen-on
(interface interface)
(port 465)
(secure-connection "smtps")
(pki smtp.gnucode.me)
(auth creds-table)
(filters (list filter-dkimsign)))
(opensmtpd-listen-on
(interface interface)
(port 587)
(secure-connection "tls-require")
(pki smtp.gnucode.me)
(auth creds-table)
(filters (list filter-dkimsign)))))
(matches (list
(opensmtpd-match
(action (opensmtpd-action-relay-configuration
(name "relay")))
(for (opensmtpd-match-configuration
(option "for any")))
(from (opensmtpd-match-configuration
(option "from any")))
(auth (opensmtpd-match-configuration
(option "auth"))))
(opensmtpd-match
(action receive-action)
(from (opensmtpd-match-configuration
(option "from any")))
(for (opensmtpd-match-configuration
(option "for domain")
(value (opensmtpd-table
(name "domain-table")
(values (list "gnucode.me" "gnu-hurd.com")))))))
(opensmtpd-match
(action receive-action)
(for (opensmtpd-match-configuration
(option "for local")))))))))
However there’s still some work to do because this doesn’t work:
(opensmtpd-configuration->mixed-text-file example-opensmtpd-configuration)
ice-9/boot-9.scm:1685:16: In procedure raise-exception:
error: value: unbound variable
Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
scheme@(opensmtpd-records) [12]> ,bt
In /home/joshua/prog/gnu/guix/guix-config/linode-guix-system-configuration/opensmtpd-records.scm:
1667:3 4 (opensmtpd-configuration->mixed-text-file #<<opensmtpd-configuration> package: #<package opensmtpd@6.8.0p2 gnu/packages/mail.scm:2979 7f1e1a3…>)
1628:9 3 (opensmtpd-configuration-fieldname->string _ _ _)
1634:10 2 (list-of-records->string _ _)
1669:99 1 (_ _)
In ice-9/boot-9.scm:
1685:16 0 (raise-exception _ #:continuable? _)
I have also sanitized the <opensmtpd-conditions-configuration>
If you type in various incorrectly written
<opensmtpd-conditions-configuration>
records, then you will get some helpful
error messages:
if condition is rdns, src, helo, mail-from, rcpt-to, then they must also provide a table
What is interesting, is that I do not know how to sanitize a whole record when the record is initiated. I can only have a parent record sanitize it. For example the following record is invalid, because if the ’condition’ is “src”, then you need to provide a table. However, the following works in a REPL.
(opensmtpd-conditions-configuration (condition "src"))
$11 = #<<opensmtpd-conditions-configuration> condition: "src" not: #f regex: #f table: #f>
But when you put the same incorrect
<opensmtpd-conditions-configuration>
into an<opensmtpd-filter-phase>
, then you get the right error message.(opensmtpd-filter-phase (name "filter") (phase "helo") (decision "bypass") (conditions (list (opensmtpd-conditions-configuration (condition "src"))))) <opensmtpd-conditions-configuration>'s fieldname 'condition' values of 'src', 'helo', 'mail-from', or 'rcpt-to' need a corresponding 'table' of type <opensmtpd-table>. eg: (opensmtpd-conditions-configuration (condition "src") (table (opensmtpd-table (name "src-table") (values (list "hello" "cat"))))) ice-9/boot-9.scm:1685:16: In procedure raise-exception: Throw to key `bad!' with args `((#<<opensmtpd-conditions-configuration> condition: "mail-from" not: #f regex: #f table: #f>))'. Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
make sure that there are no duplicate conditions
(opensmtpd-filter-phase (name "noFRDNS") (phase "commit") (conditions (list (opensmtpd-conditions-configuration (condition "fcrdns") (not #t)) (opensmtpd-conditions-configuration (condition "fcrdns") (not #t)))) (decision "disconnect") (message "No FCRDNS")) <opensmtpd-filter-phase> fieldname: 'conditions' is a list of unique <opensmtpd-conditions-configuration> records. ice-9/boot-9.scm:1685:16: In procedure raise-exception: Throw to key `bad!' with args `((#<<opensmtpd-conditions-configuration> condition: "fcrdns" not: #t regex: #f table: #f> #<<opensmtpd-conditions-configuration> condition: "fcrdns" not: #t regex: #f table: #f>))'. Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
sanitize the phase-name
(opensmtpd-filter-phase (name "filter") (phase "hello") (decision "bypass") (conditions (list (opensmtpd-conditions-configuration (condition "auth"))))) <opensmtpd-filter-phase> fieldname: 'phase' is of type string. The string can be either 'connect', 'helo', 'mail-from', 'rcpt-to', 'data', or 'commit.' ice-9/boot-9.scm:1685:16: In procedure raise-exception: Throw to key `bad!' with args `("hello")'. Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue.
Changes to the records eleminate potential errors like
misspelling a table name, or calling an action that is not defined.
The <opensmtpd-configuration>
used to be defined this way:
(service opensmtpd-service
(opensmtpd-configuration
(includes ...)
(tables ...)
(pkis ...)
(filters ...)
(listen-on ...)
(actions ...)
(matches ...)))
It would be possible to give a table a name of “password-table”, but then later to refer to it as “passwords-table”, which would NOT have worked. Like so:
(service opensmtpd-service
(opensmtpd-configuration
(tables (list (opensmtpd-table
(name "<passwords-table>")
(values
(list
(cons "joshua"
"$encrypted$password"))))))
(listen-on
(list (opensmtpd-listen-on
(auth "<password-table>" ))))
(actions ...)
(matches ...)))
Now instead, you define the table where it is used!
(opensmtpd-listen-on
(interface interface)
(port 587)
(secure-connection "tls-require")
(pki smtp.gnucode.me)
(auth (opensmtpd-table
(name "creds")
(values
(list
(cons "joshua"
"$encrypted$password")))))
(filters (list filter-dkimsign)))