[PATCH] gnu: services: Add dkimproxy-out.

OpenSubmitted by Julien Lepiller.
Details
4 participants
  • Julien Lepiller
  • Alexey Abramov
  • Ludovic Courtès
  • Tobias Geerinckx-Rice
Owner
unassigned
Severity
normal
J
J
Julien Lepiller wrote on 29 Aug 2019 21:52
(address . guix-patches@gnu.org)
20190829215226.173a4812@sybil.lepiller.eu
Hi guix, the attached patch adds the dkimproxy-out service that I use
for signing my emails (including this one, although it's probably not
valid because it went through a mailing list).

Thanks!
From 114067a7134ceb49dc5bbcef820edc49d62c8d0f Mon Sep 17 00:00:00 2001
From: Julien Lepiller <julien@lepiller.eu>
Date: Thu, 29 Aug 2019 21:48:25 +0200
Subject: [PATCH] gnu: services: Add dkimproxy-out.

* gnu/services/mail.scm (dkimproxy-out-service-type): New variable.
* doc/guix.texi (Mail Services): Document it.
---
doc/guix.texi | 114 +++++++++++++++++++++++
gnu/services/mail.scm | 211 +++++++++++++++++++++++++++++++++++++++++-
2 files changed, 324 insertions(+), 1 deletion(-)

Toggle diff (353 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 1998ad049b..ec0f04fdea 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -17264,6 +17264,120 @@ Mailutils Manual}, for details.
 @end table
 @end deftp
 
+@subsubheading Dkimproxy Outbound Service
+@cindex Dkimproxy Outbound Service
+
+@deffn {Scheme Variable} dkimproxy-out-service-type
+This is the type of the @uref{http://dkimproxy.sourceforge.net/, dkimproxy
+outbound daemon}, whose value should be a @code{dkimproxy-out-configuration}
+object as in this example:
+
+@example
+(service dkimproxy-out-service-type
+         (dkimproxy-out-configuration
+	   (listen "127.0.0.1:10027")
+	   (relay "127.0.0.1:10028")
+	   (sender-map
+	     `(("example.com"
+	        (,(dkimproxy-out-signature-configuration
+		    (type 'dkim)
+		    (key "/etc/mail/dkim/private.key")
+		    (method "relaxed")
+		    (selector "dkim"))))))))
+@end example
+@end deffn
+
+@deftp {Data Type} dkimproxy-out-configuration
+Data type representing the configuration of @command{dkimproxy.out}.
+
+@table @asis
+@item @code{package} (default: @code{dkimproxy})
+The package that provides @command{dkimproxy.out}.
+
+@item @code{listen} (default: @code{#f})
+The interface and port on which to listen for incoming connections (messages
+to sign).
+
+@item @code{relay} (default: @code{#f})
+The interface and port on which to send (relay) the signed messages.
+
+@item @code{list-id-map} (default: @code{'()})
+A map of List-Id to configuration.  The key of the association list must be a
+string representing the content of a List-Id field in the incoming message.
+The associated value is a @code{dkimproxy-out-signature-configuration} object
+that represents configuration that must be applied to messages that contain
+the corresponding List-Id header.
+
+@item @code{sender-map} (default: @code{'()})
+A map of Sender to configuration.  The key of the association list must be a
+string representing a sender of the incoming message.  The associated value is
+a @code{dkimproxy-out-signature-configuration} object that represents
+configuration that must be applied to messages that are sent by the
+corresponding sender.
+
+Note that a @code{sender-map} is required and cannot be empty.
+
+@item @code{reject-error?} (default: @code{#f})
+This option specifies what to do if an error occurs during signing of a
+message.  If this option is set to @code{#t}, the message will be rejected
+with an SMTP error code.  This will result in the MTA sending the message
+to try again later, or bounce it back to the sender (depending on the exact
+error code used).  If this option is set to @code{#f}, the message will be
+allowed to pass through without having a signature added.
+
+A @code{list-id-map} is not required and can be empty.
+
+@item @code{config-file} (default: @code{#f})
+A file-like object that contains the full configuration for dkimproxy-out. If
+this option is not @code{#f}, every other field of this configuration will
+be ignored except the package configuration. See the man page for
+@command{dkimproxy.out} for more information.
+
+@end table
+@end deftp
+
+@deftp {Data Type} dkimproxy-out-signature-configuration
+Data type representing a signature configuration for @command{dkimproxy.out}.
+
+The @code{keyfile}, @code{method}, @code{selector} and @code{signature} of the
+first @code{dkimproxy-out-signature-configuration} in @code{sender-map} are
+taken as the default value of any subsequent configuration in @code{sender-map}
+and any configuration in @code{list-id-map}.
+
+@table @asis
+@item @code{type} (default: @code{'dkim})
+The type of signature, either @code{'dkim} or @code{'domainkey}.
+
+@item @code{key} (default: @code{#f})
+The key file that contains the private key for signing.  If @code{#f}, the
+default key file is used.  It must not be @code{#f} for the first item of
+the @code{sender-map}.
+
+@item @code{algorithm} (default: @code{#f})
+The algorithm used to sign messages, as a string.  If @code{#f}, the default
+algorithm is used.
+
+@item @code{method} (default: @code{#f})
+The canonicalization method to use.  It must not be @code{#f} for the first
+item of the @code{sender-map}.  Accepted value for @command{dkimproxy.out} are
+@code{"simple"}, @code{"relaxed"} and @code{"nofws"}.
+
+@item @code{domain} (default: @code{#f})
+The domain to use for signing.  If @code{#f}, default is to use the domain
+matched.
+
+@item @code{identity} (default: @code{#f})
+The identity to use.  If @code{#f}, default is to not use any identity.
+
+@item @code{selector} (default: @code{#f})
+The selector to use.  If @code{#f}, the default selector is used.  It must
+not be @code{#f} for the first item of the @code{sender-map}.  The selector
+is the first DNS component of the name in which the public DKIM key is
+recorded for the domain used.  For instance, if you have a
+@code{dkim._domainkey.example.com.} record, the selector is @code{dkim}.
+@end table
+@end deftp
+
 @node Messaging Services
 @subsection Messaging Services
 
diff --git a/gnu/services/mail.scm b/gnu/services/mail.scm
index 3de0b4c2f3..670b3c33ff 100644
--- a/gnu/services/mail.scm
+++ b/gnu/services/mail.scm
@@ -70,7 +70,28 @@
             imap4d-configuration
             imap4d-configuration?
             imap4d-service-type
-            %default-imap4d-config-file))
+            %default-imap4d-config-file
+	    
+	    dkimproxy-out-service-type
+
+            dkimproxy-out-signature-configuration
+            dkimproxy-out-signature-configuration-type
+            dkimproxy-out-signature-configuration-key
+            dkimproxy-out-signature-configuration-algorithm
+            dkimproxy-out-signature-configuration-method
+            dkimproxy-out-signature-configuration-domain
+            dkimproxy-out-signature-configuration-identity
+            dkimproxy-out-signature-configuration-selector
+
+            dkimproxy-out-configuration
+            dkimproxy-out-configuration-package
+            dkimproxy-out-configuration-listen
+            dkimproxy-out-configuration-relay
+            dkimproxy-out-configuration-list-id-map
+            dkimproxy-out-configuration-sender-map
+            dkimproxy-out-configuration-reject-error?
+
+            dkimproxy-out-configuration-config-file))
 
 ;;; Commentary:
 ;;;
@@ -1825,3 +1846,191 @@ exim_group = exim
     (list (service-extension
            shepherd-root-service-type imap4d-shepherd-service)))
    (default-value (imap4d-configuration))))
+
+;;;
+;;; Dkimproxy Daemon.
+;;;
+
+(define-record-type* <dkimproxy-out-signature-configuration>
+  dkimproxy-out-signature-configuration make-dkimproxy-out-signature-configuration
+  dkimproxy-out-signature-configuration?
+  (type      dkimproxy-out-signature-configuration-type
+             (default 'dkim))
+  (key       dkimproxy-out-signature-configuration-key
+             (default #f))
+  (algorithm dkimproxy-out-signature-configuration-algorithm
+             (default #f))
+  (method    dkimproxy-out-signature-configuration-method
+             (default #f))
+  (domain    dkimproxy-out-signature-configuration-domain
+             (default #f))
+  (identity  dkimproxy-out-signature-configuration-identity
+             (default #f))
+  (selector  dkimproxy-out-signature-configuration-selector
+             (default #f)))
+
+(define generate-dkimproxy-out-signature-configuration
+  (match-lambda
+    (($ <dkimproxy-out-signature-configuration>
+        type key algorithm method domain identity selector)
+     (string-append
+       (match type
+         ('dkim "dkim")
+         ('domainkeys "domainkeys"))
+       (if (or key algorithm method domain identity selector)
+           (string-append
+             "("
+             (string-join
+              `(
+                ,@(if key
+                    (list (string-append "key=" key))
+                    '())
+               ,@(if algorithm
+                   (list (string-append "a=" algorithm))
+                   '())
+               ,@(if method
+                   (list (string-append "c=" method))
+                   '())
+               ,@(if domain
+                   (list (string-append "d=" domain))
+                   '())
+               ,@(if identity
+                   (list (string-append "i=" identity))
+                   '())
+               ,@(if selector
+                   (list (string-append "s=" selector))
+                   '()))
+              ",")
+             ")")
+           "")))))
+
+(define-record-type* <dkimproxy-out-configuration>
+  dkimproxy-out-configuration make-dkimproxy-out-configuration
+  dkimproxy-out-configuration?
+  (package     dkimproxy-out-configuration-package
+               (default dkimproxy))
+  (listen      dkimproxy-out-configuration-listen
+               (default #f))
+  (relay       dkimproxy-out-configuration-relay
+               (default #f))
+  (list-id-map dkimproxy-out-configuration-list-id-map
+               (default '()))
+  (sender-map  dkimproxy-out-configuration-sender-map
+               (default '()))
+  (reject-error? dkimproxy-out-configuration-sender-reject-error?
+                 (default #f))
+  (config-file dkimproxy-out-configuration-config-file
+               (default #f)))
+
+(define (generate-map-file config filename)
+  (apply plain-file filename
+         (map (lambda (config)
+                (match config
+                  ((selector (config ...))
+                   (string-append
+                     selector " "
+                     (string-join
+                       (map generate-dkimproxy-out-signature-configuration config)
+                       "\n")))
+                  ((selector config)
+                   (string-append
+                     selector " "
+                     (generate-dkimproxy-out-signature-configuration config)))))
+              config)))
+
+(define dkimproxy-out-shepherd-service
+  (match-lambda
+    (($ <dkimproxy-out-configuration> package listen relay list-id-map sender-map
+        reject-error? config-file)
+     (list (shepherd-service
+             (provision '(dkimproxy-out))
+             (requirement '(loopback))
+             (documentation "Outbound DKIM proxy.")
+             (start (let ((proxy (file-append package "/bin/dkimproxy.out")))
+                      (if config-file
+                        #~(make-forkexec-constructor
+                            (list #$proxy (string-append "--conf_file=" #$config-file)
+                                  "--pidfile=/var/run/dkimproxy.out.pid"
+                                  "--user=dkimproxy" "--group=dkimproxy")
+                            #:pid-file "/var/run/dkimproxy.out.pid")
+                        (let* ((first-signature (match sender-map
+                                                 (((sender (signature _ ...)) _ ...)
+                                                   signature)
+                                                 (((sender signature) _ ...)
+                                                   signature)))
+                               (domains
+                                 (apply append
+                                   (map
+                                     (lambda (sender)
+                                       (match sender
+                                         (((domains ...) config)
+                                          domains)
+                                         ((domain config)
+                                          domain)))
+                                     sender-map)))
+                               (sender-map (generate-map-file sender-map
+                                                              "sender.map"))
+                               (listid-map
+                                 (if (null? list-id-map)
+                                     #f
+                                     (generate-map-file list-id-map "listid.map")))
+                               (keyfile
+                                 (dkimproxy-out-signature-configuration-key
+                                   first-signature))
+                               (selector
+                                 (dkimproxy-out-signature-configuration-selector
+                                   first-signature))
+                               (method
+                                 (dkimproxy-out-signature-configuration-method
+                                   first-signature))
+                               (signature
+                                 (match (dkimproxy-out-signature-configuration-type
+                                          first-signature)
+				   ('dkim "dkim")
+				   ('domainkeys "domainkeys"))))
+                          #~(make-forkexec-constructor
+                              `(,#$proxy "--pidfile=/var/run/dkimproxy.out.pid"
+                                "--user=dkimproxy" "--group=dkimproxy"
+                                ,(string-append "--listen=" #$listen)
+                                ,(string-append "--relay=" #$relay)
+                                ,(string-append "--sender_map=" #$sender-map)
+                                ,@(if #$listid-map
+                                    (list
+                                      (string-append "--listid_map=" #$listid-map))
+                                    '())
+                                ,(string-append "--domain=" #$domains)
+                                ,(string-append "--keyfile=" #$keyfile)
+                                ,(string-append "--selector=" #$selector)
+                                ,@(if #$method
+                                      (list
+                                        (string-append "--method=" #$method))
+                                      '())
+                                ,@(if #$reject-error?
+                                      '("--reject_error")
+                                      '())
+                                ,@(if #$signature
+                                      (list
+                                        (string-append "--signature=" #$signature))
+                                      '())))))))
+             (stop #~(make-kill-destructor)))))))
+
+(define %dkimproxy-accounts
+  (list (user-group
+          (name "dkimproxy")
+          (system? #t))
+        (user-account
+          (name "dkimproxy")
+          (group "dkimproxy")
+          (system? #t)
+          (comment "Dkimproxy user")
+          (home-directory "/var/empty")
+          (shell (file-append shadow "/sbin/nologin")))))
+
+(define dkimproxy-out-service-type
+  (service-type
+    (name 'dkimproxy-out)
+    (extensions
+      (list (service-extension account-service-type
+                               (const %dkimproxy-accounts))
+            (service-extension shepherd-root-service-type
+                               dkimproxy-out-shepherd-service)))))
-- 
2.22.0
T
T
Tobias Geerinckx-Rice wrote on 29 Aug 2019 22:04
(address . guix-patches@gnu.org)(address . 37222@debbugs.gnu.org)
871rx3rb62.fsf@nckx
Julien,

Julien Lepiller 写道:
Toggle quote (6 lines)
> Hi guix, the attached patch adds the dkimproxy-out service that
> I use
> for signing my emails (including this one, although it's
> probably not
> valid because it went through a mailing list).

Interesting; I wasn't expecting that. I thought GNU'd finally
fixed their mailman to not break signatures, but you're right:

Received: from a.mx.tobias.gr (localhost [127.0.0.1])
by DKIM-proxy (OpenSMTPD) with ESMTP id a7e379af
for <me@tobias.gr>;
Thu, 29 Aug 2019 19:53:20 +0000 (UTC)
Authentication-Results: tobias.gr; dkim=fail (message has been
altered) header.i=@lepiller.eu; domainkeys=fail (message has
been altered) header.from=julien@lepiller.eu
X-DKIM-Authentication-Results: fail (message has been altered)
Received: from lists.gnu.org (lists.gnu.org [209.51.188.17])
by a.mx.tobias.gr (OpenSMTPD) with ESMTPS id eb36763a
(TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256:NO)
for <me@tobias.gr>;
Thu, 29 Aug 2019 19:53:20 +0000 (UTC)

Kind regards,

T G-R
-----BEGIN PGP SIGNATURE-----

iHUEARYKAB0WIQT12iAyS4c9C3o4dnINsP+IT1VteQUCXWgvtQAKCRANsP+IT1Vt
eZpdAQD+ar0Z5Bz0JLLRI/dvH/JdJcayRgerPUpdaZzQr7yPaQEAvS4LDpLcWYzq
KmPF+mkA+uWCEAKQTuDmep6Dx0JzCAA=
=jLZ3
-----END PGP SIGNATURE-----

L
L
Ludovic Courtès wrote on 4 Sep 2019 14:16
(name . Julien Lepiller)(address . julien@lepiller.eu)(address . 37222@debbugs.gnu.org)
87pnkg5kab.fsf@gnu.org
Hi!

Julien Lepiller <julien@lepiller.eu> skribis:

Toggle quote (4 lines)
>>From 114067a7134ceb49dc5bbcef820edc49d62c8d0f Mon Sep 17 00:00:00 2001
> From: Julien Lepiller <julien@lepiller.eu>
> Date: Thu, 29 Aug 2019 21:48:25 +0200
> Subject: [PATCH] gnu: services: Add dkimproxy-out.
^
No need for “gnu:”, which is only for (gnu packages …). (Confusingly…)
Toggle quote (7 lines)
>
> * gnu/services/mail.scm (dkimproxy-out-service-type): New variable.
> * doc/guix.texi (Mail Services): Document it.
> +@subsubheading Dkimproxy Outbound Service
> +@cindex Dkimproxy Outbound Service
> +

Could you add an introductory paragraph, for instance mentioning what
DKIM is about, linking to the Wikipedia page or something?

Toggle quote (9 lines)
> +@deffn {Scheme Variable} dkimproxy-out-service-type
> +This is the type of the @uref{http://dkimproxy.sourceforge.net/, dkimproxy
> +outbound daemon}, whose value should be a @code{dkimproxy-out-configuration}
> +object as in this example:
> +
> +@example
> +(service dkimproxy-out-service-type
> + (dkimproxy-out-configuration
> + (listen "127.0.0.1:10027")
^
No tabs please. :-)

Toggle quote (4 lines)
> + %default-imap4d-config-file
> +
> + dkimproxy-out-service-type

Ditto (several occurrences in this file.)

Toggle quote (16 lines)
> +(define (generate-map-file config filename)
> + (apply plain-file filename
> + (map (lambda (config)
> + (match config
> + ((selector (config ...))
> + (string-append
> + selector " "
> + (string-join
> + (map generate-dkimproxy-out-signature-configuration config)
> + "\n")))
> + ((selector config)
> + (string-append
> + selector " "
> + (generate-dkimproxy-out-signature-configuration config)))))
> + config)))

This is incorrect since ‘plain-file’ takes exactly two arguments.
Should it be something like:

(plain-file file-name (string-join (map … config)))

?

Toggle quote (4 lines)
> + (domains
> + (apply append
> + (map

Use ‘append-map’ instead.

Toggle quote (9 lines)
> +(define dkimproxy-out-service-type
> + (service-type
> + (name 'dkimproxy-out)
> + (extensions
> + (list (service-extension account-service-type
> + (const %dkimproxy-accounts))
> + (service-extension shepherd-root-service-type
> + dkimproxy-out-shepherd-service)))))

Please add a ‘description’ field.

It would be nice to have a system test too, which I guess could at least
ensure that the generated config is valid and that the daemon happily
starts?

Thanks,
Ludo’.
A
A
Alexey Abramov wrote on 4 Feb 2021 12:19
dkimproxy-out status
(address . julien@lepiller.eu)(address . 37222@debbugs.gnu.org)
871rdw84h9.fsf@delta.lan
Hi Julien,

I copy&pasted your great dkimproxy service, and it works like a charm. Thank you! I was wondering when are you going to address those changes that Ludo has suggested?

If you don't have time for some reason, I am willing to help with it to let it merge. What do you think?

--
Alexey
J
J
Julien Lepiller wrote on 4 Feb 2021 12:37
(name . Alexey Abramov)(address . levenson@mmer.org)(address . 37222@debbugs.gnu.org)
A5DE8A6A-3346-4D97-A27D-388A4E0BA7E3@lepiller.eu
Oh, I totally forgot about it! With FOSDEM and everything, I don't think I'll be able to take care of this before next week. I appreciate any help you can provide!

Le 4 février 2021 06:19:46 GMT-05:00, Alexey Abramov <levenson@mmer.org> a écrit :
Toggle quote (11 lines)
>Hi Julien,
>
>I copy&pasted your great dkimproxy service, and it works like a charm.
>Thank you! I was wondering when are you going to address those changes
>that Ludo has suggested?
>
>If you don't have time for some reason, I am willing to help with it to
>let it merge. What do you think?
>
>--
>Alexey
Attachment: file
?