{"id":335,"date":"2019-05-30T00:17:56","date_gmt":"2019-05-29T22:17:56","guid":{"rendered":"http:\/\/blog.le-vert.net\/?p=335"},"modified":"2019-05-30T00:17:56","modified_gmt":"2019-05-29T22:17:56","slug":"dkim-signature-in-postfix-on-debian","status":"publish","type":"post","link":"https:\/\/blog.le-vert.net\/?p=335","title":{"rendered":"DKIM signature in Postfix on Debian"},"content":{"rendered":"<div class=\"twttr_buttons\"><div class=\"twttr_twitter\">\n\t\t\t\t\t<a href=\"http:\/\/twitter.com\/share?text=DKIM+signature+in+Postfix+on+Debian\" class=\"twitter-share-button\" data-via=\"\" data-hashtags=\"\"  data-size=\"default\" data-url=\"https:\/\/blog.le-vert.net\/?p=335\"  data-related=\"\" target=\"_blank\">Tweet<\/a>\n\t\t\t\t<\/div><\/div>\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p>DKIM works by interfacing a proxy in postfix to add priv\/pub signature to out-going emails using a private key and opendkim.<\/p>\n\n\n\n<p>Matching public key will be exported in the DNS server so receiving SMTP can verify in the signature in the metadata matches the public key.<\/p>\n\n\n\n<p>It helps for non being consider as a spamer.<\/p>\n\n\n\n<p>A friend asked me to setup this on his two servers with two different domains so we&#8217;ll be setting up the whole thing here with separate keys pair for each domain\/each server and we&#8217;ll allow both servers to sign emails for these domains.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Install required packages<\/h2>\n\n\n\n<pre class=\"wp-block-preformatted\">apt install opendkim opendkim-tools rename dnsutils<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Create pub\/priv keys sets<\/h2>\n\n\n\n<p>According to <strong>\/usr\/share\/doc\/opendkim\/README.Debian.gz<\/strong> there is a tool named opendkim-genkey that can help generating the key pairs. This tool is in opendkim-tools package.<\/p>\n\n\n\n<p>As we want to allow different servers to sign messages with different key but for the same domain, we&#8217;ll use the origin server short hostname as <em>selector<\/em>. It means the signature will be somehow prefixed with this selector, indicating the receiver SMTP which DNS entry should be queried for getting associated public key. By doing so, we&#8217;ll be able to export different public key for each server.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">opendkim-genkey -b 1024 -d domain1.com -s `hostname -s` --directory=\/etc\/dkimkeys\/\nrename \"s\/`hostname -s`\/`hostname -s`_domain1.com\/\" \/etc\/dkimkeys\/`hostname -s`.{txt,private}\n\nopendkim-genkey -b 1024 -d domain2.com -s `hostname -s` --directory=\/etc\/dkimkeys\/\nrename \"s\/`hostname -s`\/`hostname -s`_domain2.com\/\" \/etc\/dkimkeys\/`hostname -s`.{txt,private}\n\nchown opendkim:opendkim \/etc\/dkimkeys\/`hostname -s`_*\nchmod 0600 \/etc\/dkimkeys\/`hostname -s`_*<\/pre>\n\n\n\n<p>Now you should see the private keys as well as public key as a bind9 snippet in  <strong>\/etc\/dkimkeys<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">drwx------   2 opendkim opendkim 4.0K May 29 22:40 .\n drwxr-xr-x 109 root     root     8.0K May 29 22:11 ..\n -rw-r-----   1 root     opendkim  887 May 29 22:39 ns1234_domain1.com.private\n -rw-r-----   1 root     opendkim  329 May 29 22:39 ns1234_domain1.com.txt\n -rw-r-----   1 root     opendkim  887 May 29 22:40 ns1234_domain2.com.private\n -rw-r-----   1 root     opendkim  339 May 29 22:40 ns1234_domain2.com.txt\n -rw-r--r--   1 root     root      664 Nov  7  2015 README.PrivateKeys<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Register public key in DNS zones<\/h2>\n\n\n\n<p>Now you need to add the public signature in your DNS zone. In this example, the primary bind server for both domain1.com and domain2.com is running on the server itself so we can just do:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">cat \/etc\/dkimkeys\/`hostname -s`_domain1.com.txt &gt;&gt; \/etc\/bind\/zones\/domain1.com\ncat \/etc\/dkimkeys\/`hostname -s`_domain2.com.txt &gt;&gt; \/etc\/bind\/zones\/domain2.com<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>This is very unlikely that method is suitable for you, but you get the idea right ?<\/strong><\/h2>\n\n\n\n<p>Don&#8217;t forget to bump DNS zone serial number and reload bind<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">sed -i \"s\/20\\(1\\|2\\)[0-9][0-9]\\{2\\}[0-9]\\{2\\}[0-9]\\{2\\}\/`date \"+%Y%m%d01\"`\/\" \/etc\/bind\/zones\/domain1.com\nsed -i \"s\/20\\(1\\|2\\)[0-9][0-9]\\{2\\}[0-9]\\{2\\}[0-9]\\{2\\}\/`date \"+%Y%m%d01\"`\/\" \/etc\/bind\/zones\/domain2.com\nsystemctl reload bind9<\/pre>\n\n\n\n<p>We can now check with dig that our DNS server now expose the public key:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">dig -t TXT `hostname -s`._domainkey.domain1.com @127.0.0.1<\/pre>\n\n\n\n<p>Should return something like:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">;; QUESTION SECTION:\n;ns1234._domainkey.domain1.com. IN\tTXT\n\n;; ANSWER SECTION:\nns1234._domainkey.domain1.com. 600 IN TXT\t\"v=DKIM1; h=sha256; k=rsa; \" \"p=aaa1111bbbb3333cccc\"<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Configure OpenDKIM<\/h2>\n\n\n\n<p>Now we need to create a <strong>KeyTable<\/strong> file to match domain, selector and private key file. We also need a <strong>SigningTable<\/strong> to actually ask for signature to be added to outgoing emails.<\/p>\n\n\n\n<p>In <strong>\/etc\/opendkim.conf<\/strong> add the following entries at bottom of files:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">LogWhy                yes\nKeyTable              refile:\/etc\/dkimkeys\/KeyTable\nSigningTable          refile:\/etc\/dkimkeys\/SigningTable<\/pre>\n\n\n\n<p>Then we we&#8217;ll create <strong>\/etc\/dkimkeys\/KeyTable<\/strong> file:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">echo -e \"domain1.com domain1.com:`hostname -s`:\/etc\/dkimkeys\/`hostname -s`_domain1.com.private\" &gt;&gt; \/etc\/dkimkeys\/KeyTable\necho -e \"domain2.com domain2.com:`hostname -s`:\/etc\/dkimkeys\/`hostname -s`_domain2.com.private\" &gt;&gt; \/etc\/dkimkeys\/KeyTable\n<\/pre>\n\n\n\n<p>The file now looks like:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">domain1.com domain1.com:ns1234:\/etc\/dkimkeys\/ns1234_domain1.com.private\ndomain2.com domain2.com:ns1234:\/etc\/dkimkeys\/ns1234_domain2.com.private<\/pre>\n\n\n\n<p>Now we create <strong>\/etc\/dkimkeys\/SigningTable<\/strong> file:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">echo \"*@domain1.com domain1.com\" &gt;&gt; \/etc\/dkimkeys\/SigningTable\necho \"*@domain2.com domain2.com\" &gt;&gt; \/etc\/dkimkeys\/SigningTable<\/pre>\n\n\n\n<p>The file should content:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">*@domain1.com domain1.com\n*@domain2.com domain2.com<\/pre>\n\n\n\n<p>OpenDKIM is now configured, restart it<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">systemctl restart opendkim<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Integrate with Postfix<\/h2>\n\n\n\n<p>On Debian systems Postfix is chrooted so there are a few additionnal steps to get it working correctly:<\/p>\n\n\n\n<p>In <strong>\/etc\/opendkim.conf<\/strong> change the socket path to Postifx chroot:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Socket                  local:\/var\/spool\/postfix\/var\/run\/opendkim\/opendkim.sock<\/pre>\n\n\n\n<p>Create proper folder in Postfix chroot and give proper permissions<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">mkdir -p \/var\/spool\/postfix\/var\/run\/opendkim\nchown opendkim:opendkim \/var\/spool\/postfix\/var\/run\/opendkim<\/pre>\n\n\n\n<p>Add Postfix to opendkim group so it can write to the socket:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">adduser postfix opendkim<\/pre>\n\n\n\n<p>Enable filtering in postfix (postconf commands will edit <strong>\/etc\/postfix\/main.cf<\/strong>):<\/p>\n\n\n\n<p>Missing trailling \/ is not a typo !<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">postconf -e milter_protocol=6\npostconf -e milter_default_action=accept\npostconf -e smtpd_milters=unix:var\/run\/opendkim\/opendkim.sock\npostconf -e non_smtpd_milters=unix:var\/run\/opendkim\/opendkim.sock<\/pre>\n\n\n\n<p>Restart both services:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">systemctl restart opendkim\nsystemctl restart postfix<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Testing<\/h2>\n\n\n\n<p>You can send an email from the server itself using following commands:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">echo \"This is a test\" | mail -s \"Test DKIM domain1.com\" -a \"From: test@domain1.com\" your@real.email\necho \"This is a test\" | mail -s \"Test DKIM domain2.com\" -a \"From: test@domain2.com\" your@real.email<\/pre>\n\n\n\n<p>My your@real.email server runs Postfix with Amavis so I can check the header of the email I just received and I can confirm valid DKIM signature has been seen:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">X-Spam-Status: No, [...] DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VERIFIED=-0.5 [...]<\/pre>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction DKIM works by interfacing a proxy in postfix to add priv\/pub signature to out-going emails using a private key and opendkim. Matching public key will be exported in the DNS server so receiving SMTP can verify in the signature &hellip; <a href=\"https:\/\/blog.le-vert.net\/?p=335\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.le-vert.net\/index.php?rest_route=\/wp\/v2\/posts\/335"}],"collection":[{"href":"https:\/\/blog.le-vert.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.le-vert.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.le-vert.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.le-vert.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=335"}],"version-history":[{"count":21,"href":"https:\/\/blog.le-vert.net\/index.php?rest_route=\/wp\/v2\/posts\/335\/revisions"}],"predecessor-version":[{"id":356,"href":"https:\/\/blog.le-vert.net\/index.php?rest_route=\/wp\/v2\/posts\/335\/revisions\/356"}],"wp:attachment":[{"href":"https:\/\/blog.le-vert.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=335"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.le-vert.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=335"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.le-vert.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=335"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}