rspamd
Contents
About
Fast, free and open-source spam filtering system.
Rspamd filtering system is designed to be fast, modular and easily scalable system. Rspamd core is written in C language using event driven processing model. Plugins for rspamd can be written in Lua programming language. Rspamd is designed to process connections completely asynchronous and do not block anywhere in code.
Please see:
Installation
Show running configuration
1 rspamadm configdump|less
Ports
https://rspamd.com/doc/workers/
Socket |
Name |
Beschreibung |
Status |
127.0.0.1:6379 |
redis |
Key-Value-Storage |
Aktiv |
127.0.0.1:11332 |
rspamd worker-proxy |
handles requests forwarding and milter protocol |
Aktiv |
127.0.0.1:11333 |
rspamd worker-normal |
scan mail messages |
Aktiv |
127.0.0.1:11334 |
rspamd worker-controller |
configuration actions |
Aktiv |
127.0.0.1:11335 |
rspamd worker-fuzzy |
stores fuzzy hashes |
Inaktiv |
Testing the spam filter - GTUBE
The GTUBE ("Generic Test for Unsolicited Bulk Email") is a 68-byte test string used to test anti-spam systems, in particular those based on SpamAssassin. In SpamAssassin, it carries an antispam score of 1000 by default, which would be sufficient to trigger any installation.
GTUBE pattern
1 XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X
Configuration
Unix Permissions
Adjust Unix-Permissions
Redis backend
Older rspamd versions default to SQLite backends, nowadays redis hasbecome the default backend, but may need redundancy.
Ports
- redis-server port tcp/6379
- redis-sentinel port tcp/26379 (default)
- redis-sentinel port tcp/5000
Redis OS optimizations
Redis complains to get some optimizations.
29516:M 22 Oct 2020 17:54:01.615 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect. 29516:M 22 Oct 2020 17:54:01.615 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled (set to 'madvise' or 'never').
Redis replication
The idea is to have two servers.
Node1 is a writeable master,
node2 is a read-only slave replicating from master.
Any rspamd instance should write to node1, read from both and failover to node2 if node1 is gone. For higher redundancy, use sentinel.
The detailed redis.conf is lengthy with about 1865 lines of code. I'll stick to the changes from the defaults for my setup.
TLS enablement and hardening not yet included. First need a certificate.
Redis replication master
/etc/redis/redis.conf
1 ################################## NETWORK #####################################
2
3 # By default, if no "bind" configuration directive is specified, Redis listens
4 # for connections from all the network interfaces available on the server.
5 # It is possible to listen to just one or multiple selected interfaces using
6 # the "bind" configuration directive, followed by one or more IP addresses.
7 #
8 # Examples:
9 #
10 # bind 192.168.1.100 10.0.0.1
11 # bind 127.0.0.1 ::1
12 #
13 # ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the
14 # internet, binding to all the interfaces is dangerous and will expose the
15 # instance to everybody on the internet. So by default we uncomment the
16 # following bind directive, that will force Redis to listen only into
17 # the IPv4 loopback interface address (this means Redis will be able to
18 # accept connections only from clients running into the same computer it
19 # is running).
20 #
21 # IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES
22 # JUST COMMENT THE FOLLOWING LINE.
23 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 #bind 127.0.0.1 ::1
25 bind 127.0.0.1 ::1 IP.ADD.RE.SS
26 …
27 # If the master is password protected (using the "requirepass" configuration
28 # directive below) it is possible to tell the replica to authenticate before
29 # starting the replication synchronization process, otherwise the master will
30 # refuse the replica request.
31 #
32 # masterauth <master-password>
33 #
34 # However this is not enough if you are using Redis ACLs (for Redis version
35 # 6 or greater), and the default user is not capable of running the PSYNC
36 # command and/or other commands needed for replication. In this case it's
37 # better to configure a special user to use with replication, and specify the
38 # masteruser configuration as such:
39 #
40 # masteruser <username>
41 #
42 # When masteruser is specified, the replica will authenticate against its
43 # master using the new AUTH form: AUTH <username> <password>.
44 masteruser replicator
45 masterauth zail1vaiThihair6al6HohsetohaiNgood8eighaibakethee4Fahgeingoaz5uo
46 …
47 # Using an external ACL file
48 #
49 # Instead of configuring users here in this file, it is possible to use
50 # a stand-alone file just listing users. The two methods cannot be mixed:
51 # if you configure users here and at the same time you activate the exteranl
52 # ACL file, the server will refuse to start.
53 #
54 # The format of the external ACL user file is exactly the same as the
55 # format that is used inside redis.conf to describe users.
56 #
57 aclfile /etc/redis/users.acl
58 …
This file contains the credentials and is going to be synced between the nodes. Unfortunately rspamd does not yet support redis acls and the user default must be configured.
/etc/redis/users.acl
Make sure /etc/redis/users.acl does not contain tabulators.
29516:M 22 Oct 2020 17:54:01.615 # Aborting Redis startup because of ACL errors: '/etc/redis/users.acl:1: username 'replicator on' contains invalid characters. '/etc/redis/users.acl:2: username 'default on' contains invalid characters. WARNING: ACL errors detected, no change to the previously active ACL rules was performed
node1 than node2
Check replication
Primary
1 # redis-cli
2 127.0.0.1:6379> AUTH secure_long_password
3 OK
4 ### WITHOUT A REPLICATING NODE
5 127.0.0.1:6379> ROLE
6 1) "master"
7 2) (integer) 0
8 3) (empty array)
9 ### WITH A REPLICATING NODE
10 127.0.0.1:6379> ROLE
11 1) "master"
12 2) (integer) 112
13 3) 1) 1) "192.168.1.42"
14 2) "6379"
15 3) "112"
16 127.0.0.1:6379> INFO
Secondary
1 # redis-cli
2 127.0.0.1:6379> AUTH secure_long_password
3 OK
4 ### REPLICATION BROKEN
5 127.0.0.1:6379> ROLE
6 1) "slave"
7 2) "192.168.0.11"
8 3) (integer) 6379
9 4) "connect"
10 5) (integer) -1
11 ### REPLICATION WORKING
12 127.0.0.1:6379> ROLE
13 1) "slave"
14 2) "80.64.180.48"
15 3) (integer) 6379
16 4) "connected"
17 5) (integer) 70
18 127.0.0.1:6379> INFO
Redis replication slave
Additionally the replication slave has been pointed to the master.
/etc/redis/redis.conf
1 ################################# REPLICATION #################################
2
3 # Master-Replica replication. Use replicaof to make a Redis instance a copy of
4 # another Redis server. A few things to understand ASAP about Redis replication.
5 #
6 # +------------------+ +---------------+
7 # | Master | ---> | Replica |
8 # | (receive writes) | | (exact copy) |
9 # +------------------+ +---------------+
10 #
11 # 1) Redis replication is asynchronous, but you can configure a master to
12 # stop accepting writes if it appears to be not connected with at least
13 # a given number of replicas.
14 # 2) Redis replicas are able to perform a partial resynchronization with the
15 # master if the replication link is lost for a relatively small amount of
16 # time. You may want to configure the replication backlog size (see the next
17 # sections of this file) with a sensible value depending on your needs.
18 # 3) Replication is automatic and does not need user intervention. After a
19 # network partition replicas automatically try to reconnect to masters
20 # and resynchronize with them.
21 #
22 # replicaof <masterip> <masterport>
23 replicaof 192.168.0.11 6379
Rspamd client
Adjust the local configuration of each rspamd service
/etc/rspamd/local.d/redis.conf
1 write_servers = "mail1.example.com:6379";
2 #write_servers = "192.168.0.11";
3 read_servers = "master-slave:mail1.example.com:6379:30,mail3.example.com:6379:10";
4 #read_servers = "master-slave:192.168.0.11:6379:30;192.168.0.13:6379:10";
5 #read_servers = "master-slave:mail3.example.com:6379:30,mail1.example.com:6379:10";
6 #read_servers = "master-slave:192.168.0.13:6379:30;192.168.0.11:6379:10";
7 password = rau4aivie9she0thoo1JeiWul5doovohTh3dah5leinee3ahvie7aino6kee7Cha
Redis redundancy with sentinel
With multiple nodes it is possible to achieve
- higher-availability,
- redundancy,
- faster learning,
- consistent decisions and
- maintainability.
Links
There is no setup with just 2 sentinels
src/sentinel.c
1 // because it requires quorum ≥ 1.
2 1645 if (quorum <= 0) return "Quorum must be 1 or greater.";
3 …
4 // because there must be more votes than 50% of the nodes
5 // and the number of votes must be greater than the configured quorum).
6 4026 voters_quorum = voters/2+1;
7 4027 if (winner && (max_votes < voters_quorum || max_votes < master->quorum))
8 4028 winner = NULL;
Example: 2 nodes, 1 down
Install redis-sentinel
1 aptitude install -t buster-backports redis-sentinel
Changes to sentinel.conf
Bind to port 5000
Bind to address bind 127.0.0.1 ::1 192.168.0.11
Set requirepass thai7eijae7Theizootei1la
/etc/redis/sentinel.conf
1 # Example sentinel.conf
2
3 # *** IMPORTANT ***
4 #
5 # By default Sentinel will not be reachable from interfaces different than
6 # localhost, either use the 'bind' directive to bind to a list of network
7 # interfaces, or disable protected mode with "protected-mode no" by
8 # adding it to this configuration file.
9 #
10 # Before doing that MAKE SURE the instance is protected from the outside
11 # world via firewalling or other means.
12 #
13 # For example you may use one of the following:
14 #
15 # bind 127.0.0.1 ::1 192.168.1.1
16 bind 127.0.0.1 ::1 192.168.0.11
17 #
18 # protected-mode no
19
20 # port <sentinel-port>
21 # The port that this sentinel instance will run on
22 #port 26379
23 port 5000
24
25 # By default Redis Sentinel does not run as a daemon. Use 'yes' if you need it.
26 # Note that Redis will write a pid file in /var/run/redis-sentinel.pid when
27 # daemonized.
28 daemonize yes
29
30 # When running daemonized, Redis Sentinel writes a pid file in
31 # /var/run/redis-sentinel.pid by default. You can specify a custom pid file
32 # location here.
33 pidfile "/var/run/sentinel/redis-sentinel.pid"
34
35 # Specify the log file name. Also the empty string can be used to force
36 # Sentinel to log on the standard output. Note that if you use standard
37 # output for logging but daemonize, logs will be sent to /dev/null
38 logfile "/var/log/redis/redis-sentinel.log"
39
40 # sentinel announce-ip <ip>
41 # sentinel announce-port <port>
42 #
43 # The above two configuration directives are useful in environments where,
44 # because of NAT, Sentinel is reachable from outside via a non-local address.
45 #
46 # When announce-ip is provided, the Sentinel will claim the specified IP address
47 # in HELLO messages used to gossip its presence, instead of auto-detecting the
48 # local address as it usually does.
49 #
50 # Similarly when announce-port is provided and is valid and non-zero, Sentinel
51 # will announce the specified TCP port.
52 #
53 # The two options don't need to be used together, if only announce-ip is
54 # provided, the Sentinel will announce the specified IP and the server port
55 # as specified by the "port" option. If only announce-port is provided, the
56 # Sentinel will announce the auto-detected local IP and the specified port.
57 #
58 # Example:
59 #
60 # sentinel announce-ip 1.2.3.4
61
62 # dir <working-directory>
63 # Every long running process should have a well-defined working directory.
64 # For Redis Sentinel to chdir to /tmp at startup is the simplest thing
65 # for the process to don't interfere with administrative tasks such as
66 # unmounting filesystems.
67 dir "/var/lib/redis"
68
69 # sentinel monitor <master-name> <ip> <redis-port> <quorum>
70 #
71 # Tells Sentinel to monitor this master, and to consider it in O_DOWN
72 # (Objectively Down) state only if at least <quorum> sentinels agree.
73 #
74 # Note that whatever is the ODOWN quorum, a Sentinel will require to
75 # be elected by the majority of the known Sentinels in order to
76 # start a failover, so no failover can be performed in minority.
77 #
78 # Replicas are auto-discovered, so you don't need to specify replicas in
79 # any way. Sentinel itself will rewrite this configuration file adding
80 # the replicas using additional configuration options.
81 # Also note that the configuration file is rewritten when a
82 # replica is promoted to master.
83 #
84 # Note: master name should not include special characters or spaces.
85 # The valid charset is A-z 0-9 and the three characters ".-_".
86 sentinel myid b7e6832629034548dd2d1e63cbac146b9dfd189b
87
88 # sentinel auth-pass <master-name> <password>
89 #
90 # Set the password to use to authenticate with the master and replicas.
91 # Useful if there is a password set in the Redis instances to monitor.
92 #
93 # Note that the master password is also used for replicas, so it is not
94 # possible to set a different password in masters and replicas instances
95 # if you want to be able to monitor these instances with Sentinel.
96 #
97 # However you can have Redis instances without the authentication enabled
98 # mixed with Redis instances requiring the authentication (as long as the
99 # password set is the same for all the instances requiring the password) as
100 # the AUTH command will have no effect in Redis instances with authentication
101 # switched off.
102 #
103 # Example:
104 #
105 # sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
106
107 # sentinel auth-user <master-name> <username>
108 #
109 # This is useful in order to authenticate to instances having ACL capabilities,
110 # that is, running Redis 6.0 or greater. When just auth-pass is provided the
111 # Sentinel instance will authenticate to Redis using the old "AUTH <pass>"
112 # method. When also an username is provided, it will use "AUTH <user> <pass>".
113 # In the Redis servers side, the ACL to provide just minimal access to
114 # Sentinel instances, should be configured along the following lines:
115 #
116 # user sentinel-user >somepassword +client +subscribe +publish \
117 # +ping +info +multi +slaveof +config +client +exec on
118
119 # sentinel down-after-milliseconds <master-name> <milliseconds>
120 #
121 # Number of milliseconds the master (or any attached replica or sentinel) should
122 # be unreachable (as in, not acceptable reply to PING, continuously, for the
123 # specified period) in order to consider it in S_DOWN state (Subjectively
124 # Down).
125 #
126 # Default is 30 seconds.
127 sentinel deny-scripts-reconfig yes
128
129 # requirepass <password>
130 #
131 # You can configure Sentinel itself to require a password, however when doing
132 # so Sentinel will try to authenticate with the same password to all the
133 # other Sentinels. So you need to configure all your Sentinels in a given
134 # group with the same "requirepass" password. Check the following documentation
135 # for more info: https://redis.io/topics/sentinel
136
137 # sentinel parallel-syncs <master-name> <numreplicas>
138 #
139 # How many replicas we can reconfigure to point to the new replica simultaneously
140 # during the failover. Use a low number if you use the replicas to serve query
141 # to avoid that all the replicas will be unreachable at about the same
142 # time while performing the synchronization with the master.
143 sentinel monitor mymaster 127.0.0.1 6379 2
144
145 # sentinel failover-timeout <master-name> <milliseconds>
146 #
147 # Specifies the failover timeout in milliseconds. It is used in many ways:
148 #
149 # - The time needed to re-start a failover after a previous failover was
150 # already tried against the same master by a given Sentinel, is two
151 # times the failover timeout.
152 #
153 # - The time needed for a replica replicating to a wrong master according
154 # to a Sentinel current configuration, to be forced to replicate
155 # with the right master, is exactly the failover timeout (counting since
156 # the moment a Sentinel detected the misconfiguration).
157 #
158 # - The time needed to cancel a failover that is already in progress but
159 # did not produced any configuration change (SLAVEOF NO ONE yet not
160 # acknowledged by the promoted replica).
161 #
162 # - The maximum time a failover in progress waits for all the replicas to be
163 # reconfigured as replicas of the new master. However even after this time
164 # the replicas will be reconfigured by the Sentinels anyway, but not with
165 # the exact parallel-syncs progression as specified.
166 #
167 # Default is 3 minutes.
168 sentinel config-epoch mymaster 0
169
170 # SCRIPTS EXECUTION
171 #
172 # sentinel notification-script and sentinel reconfig-script are used in order
173 # to configure scripts that are called to notify the system administrator
174 # or to reconfigure clients after a failover. The scripts are executed
175 # with the following rules for error handling:
176 #
177 # If script exits with "1" the execution is retried later (up to a maximum
178 # number of times currently set to 10).
179 #
180 # If script exits with "2" (or an higher value) the script execution is
181 # not retried.
182 #
183 # If script terminates because it receives a signal the behavior is the same
184 # as exit code 1.
185 #
186 # A script has a maximum running time of 60 seconds. After this limit is
187 # reached the script is terminated with a SIGKILL and the execution retried.
188
189 # NOTIFICATION SCRIPT
190 #
191 # sentinel notification-script <master-name> <script-path>
192 #
193 # Call the specified notification script for any sentinel event that is
194 # generated in the WARNING level (for instance -sdown, -odown, and so forth).
195 # This script should notify the system administrator via email, SMS, or any
196 # other messaging system, that there is something wrong with the monitored
197 # Redis systems.
198 #
199 # The script is called with just two arguments: the first is the event type
200 # and the second the event description.
201 #
202 # The script must exist and be executable in order for sentinel to start if
203 # this option is provided.
204 #
205 # Example:
206 #
207 # sentinel notification-script mymaster /var/redis/notify.sh
208
209 # CLIENTS RECONFIGURATION SCRIPT
210 #
211 # sentinel client-reconfig-script <master-name> <script-path>
212 #
213 # When the master changed because of a failover a script can be called in
214 # order to perform application-specific tasks to notify the clients that the
215 # configuration has changed and the master is at a different address.
216 #
217 # The following arguments are passed to the script:
218 #
219 # <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
220 #
221 # <state> is currently always "failover"
222 # <role> is either "leader" or "observer"
223 #
224 # The arguments from-ip, from-port, to-ip, to-port are used to communicate
225 # the old address of the master and the new address of the elected replica
226 # (now a master).
227 #
228 # This script should be resistant to multiple invocations.
229 #
230 # Example:
231 #
232 # sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
233
234 # SECURITY
235 #
236 # By default SENTINEL SET will not be able to change the notification-script
237 # and client-reconfig-script at runtime. This avoids a trivial security issue
238 # where clients can set the script to anything and trigger a failover in order
239 # to get the program executed.
240
241 sentinel leader-epoch mymaster 0
242
243 # REDIS COMMANDS RENAMING
244 #
245 # Sometimes the Redis server has certain commands, that are needed for Sentinel
246 # to work correctly, renamed to unguessable strings. This is often the case
247 # of CONFIG and SLAVEOF in the context of providers that provide Redis as
248 # a service, and don't want the customers to reconfigure the instances outside
249 # of the administration console.
250 #
251 # In such case it is possible to tell Sentinel to use different command names
252 # instead of the normal ones. For example if the master "mymaster", and the
253 # associated replicas, have "CONFIG" all renamed to "GUESSME", I could use:
254 #
255 # SENTINEL rename-command mymaster CONFIG GUESSME
256 #
257 # After such configuration is set, every time Sentinel would use CONFIG it will
258 # use GUESSME instead. Note that there is no actual need to respect the command
259 # case, so writing "config guessme" is the same in the example above.
260 #
261 # SENTINEL SET can also be used in order to perform this configuration at runtime.
262 #
263 # In order to set a command back to its original name (undo the renaming), it
264 # is possible to just rename a command to itsef:
265 #
266 # SENTINEL rename-command mymaster CONFIG CONFIG
267 # Generated by CONFIG REWRITE
268 protected-mode no
269 user default on nopass ~* +@all
270 sentinel current-epoch 0
rspamd client
Activate usage of sentinels in rspamd to determine write_servers and read_servers
/etc/rspamd/local.d/redis.conf
1 sentinels = "192.168.0.11, 192.168.0.12, 192.168.0.13"; # Servers list (default port 5000)
2 sentinel_watch_time = 1min; # How often Rspam will query sentinels for masters and slaves
3 sentinel_masters_pattern = "^mymaster.*$"; # Defines masters pattern to match in Lua syntax (no pattern means all masters)
4 servers = "192.168.0.11, 192.168.0.12, 192.168.0.13"; # Defines the initial set of servers that would be used if no sentinel can be accessed
5
Always fix #Unix Permissions.
worker-controller
Create passwords for read-only and write access with rspamadm pw
Change passwords for website
/etc/rspamd/local.d/worker-controller.inc
1 ### RSPAMD local.d config
2
3 count = 1;
4
5 # password for read-only commands
6 password = "$2$dc3s6ifn37iccs1tmnk74hzwxb6gpcbn$qak7a9wy1wcom7f99hwtxf1t3nxcht5pfsyd4t7w46qxrqofswgy";
7 # password for write commands
8 enable_password: "$2$6zfmwfmsebg31wwtrkxuezjuiiymddhi$6mbejg5camddsg1fqeu1oh1jnd15p1qsk1jbfkyzj97b5qbb8q5b";
9 # list or map with IP addresses that are treated as secure
10 # so all commands are allowed from these IPs without passwords
11 secure_ip = "127.0.0.1";
12 secure_ip = "::1";
13 # directory where interface static files are placed (usually ${WWWDIR})
14 static_dir = "${WWWDIR}";
15 # path where controller save persistent stats about rspamd
16 # (such as scanned messages count)
17 #stats_path =
Always fix #Unix Permissions.
Check Webfrontend (over ssh-tunnel)
1 ssh -L 8080:localhost:11334 mx1.rockstable.it
Then simply call in your browser: http://localhost:8080/
worker-fuzzy
Enable fuzzy worker
/etc/rspamd/local.d/worker-fuzzy.inc
Configure fuzzy check
/etc/rspamd/local.d/fuzzy_check.conf
1 rule "FUZZY_LOCAL" {
2 # List of servers. Can be an array or multi-value item
3 servers = "127.0.0.1:11335";
4
5 # List of additional mime types to be checked in this fuzzy ("*" for any)
6 mime_types = ["*"];
7
8 # Maximum global score for all maps combined
9 max_score = 20.0;
10
11 # Ignore flags that are not listed in maps for this rule
12 skip_unknown = yes;
13
14 # If this value is false (i.e. no), then allow learning for this fuzzy rule
15 read_only = no;
16
17 # Fast hash type
18 algorithm = "mumhash";
19 # This is used for binary parts and for text parts (size in bytes)
20 min_bytes = 1024;
21 # Text parts only: minimum number of words
22 min_length = 64;
23 # Divide min_bytes by 4 for texts
24 text_multiplier = 4.0,
25
26 # Minimum dimensions for images (in pixels)
27 #min_height = 500;
28 #min_width = 500;
29
30 # Scan all archives mime parts (e.g. zip attachments)
31 scan_archives = true;
32 # If part has num words < min_length, use direct hash instead of fuzzy
33 short_text_direct_hash = true;
34
35 # Apply fuzzy logic for text parts
36 text_shingles = true;
37 # Skip images if needed
38 skip_images = false;
39
40 symbol = "FUZZY_UNKNOWN";
41 fuzzy_map = {
42 FUZZY_DENIED {
43 max_score = 20.0;
44 flag = 1;
45 }
46 FUZZY_PROB {
47 max_score = 10.0;
48 flag = 2;
49 }
50 FUZZY_WHITE {
51 max_score = 2.0;
52 flag = 3;
53 }
54 }
55 }
Restart rspamd
systemctl restart rspamd.service
DomainKeys Identified Mail (DKIM)
Configure DKIM
1 DOMAIN="rockstable.it"
2 SELECTOR="$(date +%Y)"
3 DKIM_PATH="/var/lib/rspamd/dkim"
4 DKIM_FILE="$DKIM_PATH/$DOMAIN.$SELECTOR"
5
6 install -o root -g _rspamd -m 2710 \
7 -d "$DKIM_PATH"
8 rspamadm dkim_keygen \
9 -b 2048 -s "$SELECTOR" \
10 -k "$DKIM_FILE".key \
11 |tee "$DKIM_FILE".txt
12
13 2019._domainkey IN TXT ( "v=DKIM1; k=rsa; "
14 "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm1b+tmkhLzUAW0p2PpePX5XOXBqPobfUnkyEmcc4QJ6WArk67m8CSO1KStZMbK2PC81hWtSoMf4S0qprfn30ARRvlqMJYGuyE26Tk4yYZvG7oS6tszEB9HKe88SEjD6Ax6YMFtcNAZctd9kcjfLZI7YmTJXKkGaxJH8qNklr5oXco5Ka2ZQuSQKiOCiDoffeck6yQSNdMIH77GXzw"
15 "K+g2v6dab57FSOFzbNXWEkdfqZd9Ig12/vLBhPQ4gWxlpeHKR/y6LLZXfi3xBS5XtWVrt6PHqYhsLt39X0lwJosoFGcq6AfUaCgTljHq1fhkvR5cS+JJ14dpTZ6cqnbii9V2QIDAQAB"
16 ) ;
Or as a shell script
/usr/local/sbin/generate_dkim.sh
1 #!/bin/bash
2
3 if [ "${#@}" -ge "1" ]; then
4 DOMAINS=( "$@" )
5 else
6 echo "No domains given as arguments. Exiting…"
7 exit 1
8 fi
9
10 SELECTOR="$(date +%Y)"
11 DKIM_PATH="/var/lib/rspamd/dkim"
12 RSPAMD_GRP="_rspamd"
13 [ -d "$DKIM_PATH" ] || \
14 install -o root -g "$RSPAMD_GRP" -m 2710 \
15 -d "$DKIM_PATH"
16
17 for DOMAIN in "${DOMAINS[@]}"; do
18 echo
19 echo "Processing domain '$DOMAIN'"
20
21 DKIM_FILE="$DKIM_PATH/$DOMAIN.$SELECTOR"
22
23 if [ -f "$DKIM_FILE.pem" ] || [ -f "$DKIM_FILE.txt" ] ; then
24 echo "DKIM file for domain '$DOMAIN' already exists. Skipping…"
25 else
26 rspamadm dkim_keygen \
27 -b 2048 -s "$SELECTOR" \
28 -k "$DKIM_FILE".key \
29 |tee "$DKIM_FILE".txt
30 if [ "$?" ]; then
31 echo "DKIM file for domain '$DOMAIN' has been created."
32 else
33 echo "DKIM file for domain '$DOMAIN' could not be created. Error …"
34 fi
35 fi
36 find "$DKIM_PATH" -type f -name "$DOMAIN.$SELECTOR*"
37 done
38
39 echo "Fixing unix permissions"
40 find \
41 /etc/rspamd/local.d \
42 /var/lib/rspamd/dkim \
43 -type f \
44 -exec chown root:"$RSPAMD_GRP" {} \; \
45 -exec chmod 0640 {} \;
Use it like
1 ### SIMPLE
2 dkim_generate.sh domain1.tld domain2.tld domain3.tld
3 ### OR WITH INPUT FROM FILE WITH XARGS
4 grep '^\s*[^#]' /etc/postfix/virtual_mailbox_domains \
5 |cut -f1 -d\
6 |xargs dkim_generate.sh
7 ### OR WITH A SUBSHELL
8 dkim_generate.sh \
9 $(grep '^\s*[^#]' /etc/postfix/virtual_mailbox_domains \
10 |cut -f1 -d\ )
View TXT records
Or as a more powerful shell script, which can generate MX, DKIM, SPF, DMARC records, as an overview.
Be careful with the heredocs with <<-EOF Only the prefixing TAB-chars are removed, the wiki gives spaces…
/usr/local/sbin/dns_generate.sh
1 #!/bin/bash
2
3 # NO WARRANTY, NO LIABILITY, NO FITNESS, AS IS, …
4 # HAVE FUN
5
6 PROGRAM="$(basename "$0")"
7 SPF_POLICY='mx'
8 DKIM_SELECTOR="$(date +%Y)"
9 DKIM_PATH="/var/lib/rspamd/dkim"
10 DMARC_POLICY="p=none;adkim=r;aspf=r"
11 RSPAMD_GRP="_rspamd"
12 CA="letsencrypt.org"
13 CAA="false"
14 SPF="false"
15 DKIM="false"
16 DMARC="false"
17 DOMAIN_EXIT=false
18 MX="false"
19 WIDTH_MAX=64
20 NO_DOMAIN="true"
21
22 usage () {
23 cat <<-EOF
24 $PROGRAM [OPTIONS] [--] domain1 [domain2] [domainN]
25
26 Options:
27 -c[ca]|--caa[=ca] Generate CAA records
28 -d[selector]|--dkim[=selector] Generate DKIM records
29 -h|--help Show this help
30 -m'mx1,mx2'|--mx'mx1,mx2' Generate MX records
31 -p[policy|--dmarc[=policy] Generate DMARC records
32 -s[policy]|--spf[=policy] Generate SFP records
33
34 When specifiing the optional arguments,
35 make sure to leave no whitespace or
36 it wil be interpreted as a domain.
37 EOF
38 exit 0
39 }
40
41 # Note that we use "$@" to let each command-line parameter expand to a
42 # separate word. The quotes around "$@" are essential!
43 # We need TEMP as the 'eval set --' would nuke the return value of getopt.
44 TEMP=$(getopt \
45 -o 'c::,h,d::,p::,s::,m:' \
46 --long 'caa::,help,dkim::,dmarc::,spf::,mx:' \
47 -n "$PROGRAM" -- "$@")
48
49 if [ $? -ne 0 ]; then
50 echo 'Terminating...' >&2
51 exit 1
52 fi
53
54 # Note the quotes around "$TEMP": they are essential!
55 eval set -- "$TEMP"
56 unset TEMP
57
58 while true; do
59 case "$1" in
60 '-h'|'--help')
61 usage
62 shift
63 continue
64 ;;
65 '-m'|'--mx')
66 MX="true"
67 MX_RAW="$2"
68 shift 2
69 continue
70 ;;
71 '-c'|'--caa')
72 # c has an optional argument. As we are in quoted mode,
73 # an empty parameter will be generated if its optional
74 # argument is not found.
75 CAA="true"
76 case "$2" in
77 '')
78 :
79 ;;
80 *)
81 CA="$2"
82 ;;
83 esac
84 shift 2
85 continue
86 ;;
87 '-d'|'--dkim')
88 # c has an optional argument. As we are in quoted mode,
89 # an empty parameter will be generated if its optional
90 # argument is not found.
91 DKIM="true"
92 case "$2" in
93 '')
94 :
95 ;;
96 *)
97 DKIM_SELECTOR="$2"
98 ;;
99 esac
100 shift 2
101 continue
102 ;;
103 '-p'|'--dmarc')
104 # c has an optional argument. As we are in quoted mode,
105 # an empty parameter will be generated if its optional
106 # argument is not found.
107 DMARC="true"
108 case "$2" in
109 '')
110 :
111 ;;
112 *)
113 DMARC_POLICY="$2"
114 ;;
115 esac
116 shift 2
117 continue
118 ;;
119 '-s'|'--spf')
120 # c has an optional argument. As we are in quoted mode,
121 # an empty parameter will be generated if its optional
122 # argument is not found.
123 SPF="true"
124 case "$2" in
125 '')
126 :
127 ;;
128 *)
129 SPF_POLICY="$2"
130 ;;
131 esac
132 shift 2
133 continue
134 ;;
135 '--')
136 shift
137 break
138 ;;
139 *)
140 echo 'Internal error!' >&2
141 exit 1
142 ;;
143 esac
144 done
145
146
147 if [ "${#@}" -ge "1" ]; then
148 DOMAINS=( "$@" )
149 NO_DOMAIN=false
150 else
151 echo "No domains given as arguments." >&2
152 fi
153
154 ### CHECK DNS DOMAINS AGAINST REGEX
155 DNS_NAME_REGEX='(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?'
156 echo 'Processing domains:'
157 for arg; do
158 if ! grep -P "$DNS_NAME_REGEX" <<< "$arg"; then
159 echo "Domain '$arg' is not a valid domain."
160 DOMAIN_EXIT=true
161 fi
162 done
163
164 if $DOMAIN_EXIT; then
165 echo "Exiting…"
166 exit 2
167 fi
168
169 declare -a MXS
170 MXS=( ${MX_RAW//,/ } )
171
172 [ -d "$DKIM_PATH" ] || \
173 install -o root -g "$RSPAMD_GRP" -m 2710 \
174 -d "$DKIM_PATH"
175 grep -q '^v=spf1 ' <<< "$SPF_POLICY" || \
176 SPF_POLICY="v=spf1 $SPF_POLICY"
177
178 grep -q '^v=DMARC1; ' <<< "$DMARC_POLICY" || \
179 DMARC_POLICY="v=DMARC1; $DMARC_POLICY"
180
181 DMARC_POLICY="$(sed -r 's/([^ ]);([^ ])/\1; \2/g' <<< "$DMARC_POLICY")"
182
183 cat <<-EOI
184
185 Options:
186 CAA '$CAA' '$CA'
187 MX '$MX' '${MXS[*]}'
188 SPF '$SPF' '$SPF_POLICY'
189 DKIM SELECTOR '$DKIM' '$DKIM_SELECTOR'
190 DMARC '$DMARC' '$DMARC_POLICY'
191
192 EOI
193
194 if $NO_DOMAIN || ( ! $MX && ! $SPF && ! $DKIM && ! $DMARC); then
195 echo "Nothing to do." >&2
196 exit 0
197 fi
198
199
200 dns_multiline () {
201 local STRING
202 local BRACED
203
204 STRING="$1"
205 BRACED="${2:-false}"
206
207 if [ "${#STRING}" -gt "$WIDTH_MAX" ]; then
208 STRING="$(fold -s -w "$WIDTH_MAX" <<< "$STRING" \
209 |sed 's/^/\"/;s/$/\"/')"
210 if $BRACED; then
211 STRING="$(echo -e "( $STRING\n) ;" )"
212 STRING="$( sed \
213 -e '1p' \
214 -e '1d;s/^\s*/\t\t\t /' \
215 -e '$s/^\s*//' \
216 <<< "$STRING" )"
217 else
218 STRING="$( sed \
219 -e 's/^\s*/\t\t\t /' \
220 <<< "$STRING" )"
221 fi
222 echo "$STRING"
223 else
224 echo "\"$STRING\""
225 fi
226 }
227
228 SPF_POLICY="$(dns_multiline "$SPF_POLICY")"
229 DMARC_POLICY="$(dns_multiline "$DMARC_POLICY" "true")"
230
231 ### CAA
232 if $MX && $CAA; then
233 cat <<-EOF
234
235 ### MX CAA
236 $(for MX_RECORD in "${MXS[@]}"; do
237 echo "$MX_RECORD. CAA 128 issue \"$CA\""
238 done
239 )
240 EOF
241 fi
242
243 for DOMAIN in "${DOMAINS[@]}"; do
244 echo -e '\n\n'"Processing domain '$DOMAIN'"
245
246 ### MX
247 if $MX; then
248 cat <<-EOF
249
250 ### MX
251 \$ORIGIN $DOMAIN.
252 $(for MX_RECORD in "${MXS[@]}"; do
253 echo "@ MX 10 $MX_RECORD."
254 done
255 )
256 EOF
257 fi
258
259 ### SPF
260 if $SPF; then
261 cat <<-EOF
262
263 ### SPF
264 \$ORIGIN $DOMAIN.
265 @ TXT $SPF_POLICY
266 EOF
267 fi
268
269 ### DKIM
270 if $DKIM; then
271 echo -e "\n### DKIM"
272 DKIM_FILE="$DKIM_PATH/$DOMAIN.$DKIM_SELECTOR"
273 echo -n "DKIM file for domain '$DOMAIN' "
274 if [ -f "$DKIM_FILE.pem" ] || [ -f "$DKIM_FILE.txt" ] ; then
275 echo "already exists. Skipping…"
276 else
277 rspamadm dkim_keygen \
278 -b 2048 -s "$DKIM_SELECTOR" \
279 -k "$DKIM_FILE".key \
280 > "$DKIM_FILE".txt
281 if [ "$?" ]; then
282 echo " has been created."
283 else
284 echo " could not be created. Error …"
285 fi
286 fi
287 ls -1 "$DKIM_FILE"*
288 #cat "$DKIM_FILE.txt"
289 head -n1 "$DKIM_FILE.txt"
290 DKIM_PUB="$(sed -e '1d;$d' "$DKIM_FILE.txt" \
291 |sed -e ':a;/$/{N;s/\n//;ba}' \
292 |sed 's/[[:space:]"]*//g')"
293 dns_multiline "$DKIM_PUB"
294 tail -n1 "$DKIM_FILE.txt"
295 fi
296
297 ### DMARC
298 if $DMARC; then
299 cat <<-EOF
300
301 ### DMARC
302 \$ORIGIN $DOMAIN.
303 _dmarc TXT $DMARC_POLICY
304
305 EOF
306 fi
307 done
308
309 echo "Fixing unix permissions" >&2
310 find \
311 /etc/rspamd/local.d \
312 /var/lib/rspamd/dkim \
313 -type f \
314 -exec chown root:"$RSPAMD_GRP" {} \; \
315 -exec chmod 0640 {} \;
Use it like
1 dns_generate.sh -c -d2021 \
2 -m'mail1.example.com mail3.example.com' \
3 -p'v=DMARC1; p=none; adkim=r; aspf=r' \
4 -s'v=spf1 mx' -- \
5 domain1.tld domain2.tld
6 grep -v '^#' /etc/postfix/virtual_mailbox_domains \
7 |cut -f1 -d\ \
8 |xargs dns_generate.sh \
9 -c -d2021 \
10 -m'mail1.example.com mail3.example.com' \
11 -p'v=DMARC1; p=none; adkim=r; aspf=r' \
12 -s'v=spf1 mx' -- \
13 |tee vmd.txt
View TXT records
/etc/rspamd/local.d/dkim_signing.conf
Always fix #Unix Permissions.
Configure your TXT record in DNS as in file
/var/lib/rspamd/dkim/rockstable.it.2019.txt
1 $TTL 86400
2 $ORIGIN rockstable.it.
3
4 ; SOA RECORD WITH INCREMENTED SERIAL OMITTED
5
6 ; DKIM DOMAINKEY
7 2019._domainkey TXT ( "v=DKIM1; k=rsa; "
8 "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5Ld6R37vRthQgqQlLkWs+HDxPNz/sTn/qCQooBtWMlTjz3487TM7owU4ryEg1RKsflxPvZrxcDnEthib6ckiH4LpFRYdK3kCkpoCNshPZruXcY0fV+Qv/pJ12nlKBe7jTcqAVhvbh/P6eyYdERCuDFN6yiWJtjKOKgt4sKZHtSR12ZPlPLHguR9C+rXQzoNbV69WaWOo0HpDaPEvj"
9 "ZRXawEBfczQ7ArYzEu6V737PIlsBdEK29Jg/rQozBBN3ZzrfdR3gFHYytDZimVdx7rZ2KIvFR+E1l+EQnjw8EW/6Hk5yCjCr0HcgnWPJ88enNOS2EifhYLp8Wyocj+UFBcY1QIDAQAB"
10 ) ;
Reload rspamd
1 systemctl reload rspamd.service
Check it!
There should be a DKIM-Header in the mails you send. The selector is specified by s=2019.
1 …
2 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=rockstable.it;
3 s=2019; t=1602958680; h=from:from:sender:reply-to:subject:subject:date:date:
4 message-id:message-id:to:to:cc:mime-version:mime-version:
5 content-type:content-type:
6 content-transfer-encoding:content-transfer-encoding:
7 in-reply-to:in-reply-to:references:references;
8 bh=VJsvh7GQqRsC7z/aMdedXfoWJ3TGCqWZKhAQC8zBYtU=;
9 b=oJzj33E6XjYdJrvSIB1rrSnaZuWT4oD3xDUbn3VkACul6z+gaAwFhKLJXv8Z6MwtjWfxjb
10 3iUwbdfthJ7d8h84XT+cIUWX9INGK9YUSBiG4TjArt3Y/7AtlW/6z0qz/6/T1US0ULTdlZ
11 97/DGp31mhypWSEYK+kCEOFJbMRQ0ZzDipRMhygrbZgFONaap16FXh5SDwyCcbRHuM18A2
12 wEKjiDbHz80OToPRECsfSQr+Pag1+VN5/4pXTeXziIIagt+V/tWMgd5afhrotLGhP8sEBf
13 PYWuHe3UFICHnn2xX1gmm2ZZ1F4iv2RMiIRx0KqoTVJbf2oCr3n7O4jazhpYyQ==
14 …
Authenticated Received Chain (ARC)
Just link to dkim_signing.conf because they are using the same backend for handling signatures.
Antivirus Checks
Make sure http is allowed to the following hosts to download virus definitions.
- db.local.clamav.net
- database.clamav.net
Install clamav-daemon
1 aptitude install clamav-daemon clamav
Adjust ClamAV Config
Change LocalSocket
Add TCPAddr and TCPSocket
/etc/clamav/clamd.conf
1 #Automatically Generated by clamav-daemon postinst
2 #To reconfigure clamd run #dpkg-reconfigure clamav-daemon
3 #Please read /usr/share/doc/clamav-daemon/README.Debian.gz for details
4 #LocalSocket /var/run/clamav/clamd.ctl
5 LocalSocket /run/clamav/clamd.ctl
6 FixStaleSocket true
7 LocalSocketGroup clamav
8 LocalSocketMode 666
9 # TemporaryDirectory is not set to its default /tmp here to make overriding
10 # the default with environment variables TMPDIR/TMP/TEMP possible
11 User clamav
12 ScanMail true
13 ScanArchive true
14 ArchiveBlockEncrypted false
15 MaxDirectoryRecursion 15
16 FollowDirectorySymlinks false
17 FollowFileSymlinks false
18 ReadTimeout 180
19 MaxThreads 12
20 MaxConnectionQueueLength 15
21 LogSyslog false
22 LogRotate true
23 LogFacility LOG_LOCAL6
24 LogClean false
25 LogVerbose false
26 PreludeEnable no
27 PreludeAnalyzerName ClamAV
28 DatabaseDirectory /var/lib/clamav
29 OfficialDatabaseOnly false
30 SelfCheck 3600
31 Foreground false
32 Debug false
33 ScanPE true
34 MaxEmbeddedPE 10M
35 ScanOLE2 true
36 ScanPDF true
37 ScanHTML true
38 MaxHTMLNormalize 10M
39 MaxHTMLNoTags 2M
40 MaxScriptNormalize 5M
41 MaxZipTypeRcg 1M
42 ScanSWF true
43 ExitOnOOM false
44 LeaveTemporaryFiles false
45 AlgorithmicDetection true
46 ScanELF true
47 IdleTimeout 30
48 CrossFilesystems true
49 PhishingSignatures true
50 PhishingScanURLs true
51 PhishingAlwaysBlockSSLMismatch false
52 PhishingAlwaysBlockCloak false
53 PartitionIntersection false
54 DetectPUA false
55 ScanPartialMessages false
56 HeuristicScanPrecedence false
57 StructuredDataDetection false
58 CommandReadTimeout 30
59 SendBufTimeout 200
60 MaxQueue 100
61 ExtendedDetectionInfo true
62 OLE2BlockMacros false
63 AllowAllMatchScan true
64 ForceToDisk false
65 DisableCertCheck false
66 DisableCache false
67 MaxScanTime 120000
68 MaxScanSize 100M
69 MaxFileSize 25M
70 MaxRecursion 16
71 MaxFiles 10000
72 MaxPartitions 50
73 MaxIconsPE 100
74 PCREMatchLimit 10000
75 PCRERecMatchLimit 5000
76 PCREMaxFileSize 25M
77 ScanXMLDOCS true
78 ScanHWP3 true
79 MaxRecHWP3 16
80 StreamMaxLength 25M
81 LogFile /var/log/clamav/clamav.log
82 LogTime true
83 LogFileUnlock false
84 LogFileMaxSize 0
85 Bytecode true
86 BytecodeSecurity TrustSigned
87 BytecodeTimeout 60000
88 OnAccessMaxFileSize 5M
89 ### ENABLE NETWORKING
90 TCPAddr 127.0.0.1
91 TCPSocket 3310
/etc/systemd/system/clamav-daemon.socket
Reload systemd, enable socket restart clamav-daemon
Check if daemon is listening ss -tulpen|grep 3310
Copy configuration
Enable Antivirus/ClamAV in Rspamd
Set action = "reject";
- You may change the reason message, e.g. to not disclose which anti-malware solution you are using.
Set type = "clamav";
Define connection to clamdscan
servers = "127.0.0.1:3310";Maybe create a IP-base whitelist =
/etc/rspamd/local.d/antivirus.conf
1 # Please don't modify this file as your changes might be overwritten with
2 # the next update.
3 #
4 # You can modify 'local.d/antivirus.conf' to add and merge
5 # parameters defined inside this section
6 #
7 # You can modify 'override.d/antivirus.conf' to strictly override all
8 # parameters defined inside this section
9 #
10 # See https://rspamd.com/doc/faq.html#what-are-the-locald-and-overrided-directories
11 # for details
12 #
13 # Module documentation can be found at https://rspamd.com/doc/modules/antivirus.html
14
15 antivirus {
16 # multiple scanners could be checked, for each we create a configuration block with an arbitrary name
17 clamav {
18 # If set force this action if any virus is found (default unset: no action is forced)
19 action = "reject";
20 # message = '${SCANNER}: virus found: "${VIRUS}"';
21 # Scan mime_parts separately - otherwise the complete mail will be transferred to AV Scanner
22 #scan_mime_parts = true;
23 # Scanning Text is suitable for some av scanner databases (e.g. Sanesecurity)
24 #scan_text_mime = false;
25 #scan_image_mime = false;
26 # If `max_size` is set, messages > n bytes in size are not scanned
27 #max_size = 20000000;
28 # symbol to add (add it to metric if you want non-zero weight)
29 #symbol = "CLAM_VIRUS";
30 # type of scanner: "clamav", "fprot", "sophos" or "savapi"
31 type = "clamav";
32 # For "savapi" you must also specify the following variable
33 #product_id = 12345;
34 # You can enable logging for clean messages
35 #log_clean = true;
36 # servers to query (if port is unspecified, scanner-specific default is used)
37 # can be specified multiple times to pool servers
38 # can be set to a path to a unix socket
39 # Enable this in local.d/antivirus.conf
40 servers = "127.0.0.1:3310";
41 # if `patterns` is specified virus name will be matched against provided regexes and the related
42 # symbol will be yielded if a match is found. If no match is found, default symbol is yielded.
43 #patterns {
44 # symbol_name = "pattern";
45 # JUST_EICAR = '^Eicar-Test-Signature$';
46 #}
47 #patterns_fail {
48 # symbol_name = "pattern";
49 #CLAM_PROTOCOL_ERROR = '^unhandled response';
50 #}
51 # `whitelist` points to a map of IP addresses. Mail from these addresses is not scanned.
52 whitelist = "/etc/rspamd/antivirus.wl";
53 }
54 }
Always fix #Unix Permissions.
Restart rspamd
1 systemctl restart rspamd.service
Test antivirus
The EICAR Anti-Virus Test File or EICAR test file is a computer file that was developed by the European Institute for Computer Antivirus Research (EICAR) and Computer Antivirus Research Organization (CARO), to test the response of computer antivirus (AV) programs. Instead of using real malware, which could cause real damage, this test file allows people to test anti-virus software without having to use a real computer virus.
EICAR test pattern
1 X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
Basic test script
1 ADDRESS="user@domain.tld"
2 EICAR='X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*'
3 TEST_FILE="$(mktemp)"
4 echo -n "$EICAR" > "$TEST_FILE"
5 #wget -O "$TEST_FILE" 'https://secure.eicar.org/eicar_com.zip'
6 echo 'Testing the AV-scanner' \
7 |mail -A "$TEST_FILE" \
8 -s "Test AV $(date '+%F %T')" \
9 "$ADDRESS"
10 [ -f "$TEST_FILE" ] && rm "$TEST_FILE"
Olefy
olefy - oletools verify over TCP socket
Small Python Daemon to use oletools over TCP sockets. Mainly to use oletools in Rspamd.
Many thanks to https://www.heinlein-support.de for your interesting Blog
Philippe Lagadec - Advanced VBA Macros Attack & Defence - Black Hat Europe 2019
Install olefy
Prepare a user, group …
Clone olefy repo
Install requirements
Change a path in the systemd-service to honor path to git repo.
I like the idea to keep the files in git to have the ability to git diff and link everything symbolically.
Changes
git diff master
1 diff --git a/olefy.service b/olefy.service
2 index 9527b73..3d4edbd 100644
3 --- a/olefy.service
4 +++ b/olefy.service
5 @@ -8,7 +8,7 @@ User=olefy
6 Group=olefy
7
8 EnvironmentFile=/etc/olefy.conf
9 -ExecStart=/usr/bin/python3 /usr/local/bin/olefy.py
10 +ExecStart=/usr/bin/python3 "/opt/olefy/olefy.py"
11 ExecReload=/bin/kill -HUP $MAINPID
12
13 PIDFile=/run/olefy.pid
Test olefy
Configure rspamd to use olefy
Create local configuration (derived from /etc/rspamd/local.d/external_services.conf)
/etc/rspamd/local.d/external_services.conf
1 oletools {
2 # If set force this action if any virus is found (default unset: no action is forced)
3 action = "reject";
4 # If set, then rejection message is set to this value (mention single quotes)
5 # If `max_size` is set, messages > n bytes in size are not scanned
6 # max_size = 20000000;
7 # log_clean = true;
8 servers = "127.0.0.1:10050";
9 # cache_expire = 86400;
10 scan_mime_parts = true;
11 scan_image_mime = false;
12 # extended = false;
13 # if `patterns` is specified virus name will be matched against provided regexes and the related
14 # symbol will be yielded if a match is found. If no match is found, default symbol is yielded.
15 patterns {
16 # symbol_name = "pattern";
17 }
18 # mime-part regex matching in content-type or filename
19 mime_parts_filter_regex {
20 #GEN1 = "application\/octet-stream";
21 DOC2 = "application\/msword";
22 DOC3 = "application\/vnd\.ms-word.*";
23 XLS = "application\/vnd\.ms-excel.*";
24 PPT = "application\/vnd\.ms-powerpoint.*";
25 GEN2 = "application\/vnd\.openxmlformats-officedocument.*";
26 }
27 # Mime-Part filename extension matching (no regex)
28 mime_parts_filter_ext {
29 doc = "doc";
30 dot = "dot";
31 docx = "docx";
32 dotx = "dotx";
33 docm = "docm";
34 dotm = "dotm";
35 xls = "xls";
36 xlt = "xlt";
37 xla = "xla";
38 xlsx = "xlsx";
39 xltx = "xltx";
40 xlsm = "xlsm";
41 xltm = "xltm";
42 xlam = "xlam";
43 xlsb = "xlsb";
44 ppt = "ppt";
45 pot = "pot";
46 pps = "pps";
47 ppa = "ppa";
48 pptx = "pptx";
49 potx = "potx";
50 ppsx = "ppsx";
51 ppam = "ppam";
52 pptm = "pptm";
53 potm = "potm";
54 ppsm = "ppsm";
55 }
56 # `whitelist` points to a map of IP addresses. Mail from these addresses is not scanned.
57 whitelist = "/etc/rspamd/antivirus.wl";
58 }
59 dcc {
60 # If set force this action if any virus is found (default unset: no action is forced)
61 # action = "reject";
62 # If set, then rejection message is set to this value (mention single quotes)
63 # If `max_size` is set, messages > n bytes in size are not scanned
64 max_size = 20000000;
65 #servers = "127.0.0.1:10045";
66 # if `patterns` is specified virus name will be matched against provided regexes and the related
67 # symbol will be yielded if a match is found. If no match is found, default symbol is yielded.
68 patterns {
69 # symbol_name = "pattern";
70 }
71 # `whitelist` points to a map of IP addresses. Mail from these addresses is not scanned.
72 whitelist = "/etc/rspamd/antivirus.wl";
73 }
Restart rspamd
1 systemctl restart rspamd.service
Scores and actions
rspamd docs - Configuring scores and actions
When sending emails containing multiple charsets (like cyrillic А а, Б б, В в, Г г, Д д, Е е, Ё ё, Ж ж, … and German Ä ä Ö ö Ü ü ß ) rspamd scores additional 5.0.
This may be disabled by creating
/etc/rspamd/local.d/headers_group.conf
Restart rspamd
1 systemctl restart rspamd.service
Greylisting
rspamd docs - Greylisting module
For weekly occuring tasks, the expiry of "1d" is to short.
Raise it to 10d in
/etc/rspamd/local.d/greylist.conf
Restart rspamd
1 systemctl restart rspamd.service
Once received
rspamd docs - Once received module
If a email has only a single "Received" header, this email is suspected to be a hacked device (like personal computer) that directly spams to the internet and is honored with a higher score.
Unfortunatly having no or only a single header applies to some "internal" scripts using the mail-gateway directly. IP-based whitelist may be used to allow these internal networks send email.
/etc/rspamd/local.d/once_received.wl
/etc/rspamd/local.d/once_received.conf
1 whitelist = "$LOCAL_CONFDIR/local.d/once_received.wl"
Restart rspamd
1 systemctl restart rspamd.service
Integration with MTA
For integration with Postfix, please return to postfix#rspamd