Skip to content

Commit

Permalink
Merge pull request #8 from netmanagers/master
Browse files Browse the repository at this point in the history
feat(dkim): add state to manage DKIM keys
  • Loading branch information
javierbertoli authored May 15, 2021
2 parents 2987146 + e4ac895 commit 0ea2459
Show file tree
Hide file tree
Showing 10 changed files with 320 additions and 37 deletions.
5 changes: 5 additions & 0 deletions docs/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ Installs the rspamd package.

Configures the rspamd package.

``rspamd.dkim_keys``
----------------

Creates and configures the DKIM signing keys for rspamd. See the ``pillar.example`` for examples of how to use it.

``rspamd.service``
^^^^^^^^^^^^^^^^^^

Expand Down
85 changes: 73 additions & 12 deletions pillar.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
# vim: ft=yaml
---
rspamd:
# These are the user and group that will be used for the dkim files
# (both default to `_rspamd`, as set in both Debian and Centos)
# user: _rspamd
# group: _rspamd

use_upstream_repo: true
# If you want to install the redis-server using this formula, set this
# to true. It will install the redis-server package for the supported distros
Expand All @@ -10,6 +15,20 @@ rspamd:
# Default: false
manage_redis: true

# Allows you to manage dkim keys through rspamadm. See examples below
# See https://www.rspamd.com/doc/modules/dkim_signing.html#dkim-key-management
# Default: false
manage_dkim_keys: true

# Where dkim keys will be stored
dkim_keys_dir: /var/lib/rspamd/dkim

# If true, all files under the <dkim_keys_dir> not managed by the formula
# will be removed. Use with caution
# Default: false. All existing files will be kept
# clean_dkim_keys_dir: false

## RSPAMD config
config:
# rspamd uses two type of config files to modify the configuration,
# determined by the file extension:
Expand Down Expand Up @@ -96,21 +115,23 @@ rspamd:
write_servers: localhost
read_servers: localhost

dkim:
dkim_signing:
module: true # File 'ext' will be set to 'conf'
# These parameters here are merged and/or overwritten by those
# defined in the `rspamd:dkim_keys` dict (see below)
domain:
example.com:
this.should.be.merged.too:
selector: dkim
privkey: /var/lib/rspamd/dkim/example.com.dkim.key
path: /var/lib/rspamd/dkim/should.be.merged.with.the.others.key
example.net:
selector: dkim
privkey: /var/lib/rspamd/dkim/example.net.dkim.key
nom: 1234
stri: cadena1
lis:
- a
- b
- c
selector: fancy_selector
path: /var/lib/rspamd/dkim/this.will.be.overrriden
nom: 1234
stri: cadena1
lis:
- a
- b
- c

override:
rspamd:
Expand All @@ -123,7 +144,47 @@ rspamd:
# yamllint enable rule:line-length

options: {}
logging: {}
logging:
some: parameter
worker-normal: {}
worker-controller: {}
worker-proxy: {}

## DKIM keys management
# All domains and keys added in this dict and enabled (the default) will be
# ADDED to the `config:local:dkim_signing:domain` dict above. For any given domain,
# values set in this dict will OVERRIDE those set in the dict above if the same
# key exist in both places.
# Keys created here can be disabled (ie, not added to the config file) just
# setting `ehable: false` to it.
# Keys not listed here will be REMOVED from disk if `clean_dkim_keys_diri: true`
dkim_keys:
identifier1:
# If key should be used in the config generated by the rspamd.config state
# Default: true
# enable: true

domain: example.net # required. If not set, the identifier will be used

# Selector used to build the selector, ie <something>._domainkey
# Defaults to `domain` above
# selector: something

# File where the privkey will be stored
# Defaults to <dkim_keys_dir>/<domain>.key
# privkey_file:

# File where the txt output will be stored
# Defaults to <dkim_keys_dir>/<domain>.txt
# txt_file:

# bits: # key size. Defaults to 2048
# type: # Either 'rsa' or 'ed25519'. Defaults to 'rsa'

identifier2: # Will be managed and enabled in the config
domain: example.com
type: ed25519
identifier3: # Will be managed but disabled in the config
enable: false
domain: example.org
just.a.domain: {} # This will be enabled with all the defaults
27 changes: 27 additions & 0 deletions rspamd/config.sls
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,33 @@
{%- set filename = rspamd.base_dir ~ '/' ~ type ~ '.d/' ~ file ~ '.' ~ ext %}
{%- endif %}
{# If `manage_dkim_keys: true` #}
{%- if file == 'dkim_signing' and rspamd.manage_dkim_keys %}
{# add a domain entry in the dkim_signing dict if it does not exist #}
{%- do params.update({'domain': {} }) if params.domain is not defined %}
{%- for dom, domp in rspamd.dkim_keys.items() %}
{%- set domain = domp.domain | default(dom) %}
{# enable is default for domains, so if the parameter is undefined,
it means we consider it true #}
{%- if domp.enable | default(true) %}
{%- set privkey_file = domp.privkey_file | default( rspamd.dkim_keys_dir ~ '/' ~ domain ~ '.key') %}
{%- set selector = domp.selector | default(domain) %}
{# add the domain to the domains dict if it does not exist #}
{%- do params.domain.update({domain: {} }) if params.domain[domain] is not defined %}
{# update the selector and file for the domain #}
{%- do params.domain[domain].update({'path': privkey_file }) %}
{%- do params.domain[domain].update({'selector': selector}) if params.domain[domain]['selector'] is not defined %}
{%- else %}
{%- do params.domain.pop(domain) if params.domain[domain] is defined %}
{%- endif %}
{%- endfor %}
{%- endif %}
{%- set enabled = false if (params == {} or (params.enable is defined and params.enable == false)) else true %}
{{ filename }}:
{%- if enabled %}
Expand Down
8 changes: 8 additions & 0 deletions rspamd/defaults.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@ rspamd:
repo:
humanname: Rspamd Official Repository

user: _rspamd
group: _rspamd

manage_redis: false
redis_pkg: redis
redis_service: redis

manage_dkim_keys: false
dkim_keys_dir: /var/lib/rspamd/dkim
clean_dkim_keys_dir: false
dkim_keys: {}

pkg: rspamd

base_dir: /etc/rspamd
Expand Down
62 changes: 62 additions & 0 deletions rspamd/dkim_keys.sls
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# vim: ft=sls

{% from "rspamd/map.jinja" import rspamd with context %}
{%- if rspamd.manage_dkim_keys -%}
rspamd_dkim_keys_dir:
file.directory:
- name: {{ rspamd.dkim_keys_dir }}
- user: {{ rspamd.user }}
- group: {{ rspamd.group }}
- mode: 755
- clean: {{ rspamd.clean_dkim_keys_dir }}
- require:
- pkg: rspamd_pkg
{%- for key, params in rspamd.dkim_keys.items() %}
{%- set domain = params.domain | default(key) %}
{%- set selector = params.selector | default(domain) %}
{%- set privkey_file = params.privkey_file | default( rspamd.dkim_keys_dir ~ '/' ~ domain ~ '.key') %}
{%- set txt_file = params.txt_file | default( rspamd.dkim_keys_dir ~ '/' ~ domain ~ '.txt') %}
{%- set bits = params.bits | default(2048) %}
{%- set type = params.type | default('rsa') %}
rspamd_dkim_keys_{{ key }}:
cmd.run:
- name: |
mkdir -p {{ rspamd.dkim_keys_dir }} && \
rspamadm dkim_keygen \
--selector '{{ selector }}' \
--bits {{ bits }} \
--domain {{ domain }} \
--privkey {{ privkey_file }} \
> {{ txt_file }}
- unless: test -f {{ txt_file }}
- require_in:
- file: rspamd_dkim_keys_{{ privkey_file }}_perms
- file: rspamd_dkim_keys_{{ txt_file }}_perms
- sls: config
- service: rspamd_service
rspamd_dkim_keys_{{ privkey_file }}_perms:
file.managed:
- name: {{ privkey_file }}
- user: {{ rspamd.user }}
- group: {{ rspamd.group }}
- mode: 640
- onlyif: test -f {{ privkey_file }}
- require_in:
- file: rspamd_dkim_keys_dir
rspamd_dkim_keys_{{ txt_file }}_perms:
file.managed:
- name: {{ txt_file }}
- user: {{ rspamd.user }}
- group: {{ rspamd.group }}
- mode: 640
- onlyif: test -f {{ txt_file }}
- require_in:
- file: rspamd_dkim_keys_dir
{%- endfor %}
{%- endif %}
1 change: 1 addition & 0 deletions rspamd/init.sls
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@

include:
- .install
- .dkim_keys
- .config
- .service
4 changes: 3 additions & 1 deletion rspamd/osfamilymap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ RedHat:
file: /etc/yum.repos.d/rspamd.repo
key_url: https://rspamd.com/rpm-stable/gpg.key
# yamllint disable rule:line-length
baseurl: http://rspamd.com/rpm-stable/{{ grains['os']|lower }}-{{ grains['osmajorrelease']}}/{{ grains['osarch'] }}/
baseurl: http://rspamd.com/rpm-stable/{{ grains['os']|lower }}-{{ grains['osmajorrelease'] }}/{{ grains['osarch'] }}/
# yamllint enable rule:line-length
gpgcheck: 1
gpgkey: http://rspamd.com/rpm/gpg.key
{% endif %}

Arch:
use_upstream_repo: false
user: root
group: root
70 changes: 70 additions & 0 deletions test/integration/default/controls/config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# frozen_string_literal: true

config_dir = '/etc/rspamd'

rspamd_conf_override = <<~RSPAMD_CONF_OVERRIDE
options {
.include "$CONFDIR/options.inc";
.include(try=true; priority=1,duplicate=merge) "$LOCAL_CONFDIR/local.d/options.inc";
.include(try=true; priority=10) "$LOCAL_CONFDIR/override.d/options.inc";
pidfile = "$RUNDIR/rspamd.pid";
}
RSPAMD_CONF_OVERRIDE

dkim_domains = <<~DKIM_DOMAINS
domain {
example.com {
path = "/var/lib/rspamd/dkim/example.com.key";
selector = "example.com";
}
example.net {
path = "/var/lib/rspamd/dkim/example.net.key";
selector = "fancy_selector";
}
just.a.domain {
path = "/var/lib/rspamd/dkim/just.a.domain.key";
selector = "just.a.domain";
}
this.should.be.merged.too {
path = "/var/lib/rspamd/dkim/should.be.merged.with.the.others.key";
selector = "dkim";
}
}
DKIM_DOMAINS

control 'rspamd configuration' do
impact 1.0
title 'Manage the Rspamd Configuration'
desc '
Manage the rspamd configuration
'
tag 'rspamd', 'config'

%w[
local.d
override.d
].each do |d|
describe file("#{config_dir}/#{d}") do
it { should be_directory }
it { should be_owned_by 'root' }
it { should be_grouped_into 'root' }
its('mode') { should cmp '0755' }
end
end

describe file("#{config_dir}/rspamd.conf.override") do
its('content') { should include(rspamd_conf_override) }
end

describe file("#{config_dir}/local.d/redis.conf") do
its('content') { should match(/^read_servers = "localhost";/) }
its('content') { should match(/^write_servers = "localhost";/) }
end

describe file("#{config_dir}/local.d/dkim_signing.conf") do
its('content') { should match(/^lis = \["a", "b", "c"\];/) }
its('content') { should match(/^nom = 1234;/) }
its('content') { should match(/^stri = "cadena1";/) }
its('content') { should include(dkim_domains) }
end
end
42 changes: 42 additions & 0 deletions test/integration/default/controls/file.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true

case platform[:family]
when 'linux', 'arch'
rspamd_user = rspamd_group = 'root'
else
rspamd_user = rspamd_group = '_rspamd'
end

control 'rspamd dkim_keys' do
impact 1.0
title 'Manage the DKIM key files and directory'
desc '
Manage the DKIM keys and directory
'
tag 'rspamd', 'dkim_keys'

describe file('/var/lib/rspamd/dkim') do
it { should be_directory }
it { should be_owned_by rspamd_user }
it { should be_grouped_into rspamd_group }
its('mode') { should cmp '0755' }
end

%w[
example.com.key
example.com.txt
example.net.key
example.net.txt
example.org.key
example.org.txt
just.a.domain.key
just.a.domain.txt
].each do |f|
describe file("/var/lib/rspamd/dkim/#{f}") do
it { should be_file }
it { should be_owned_by rspamd_user }
it { should be_grouped_into rspamd_group }
its('mode') { should cmp '0640' }
end
end
end
Loading

0 comments on commit 0ea2459

Please sign in to comment.