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