From 010a262e16853c4ca5fc58c00244dbc1ab1a1184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Tue, 3 Mar 2020 15:56:57 +0100 Subject: [PATCH 01/50] FROM mailserver2/debian-mail-overlay:latest --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8bd7be52..bd6c249c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM hardware/debian-mail-overlay:latest +FROM mailserver2/debian-mail-overlay:latest LABEL description "Simple and full-featured mail server using Docker" \ maintainer="Hardware " From 30db4cbe4e9f1614aa60fe900c6a8feef9012f92 Mon Sep 17 00:00:00 2001 From: HerrH Date: Tue, 1 Oct 2019 13:21:21 +0200 Subject: [PATCH 02/50] fixes spelling errors and typos --- README.md | 232 +++++++++++++++++++++++++++--------------------------- 1 file changed, 116 insertions(+), 116 deletions(-) diff --git a/README.md b/README.md index 6129abc7..d36f739d 100644 --- a/README.md +++ b/README.md @@ -20,20 +20,20 @@ **hardware/mailserver** is a simple and full-featured mail server build as a set of multiple docker images, including: -- **Postfix** : a full-set smtp email server -- **Dovecot** : secure IMAP and POP3 email server -- **Rspamd** : anti-spam filter with SPF, DKIM, DMARC, ARC, ratelimit and greylisting capabilities -- **Clamav** : antivirus with automatic updates and third-party signature databases -- **Zeyple** : automatic GPG encryption of all your emails -- **Sieve** : email filtering (vacation auto-responder, auto-forward, etc...) -- **Fetchmail** : fetch emails from external IMAP/POP3 server into local mailbox -- **Rainloop** : web based email client -- **Postfixadmin** : web-based administration interface +- **Postfix**: a full-set SMTP email server +- **Dovecot**: secure IMAP and POP3 email server +- **Rspamd**: anti-spam filter with SPF, DKIM, DMARC, ARC, rate limiting and greylisting capabilities +- **Clamav**: antivirus with automatic updates and third-party signature databases +- **Zeyple**: automatic GPG encryption of all your emails +- **Sieve**: email filtering (vacation auto-responder, auto-forward, etc...) +- **Fetchmail**: fetch emails from external IMAP/POP3 server into local mailbox +- **Rainloop**: web based email client +- **Postfixadmin**: web-based administration interface - **Unbound**: recursive caching DNS resolver with DNSSEC support -- **NSD** : authoritative DNS server with DNSSEC support -- **Træfik** : modern HTTP reverse proxy -- **SSL** : _let's encrypt_ with auto-renewal (SAN and wildcard certificates), custom and self-signed certificates support -- **OpenLDAP** : [BETA] ldap support available (only in `1.1-latest` for now) +- **NSD**: authoritative DNS server with DNSSEC support +- **Træfik**: modern HTTP reverse proxy +- **SSL**: _Let's Encrypt_ with auto-renewal (SAN and wildcard certificates), custom and self-signed certificates support +- **OpenLDAP*: [BETA] LDAP support available (only in `1.1-latest` for now) - Supporting multiple virtual domains over MySQL/PostgreSQL backend - Integration tests with Travis CI - Automated builds on DockerHub @@ -67,7 +67,7 @@ - [Enable automatic GPG encryption](#enable-automatic-gpg-encryption) - [Import your public key](#import-your-public-key) - [Import all recipients public keys](#import-all-recipients-public-keys) - - [Specify another gpg keyserver](#specify-another-gpg-keyserver) + - [Specify another GPG keyserver](#specify-another-gpg-keyserver) - [Run other GPG options](#run-other-gpg-options) - [Relaying from other networks](#relaying-from-other-networks) - [SSL certificates](#ssl-certificates) @@ -84,7 +84,7 @@ - [IPv6 support](#ipv6-support) - [Persistent files and folders in /mnt/docker/mail Docker volume](#persistent-files-and-folders-in-mntdockermail-docker-volume) - [Override postfix configuration](#override-postfix-configuration) - - [Custom configuration for dovecot](#custom-configuration-for-dovecot) + - [Custom configuration for Dovecot](#custom-configuration-for-dovecot) - [Postfix blacklist](#postfix-blacklist) - [Email client settings](#email-client-settings) - [Components](#components) @@ -95,7 +95,7 @@ ### System Requirements -Please check, if your system meets the following minimum requirements : +Please check, if your system meets the following minimum requirements: #### With MariaDB/PostgreSQL and Redis on the same host @@ -117,13 +117,13 @@ Please check, if your system meets the following minimum requirements : #### Cleaning -Please remove any web server and mail services running on your server. I recommend using a clean installation of your prefered distro. If you are using Debian, remember to remove the default MTA **Exim4** : +Please remove any web server and mail services running on your server. I recommend using a clean installation of your preferred distribution. If you are using Debian, remember to remove the default MTA **Exim4**: ``` # apt-get purge exim4* ``` -Also make sure that no other application is interfering with mail server configuration : +Also make sure that no other application is interfering with mail server configuration: ``` # netstat -tulpn | grep -E -w '25|80|110|143|443|465|587|993|995|4190' @@ -133,7 +133,7 @@ If this command returns any results please remove or stop the application runnin #### Ports -If you have a firewall, unblock the following ports, according to your needs : +If you have a firewall, unblock the following ports, according to your needs: | Service | Software | Protocol | Port | | ------- | -------- | -------- | ---- | @@ -169,9 +169,9 @@ A correct DNS setup is required, this step is very important. **Notes:** -* Make sure that the **PTR record** of your IP matches the FQDN (default : mail.domain.tld) of your mailserver host. This record is usually set in your web hosting interface. +* Make sure that the **PTR record** of your IP matches the FQDN (default: mail.domain.tld) of your mailserver host. This record is usually set in your web hosting interface. * DKIM, SPF and DMARC records are recommended to build a good reputation score. -* The DKIM public key will be available on host after the container startup : +* The DKIM public key will be available on host after the container startup: ``` /mnt/docker/mail/dkim/domain.tld/public.key @@ -179,7 +179,7 @@ A correct DNS setup is required, this step is very important. To regenerate your public and private keys, remove the `/mnt/docker/mail/dkim/domain.tld` folder. By default a **1024-bit** key is generated, you can increase this size by setting the `OPENDKIM_KEY_LENGTH` environment variable with a higher value. Check your domain registrar support to verify that it supports a TXT record long enough for a key larger than 1024 bits. -These DNS record will raise your trust reputation score and reduce abuse of your domain name. You can find more information here : +These DNS record will raise your trust reputation score and reduce abuse of your domain name. You can find more information here: * http://www.openspf.org/ * http://www.opendkim.org/ @@ -188,7 +188,7 @@ These DNS record will raise your trust reputation score and reduce abuse of your #### Testing -You can audit your mailserver with the following assessment services : +You can audit your mailserver with the following assessment services: * https://www.mail-tester.com/ * https://www.hardenize.com/ @@ -201,7 +201,7 @@ You can audit your mailserver with the following assessment services : #### 1 - Prepare your environment -:bulb: The reverse proxy used in this setup is [Traefik](https://traefik.io/), but you can use the solution of your choice (Nginx, Apache, Haproxy, Caddy, H2O...etc). +:bulb: The reverse proxy used in this setup is [Traefik](https://traefik.io/), but you can use the solution of your choice (Nginx, Apache, Haproxy, Caddy, H2O, etc pp). :warning: This docker image may not work with some hardened Linux distribution using security-enhancing kernel patches like GrSecurity, please use a [supported platform](https://docs.docker.com/install/#supported-platforms). @@ -219,7 +219,7 @@ mkdir -p /mnt/docker/traefik/acme && cd /mnt/docker \ && chmod 600 docker-compose.yml .env traefik/traefik.toml traefik/acme/acme.json ``` -Edit the `.env` and `traefik.toml`, adapt to your needs, then start all services : +Edit the `.env` and `traefik.toml`, adapt to your needs, then start all services: ``` docker-compose up -d @@ -229,19 +229,19 @@ docker-compose up -d PostfixAdmin is a web based interface used to manage mailboxes, virtual domains and aliases. -* Docker image : https://github.com/hardware/postfixadmin -* How to setup : [Postfixadmin initial configuration](https://github.com/hardware/mailserver/wiki/Postfixadmin-initial-configuration) +* Docker image: https://github.com/hardware/postfixadmin +* How to setup: [Postfixadmin initial configuration](https://github.com/hardware/mailserver/wiki/Postfixadmin-initial-configuration) #### 3 - Rainloop installation (optional) -Rainloop is a simple, modern and fast webmail with Sieve scripts support (filters and vacation message), GPG and a modern user interface. +Rainloop is a simple, modern and fast web mail front end with Sieve scripts support (filters and vacation message), GPG and a modern user interface. -* Docker image : https://github.com/hardware/rainloop -* How to setup : [Rainloop initial configuration](https://github.com/hardware/mailserver/wiki/Rainloop-initial-configuration) +* Docker image: https://github.com/hardware/rainloop +* How to setup: [Rainloop initial configuration](https://github.com/hardware/mailserver/wiki/Rainloop-initial-configuration) #### 4 - Done, congratulation ! :tada: -At first launch, the container takes few minutes to generate SSL certificates (if needed), DKIM keypair and update clamav database, all of this takes some time (1/2 minutes). This image comes with a snake-oil self-signed certificate, please use your own trusted certificates. [See below](https://github.com/hardware/mailserver#ssl-certificates) for configuration. +At first launch, the container takes few minutes to generate SSL certificates (if needed), DKIM keypair generation and update Clamav database, all of this takes some time (1/2 minutes). This image comes with a snake-oil self-signed certificate, please use your own trusted certificates. [See below](https://github.com/hardware/mailserver#ssl-certificates) for configuration. **List of webservices available:** @@ -254,7 +254,7 @@ At first launch, the container takes few minutes to generate SSL certificates (i Traefik dashboard use a basic authentication (user:admin, password:12345), the password can be encoded in MD5, SHA1 and BCrypt. You can use [htpasswd ](https://httpd.apache.org/docs/2.4/programs/htpasswd.html) to generate those ones. Users can be specified directly in the `traefik.toml` file. Rspamd dashboard use the password defined in your `docker-compose.yml`. -You can check the startup logs with this command : +You can check the startup logs with this command: ``` # docker logs -f mailserver @@ -295,7 +295,7 @@ This catalog provides a basic template to easily deploy an email server based on ![logo](https://i.imgur.com/tvTG8pN.png) -If you use Ansible, I recommend you to go to see [@ksylvan](https://github.com/ksylvan) playbooks here : https://github.com/ksylvan/docker-mail-server +If you use Ansible, I recommend you to go to see [@ksylvan](https://github.com/ksylvan) playbooks here: https://github.com/ksylvan/docker-mail-server

Back to table of contents :arrow_up_small:

@@ -328,7 +328,7 @@ If you use Ansible, I recommend you to go to see [@ksylvan](https://github.com/k | **DISABLE_SIEVE** | Disable ManageSieve protocol | *optional* | false | **DISABLE_SIGNING** | Disable DKIM/ARC signing | *optional* | false | **DISABLE_GREYLISTING** | Disable greylisting policy | *optional* | false -| **DISABLE_RATELIMITING** | Disable ratelimiting policy | *optional* | true +| **DISABLE_RATELIMITING** | Disable rate limiting policy | *optional* | true | **DISABLE_DNS_RESOLVER** | Disable the local DNS resolver | *optional* | false | **DISABLE_SSL_WATCH** | Disable watching of `acme.json` and the Let's Encrypt directory | *optional* | false | **ENABLE_POP3** | Enable POP3 protocol | *optional* | false @@ -339,13 +339,13 @@ If you use Ansible, I recommend you to go to see [@ksylvan](https://github.com/k \*1) **DBPASS** is NOT required when using LDAP authentication -* Use **DEBUG_MODE** to enable the debug mode. Switch to `true` to enable verbose logging for `postfix`, `dovecot`, `rspamd` and `Unbound`. To debug components separately, use this syntax : `DEBUG_MODE=postfix,rspamd`. -* **VMAIL_SUBDIR** is the mail location subdirectory name `/var/mail/vhosts/%domain/%user/$subdir`. For more information, read this : https://wiki.dovecot.org/VirtualUsers/Home -* **PASSWORD_SCHEME** for compatible schemes, read this : https://wiki.dovecot.org/Authentication/PasswordSchemes +* Use **DEBUG_MODE** to enable the debug mode. Switch to `true` to enable verbose logging for `postfix`, `dovecot`, `rspamd` and `Unbound`. To debug components separately, use this syntax: `DEBUG_MODE=postfix,rspamd`. +* **VMAIL_SUBDIR** is the mail location subdirectory name `/var/mail/vhosts/%domain/%user/$subdir`. For more information, read this: https://wiki.dovecot.org/VirtualUsers/Home +* **PASSWORD_SCHEME** for compatible schemes, read this: https://wiki.dovecot.org/Authentication/PasswordSchemes * Currently, only a single **RECIPIENT_DELIMITER** is supported. Support for multiple delimiters will arrive with Dovecot v2.3. * **FETCHMAIL_INTERVAL** must be a number between **1** and **59** minutes. * Use **DISABLE_DNS_RESOLVER** if you have some DNS troubles and DNSSEC lookup issues with the local DNS resolver. -* Use **DISABLE_RSPAMD_MODULE** to disable any module listed here : https://rspamd.com/doc/modules/ +* Use **DISABLE_RSPAMD_MODULE** to disable any module listed here: https://rspamd.com/doc/modules/ When using LDAP authentication the following additional variables become available. All *DBUSER*, *DBNAME* and *DBPASS* variables will not be used in this case: @@ -358,37 +358,37 @@ When using LDAP authentication the following additional variables become availab | **LDAP_BIND** | Bind to LDAP Server | *optional* | true | **LDAP_BIND_DN** | The DN to bind to | **required** if **LDAP_BIND** | | **LDAP_BIND_PW** | LDAP password or location of a file containing it | **required** if **LDAP_BIND** | -| **LDAP_DEFAULT_SEARCH_BASE** | The base DN for all lookus | **required** | +| **LDAP_DEFAULT_SEARCH_BASE** | The base DN for all lookups | **required** | | **LDAP_DEFAULT_SEARCH_SCOPE** | The default scope for all lookups (sub, base or one) | *optional* | sub | **LDAP_DOMAIN_SEARCH_BASE** | The search base for domain lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} | **LDAP_DOMAIN_SEARCH_SCOPE** | The search scope for domain lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} | **LDAP_DOMAIN_FILTER** | The search filter for domain lookups | **required** | -| **LDAP_DOMAIN_ATTRIBUTE** | The attibutes for domain lookup | **required** | +| **LDAP_DOMAIN_ATTRIBUTE** | The attributes for domain lookup | **required** | | **LDAP_DOMAIN_FORMAT** | The format for domain lookups | *optional* | | **LDAP_MAILBOX_SEARCH_BASE** | The search base for mailbox lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} | **LDAP_MAILBOX_SEARCH_SCOPE** | The search scope for mailbox lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} | **LDAP_MAILBOX_FILTER** | The search filter for mailbox lookups | **required** | -| **LDAP_MAILBOX_ATTRIBUTE** | The attibutes for mailbox lookup | **required** | +| **LDAP_MAILBOX_ATTRIBUTE** | The attributes for mailbox lookup | **required** | | **LDAP_MAILBOX_FORMAT** | The format for domain mailbox | *optional* | | **LDAP_ALIAS_SEARCH_BASE** | The search base for domain lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} | **LDAP_ALIAS_SEARCH_SCOPE** | The search scope for domain lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} | **LDAP_ALIAS_FILTER** | The search filter for domain lookups | **required** | -| **LDAP_ALIAS_ATTRIBUTE** | The attibutes for domain lookup | **required** | +| **LDAP_ALIAS_ATTRIBUTE** | The attributes for domain lookup | **required** | | **LDAP_ALIAS_FORMAT** | The format for domain lookups | *optional* | | **LDAP_FORWARD_SEARCH_BASE** | The search base for forward lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} | **LDAP_FORWARD_SEARCH_SCOPE** | The search scope for forward lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} | **LDAP_FORWARD_FILTER** | The search filter for forward lookups | *optional* | -| **LDAP_FORWARD_ATTRIBUTE** | The attibutes for forward lookup | *optional* | +| **LDAP_FORWARD_ATTRIBUTE** | The attributes for forward lookup | *optional* | | **LDAP_FORWARD_FORMAT** | The format for forward lookups | *optional* | | **LDAP_GROUP_SEARCH_BASE** | The search base for group lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} | **LDAP_GROUP_SEARCH_SCOPE** | The search scope for group lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} | **LDAP_GROUP_FILTER** | The search filter for group lookups | *optional* | -| **LDAP_GROUP_ATTRIBUTE** | The attibutes for group lookup | *optional* | +| **LDAP_GROUP_ATTRIBUTE** | The attributes for group lookup | *optional* | | **LDAP_GROUP_FORMAT** | The format for group lookups | *optional* | | **LDAP_SENDER_SEARCH_BASE** | The search base for sender lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} | **LDAP_SENDER_SEARCH_SCOPE** | The search scope for sender lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} | **LDAP_SENDER_FILTER** | The search filter for sender lookups | **required** | -| **LDAP_SENDER_ATTRIBUTE** | The attibutes for sender lookup | **required** | +| **LDAP_SENDER_ATTRIBUTE** | The attributes for sender lookup | **required** | | **LDAP_SENDER_FORMAT** | The format for sender lookups | **required** | | **LDAP_DOVECOT_USER_ATTRS** | Dovecot user attribute mapping | **required** | | **LDAP_DOVECOT_USER_FILTER** | Dovecot user search filter | **required** | @@ -397,11 +397,11 @@ When using LDAP authentication the following additional variables become availab | **LDAP_DOVECOT_ITERATE_ATTRS** | Dovecot user iterate attributes | *optional* | | **LDAP_DOVECOT_ITERATE_FILTER** | Dovecot user iterate filters | *optional* | | **LDAP_MASTER_USER_ENABLED** | Enable LDAP master users | *optional* | false -| **LDAP_MASTER_USER_SEPARATOR** | LDAP master user seperator | **required** if **LDAP_MASTER_USER_ENABLED** | \* +| **LDAP_MASTER_USER_SEPARATOR** | LDAP master user separator | **required** if **LDAP_MASTER_USER_ENABLED** | \* | **LDAP_MASTER_USER_SEARCH_BASE** | LDAP master user search base | **required** if **LDAP_MASTER_USER_ENABLED** | ${LDAP_DEFAULT_SEARCH_BASE} | **LDAP_MASTER_USER_SEARCH_SCOPE** | LDAP master user scope | **required** if **LDAP_MASTER_USER_ENABLED** | ${LDAP_DEFAULT_SEARCH_SCOPE} -| **LDAP_DOVECOT_MASTER_USER_ATTRS** | LDAP master user dovecot attributes | **required** if **LDAP_MASTER_USER_ENABLED** | -| **LDAP_DOVECOT_MASTER_USER_FILTER** | LDAP master user dovecot search filter | **required** if **LDAP_MASTER_USER_ENABLED** | +| **LDAP_DOVECOT_MASTER_USER_ATTRS** | LDAP master user Dovecot attributes | **required** if **LDAP_MASTER_USER_ENABLED** | +| **LDAP_DOVECOT_MASTER_USER_FILTER** | LDAP master user Dovecot search filter | **required** if **LDAP_MASTER_USER_ENABLED** |

Back to table of contents :arrow_up_small:

@@ -422,7 +422,7 @@ Please don't change the default value of `RECIPIENT_DELIMITER` (default = "+"). #### Import your public key -:warning: Make sure to send your public key on a gpg keyserver before to run the following command. +:warning: Make sure to send your public key on a GPG keyserver before to run the following command. ``` docker exec -ti mailserver encryption.sh import-key YOUR_KEY_ID @@ -436,7 +436,7 @@ This command browses all `/var/mail/vhosts/*` domains directories and users subd docker exec -ti mailserver encryption.sh import-all-keys ``` -#### Specify another gpg keyserver +#### Specify another GPG keyserver ``` docker exec -ti mailserver encryption.sh import-key YOUR_KEY_ID hkp://pgp.mit.edu @@ -445,7 +445,7 @@ docker exec -ti mailserver encryption.sh import-all-keys hkp://keys.gnupg.net #### Run other GPG options -You can use all options of gpg command line except an already assigned parameter called `--homedir`. +You can use all options of GPG command line except an already assigned parameter called `--homedir`. ```bash @@ -455,13 +455,13 @@ docker exec -ti mailserver encryption.sh --refresh-keys docker exec -ti mailserver encryption.sh ... ``` -Documentation : https://www.gnupg.org/documentation/manuals/gnupg/Operational-GPG-Commands.html +Documentation: https://www.gnupg.org/documentation/manuals/gnupg/Operational-GPG-Commands.html

Back to table of contents :arrow_up_small:

### Relaying from other networks -The **RELAY_NETWORKS** is a space separated list of additional IP addresses and subnets (in CIDR notation) which the mailserver relays without authentication. Hostnames are possible, but generally disadvised. IPv6 addresses must be surrounded by square brackets. You can also specify an absolut path to a file with IPs and networks so you can keep it on a mounted volume. Note that the file is not monitored for changes. +The **RELAY_NETWORKS** is a space separated list of additional IP addresses and subnets (in CIDR notation) which the mailserver relays without authentication. Hostnames are possible, but generally dis-advised. IPv6 addresses must be surrounded by square brackets. You can also specify an absolute path to a file with IPs and networks so you can keep it on a mounted volume. Note that the file is not monitored for changes. You can use this variable to allow other local containers to relay via the mailserver. Typically you would set this to the IP range of the default docker bridge (172.17.0.0/16) or the default network of your compose. If you are unable to determine, you might just add all RFC 1918 addresses `192.168.0.0/16 172.16.0.0/12 10.0.0.0/8` @@ -473,7 +473,7 @@ You can use this variable to allow other local containers to relay via the mails #### Let's Encrypt certificates generated by Traefik -To use Let's Encrypt certificates generated by Traefik, mount a new docker volume like this : +To use Let's Encrypt certificates generated by Traefik, mount a new docker volume like this: ```yml mailserver: @@ -496,7 +496,7 @@ docker logs -f mailserver [INFO] Using /etc/letsencrypt/live/mail.domain.tld folder ``` -Don't forget to add a new traefik frontend rule somewhere in your docker-compose.yml to generate a certificate for your mailserver FQDN (default : mail.domain.tld) subdomain. +Don't forget to add a new Traefik frontend rule somewhere in your docker-compose.yml to generate a certificate for your mailserver FQDN (default: mail.domain.tld) subdomain. ```yml # docker-compose.yml @@ -505,7 +505,7 @@ labels: - traefik.frontend.rule=Host:mail.${DOMAIN} ``` -Alternatively, you can specify your domains in the `traefik.toml` to generate a SAN certificate : +Alternatively, you can specify your domains in the `traefik.toml` to generate a SAN certificate: ```toml [acme] @@ -516,7 +516,7 @@ main = "domain.tld" sans = ["mail.domain.tld", "spam.domain.tld", "postfixadmin.domain.tld", "webmail.domain.tld"] ``` -Or a wildcard certificate : +Or a wildcard certificate: :warning: ACME wildcard certificates can only be generated thanks to a `DNS-01` challenge. @@ -533,7 +533,7 @@ delayBeforeCheck = 0 main = "*.domain.tld" ``` -If the startup script does not find the appropriate SSL certificate and private key, look at Traefik's logs to see what's going on. +If the startup script does not find the appropriate SSL certificate and private key, look at the Traefik logs to see what's going on. ``` docker logs -f mailserver @@ -560,7 +560,7 @@ docker-compose restart traefik && docker logs -f traefik #### Custom certificates -You can use Let's Encrypt or any other certification authority. Setup your `docker-compose.yml` like this : +You can use Let's Encrypt or any other certification authority. Setup your `docker-compose.yml` like this: ```yml mailserver: @@ -572,7 +572,7 @@ mailserver: Request your certificates in `/mnt/docker/ssl/live/mail.domain.tld` with an [ACME client](https://letsencrypt.org/docs/client-options/) if you use Let's Encrypt, otherwise get your SSL certificates with the method provided by your CA and put everything needed in this directory. -Required files in this folder : +Required files in this folder: :bulb: If you only have the fullchain.pem and privkey.pem, the startup script extract automatically the cert.pem and chain.pem from fullchain.pem. @@ -583,7 +583,7 @@ Required files in this folder : | chain.pem | Root and intermediate certificates only, excluding server certificate | | fullchain.pem | All certificates, including server certificate. This is concatenation of cert.pem and chain.pem | -Example with [acme.sh](https://acme.sh) : +Example with [acme.sh](https://acme.sh): ```bash acme.sh --install-cert -d example.com \ @@ -594,13 +594,13 @@ acme.sh --install-cert -d example.com \ --reloadcmd "docker restart mailserver" ``` -**Notes** : +**Notes**: -* Important : When renewing certificates, you must restart the mailserver container. +* Important: When renewing certificates, you must restart the mailserver container. -* If you do not use your own trusted certificates or those generated by Traefik, a default self-signed certificate (RSA 4096 bits SHA2) is added here : `/mnt/docker/mail/ssl/selfsigned/{cert.pem, privkey.pem}`. +* If you do not use your own trusted certificates or those generated by Traefik, a default self-signed certificate (RSA 4096 bits SHA2) is added here: `/mnt/docker/mail/ssl/selfsigned/{cert.pem, privkey.pem}`. -* If you have generated a ECDSA certificate with a curve other than `prime256v1` (NIST P-256), you need to change the Postfix TLS configuration because of a change in OpenSSL >= 1.1.0. For example, if you use `secp384r1` elliptic curve with your ECDSA certificate, change the `tls_eecdh_strong_curve` value : +* If you have generated a ECDSA certificate with a curve other than `prime256v1` (NIST P-256), you need to change the Postfix TLS configuration because of a change in OpenSSL >= 1.1.0. For example, if you use `secp384r1` elliptic curve with your ECDSA certificate, change the `tls_eecdh_strong_curve` value: ```ini # /mnt/docker/mail/postfix/custom.conf @@ -608,7 +608,7 @@ acme.sh --install-cert -d example.com \ tls_eecdh_strong_curve = secp384r1 ``` -Additional informations about this issue : +Additional informations about this issue: * https://github.com/openssl/openssl/issues/2033 * https://bugzilla.redhat.com/show_bug.cgi?id=1473971 @@ -632,7 +632,7 @@ openssl s_client -connect mail.domain.tld:993 -tlsextdebug MTA-STS is a new standard that makes it possible to send downgrade-resistant email over SMTP. In that sense, it is like an alternative to DANE but it does this by piggybacking on the browser Certificate Authority model, not DNSSEC. -To enable Strict Transport Security on your mailserver configure the following things : +To enable Strict Transport Security on your mailserver configure the following things: 1. Add a TLSRPT DNS TXT record at `_smtp._tls` on your domain, e.g. `_smtp._tls.domain.tld`, with something like `v=TLSRPTv1; rua=mailto:postmaster@domain.tld`. 2. Add a MTA-STS DNS TXT record at `_mta-sts` on your domain, e.g. `_mta-sts.domain.tld`, with something like `v=STSv1; id=2018072801`. @@ -651,11 +651,11 @@ Test your mail domain using a MTA-STS validator like [Hardenize](https://www.har

Back to table of contents :arrow_up_small:

-### Third-party clamav signature databases +### Third-party Clamav signature databases [Clamav-unofficial-sigs](https://github.com/extremeshok/clamav-unofficial-sigs) provides a simple way to download and update third-party signature databases provided by Sanesecurity, FOXHOLE, OITC, Scamnailer, BOFHLAND, CRDF, Porcupine, Securiteinfo, MalwarePatrol, Yara-Rules Project, etc. -Readme : https://github.com/extremeshok/clamav-unofficial-sigs +Readme: https://github.com/extremeshok/clamav-unofficial-sigs #### Required Ports @@ -667,19 +667,19 @@ Readme : https://github.com/extremeshok/clamav-unofficial-sigs #### Enable clamav-unofficial-sigs Create your `user.conf` file under `/mnt/docker/mail/clamav-unofficial-sigs` directory to configure clamav-unofficial-sigs updater. This file override the default configuration specified in [os.conf](https://github.com/hardware/mailserver/blob/master/rootfs/etc/clamav/unofficial-sigs/os.conf) and [master.conf](https://github.com/hardware/mailserver/blob/master/rootfs/etc/clamav/unofficial-sigs/master.conf). Don't forget, once you have completed the configuration of this file, set the value of `user_configuration_complete` to `yes` otherwise the script will not be able to execute. -As [Yara rules are broken with clamav ≥ 0.100](https://github.com/extremeshok/clamav-unofficial-sigs/issues/203), we disable Yara rules for now. +As [Yara rules are broken with Clamav = 0.100](https://github.com/extremeshok/clamav-unofficial-sigs/issues/203), we disable Yara rules for now. ```ini # /mnt/docker/mail/clamav-unofficial-sigs/user.conf # ========================= -# MalwarePatrol : https://www.malwarepatrol.net -# MalwarePatrol 2016 (free) clamav signatures +# MalwarePatrol: https://www.malwarepatrol.net +# MalwarePatrol 2016 (free) Clamav signatures # -# 1. Sign up for an account : https://www.malwarepatrol.net/signup-free.shtml +# 1. Sign up for an account: https://www.malwarepatrol.net/signup-free.shtml # 2. You will receive an email containing your password/receipt number # 3. Login to your account at malwarePatrol -# 4. In My Accountpage, choose the ClamAV list you will download. Free subscribers only get ClamAV Basic, commercial subscribers have access to ClamAV Extended. Do not use the agressive lists. +# 4. In My Accountpage, choose the ClamAV list you will download. Free subscribers only get ClamAV Basic, commercial subscribers have access to ClamAV Extended. Do not use the aggressive lists. # 5. In the download URL, you will see 3 parameters: receipt, product and list, enter them in the variables below. # malwarepatrol_receipt_code="YOUR-RECEIPT-NUMBER" # malwarepatrol_product_code="8" @@ -687,22 +687,22 @@ As [Yara rules are broken with clamav ≥ 0.100](https://github.com/extremeshok/ # malwarepatrol_free="yes" # ========================= -# SecuriteInfo : https://www.SecuriteInfo.com -# SecuriteInfo 2015 free clamav signatures +# SecuriteInfo: https://www.SecuriteInfo.com +# SecuriteInfo 2015 free Clamav signatures # -# Usage of SecuriteInfo 2015 free clamav signatures : https://www.securiteinfo.com -# - 1. Sign up for a free account : https://www.securiteinfo.com/clients/customers/signup -# - 2. You will receive an email to activate your account and then a followup email with your login name -# - 3. Login and navigate to your customer account : https://www.securiteinfo.com/clients/customers/account +# Usage of SecuriteInfo 2015 free Clamav signatures: https://www.securiteinfo.com +# - 1. Sign up for a free account: https://www.securiteinfo.com/clients/customers/signup +# - 2. You will receive an email to activate your account and then a follow-up email with your login name +# - 3. Login and navigate to your customer account: https://www.securiteinfo.com/clients/customers/account # - 4. Click on the Setup tab # - 5. You will need to get your unique identifier from one of the download links, they are individual for every user # - 5.1. The 128 character string is after the http://www.securiteinfo.com/get/signatures/ # - 5.2. Example https://www.securiteinfo.com/get/signatures/your_unique_and_very_long_random_string_of_characters/securiteinfo.hdb -# Your 128 character authorisation signature would be : your_unique_and_very_long_random_string_of_characters -# - 6. Enter the authorisation signature into the config securiteinfo_authorisation_signature: replacing YOUR-SIGNATURE-NUMBER with your authorisation signature from the link +# Your 128 character authorization signature would be: your_unique_and_very_long_random_string_of_characters +# - 6. Enter the authorization signature into the config securiteinfo_authorisation_signature: replacing YOUR-SIGNATURE-NUMBER with your authorization signature from the link # securiteinfo_authorisation_signature="YOUR-SIGNATURE-NUMBER" -# We disable Yara rules for now because they are broken with clamav releases > 0.100 +# We disable Yara rules for now because they are broken with Clamav releases > 0.100 yararulesproject_enabled="no" enable_yararules="no" @@ -710,7 +710,7 @@ enable_yararules="no" user_configuration_complete="yes" ``` -If the startup script detects this file, clamav-unofficial-sigs is automatically enabled and third-party databases downloaded under `/mnt/docker/mail/clamav` after clamav startup. Once the databases are downloaded, a SIGUSR2 signal is sent to clamav to reload the signature databases : +If the startup script detects this file, clamav-unofficial-sigs is automatically enabled and third-party databases downloaded under `/mnt/docker/mail/clamav` after Clamav startup. Once the databases are downloaded, a SIGUSR2 signal is sent to Clamav to reload the signature databases: ``` docker logs -f mailserver @@ -731,7 +731,7 @@ clamd[xxxxxx]: Database correctly reloaded (6812263 signatures) Unbound is a validating, recursive, and caching DNS resolver inside the container, you can control it with the remote server control utility. -Some examples : +Some examples: ```bash # Display server status @@ -744,13 +744,13 @@ docker exec -ti mailserver unbound-control stats_noreset docker exec -ti mailserver unbound-control reload ``` -Documentation : https://www.unbound.net/documentation/unbound-control.html +Documentation: https://www.unbound.net/documentation/unbound-control.html

Back to table of contents :arrow_up_small:

### PostgreSQL support -PostgreSQL can be used instead of MariaDB. You have to make some changes in the original `docker-compose.yml` file to use this DBMS : +PostgreSQL can be used instead of MariaDB. You have to make some changes in the original `docker-compose.yml` file to use this DBMS: ```yml mailserver: @@ -780,8 +780,8 @@ postgres: image: postgres:10.5-alpine container_name: postgres restart: ${RESTART_MODE} - stop_signal: SIGINT # Fast Shutdown mode - # Info : These variables are ignored when the volume already exists (if databases was created before). + stop_signal: SIGINT # Fast shutdown mode + # Info: These variables are ignored when the volume already exists (if databases was created before). environment: - POSTGRES_DB=postfix - POSTGRES_USER=postfix @@ -796,21 +796,21 @@ postgres: ### LDAP support -This mailserver supports LDAP now aswell. Please keep in mind that LDAP itself is an already complicated system and using this mailserver with LDAP will require you to already have a deeper understanding on how LDAP, postfix and dovecot works. Due to the nature of LDAP there is no "default" setup you can or is suggested to be used. This means **a lot** of configuration is **required** to set this mailserver up with your LDAP system and it will definetly not work out of the box. +This mailserver supports LDAP now as well. Please keep in mind that LDAP itself is an already complicated system and using this mailserver with LDAP will require you to already have a deeper understanding on how LDAP, postfix and Dovecot works. Due to the nature of LDAP there is no "default" setup you can or is suggested to be used. This means **a lot** of configuration is **required** to set this mailserver up with your LDAP system and it will definitely not work out of the box. -To enable LDAP you have to set **DBDRIVER** to *ldap*. *DBHOST* and *DBPORT* must point to the LDAP server used. *DBUSER*, *DBNAME*, *DBPASS* enviroment variables will not be used in this case. +To enable LDAP you have to set **DBDRIVER** to *ldap*. *DBHOST* and *DBPORT* must point to the LDAP server used. *DBUSER*, *DBNAME*, *DBPASS* environment variables will not be used in this case. If you want to use TLS set **LDAP_TLS_ENABLED** to *true* and specify a **LDAP_TLS_CA_FILE**. If you want to require the use of TLS set **LDAP_TLS_FORCE** to true. -If you want to bind to the LDAP server (default) set **LDAP_BIND** to *true* (default) and give your bind user dn (full path) as **LDAP_BIND_DN** and password as **LDAP_BIND_PW**. If a path to a existing file is given in **LDAP_BIND_PW** the content of the file will be used instead. +If you want to bind to the LDAP server (default) set **LDAP_BIND** to *true* (default) and give your bind user DN (full path) as **LDAP_BIND_DN** and password as **LDAP_BIND_PW**. If a path to a existing file is given in **LDAP_BIND_PW** the content of the file will be used instead. -All lookups will by default use **LDAP_DEFAULT_SEARCH_BASE** as base and **LDAP_DEFAULT_SEARCH_SCOPE** as scope. But for any query a specific base and scope can be provided aswell. Valid scopes are: *sub* for subtree meaning all nodes below the base. *one* for all direct child nodes of the base and *one* for only the base node itself. +All lookups will by default use **LDAP_DEFAULT_SEARCH_BASE** as base and **LDAP_DEFAULT_SEARCH_SCOPE** as scope. But for any query a specific base and scope can be provided as well. Valid scopes are: *sub* for subtree meaning all nodes below the base. *one* for all direct child nodes of the base and *one* for only the base node itself. -Unlike with postfixadmin where all tables are fixed, this mailserver is intended to work with existing ldap structures. This requires all lookups to be specified by you. +Unlike with Postfixadmin where all tables are fixed, this mailserver is intended to work with existing LDAP structures. This requires all lookups to be specified by you. -There are 4 required and 2 optional lookups for postfix that have to be provided by you. Each consists of 5 variables. The loopups are: Domain, Mailbox, Alias and Sender (all 4 required) and Forward and Group (optional). Each has the enviroment variables **LDAP_XXX_SEARCH_BASE**, **LDAP_XXX_SEARCH_SCOPE**, **LDAP_XXX_FILTER**, **LDAP_XXX_ATTRIBUTE** and **LDAP_XXXN_FORMAT** (*optional*) where **XXX*** must be replaced with **DOMAIN**, **MAILBOX**, **ALIAS**, **SENDER**, **FORWARD** or **GROUP**. E.g. **LDAP_DOMAIN_SEARCH_BASE** or **LDAP_MAILBOX_FILTER** +There are 4 required and 2 optional lookups for postfix that have to be provided by you. Each consists of 5 variables. The lookups are: Domain, Mailbox, Alias and Sender (all 4 required) and Forward and Group (optional). Each has the environment variables **LDAP_XXX_SEARCH_BASE**, **LDAP_XXX_SEARCH_SCOPE**, **LDAP_XXX_FILTER**, **LDAP_XXX_ATTRIBUTE** and **LDAP_XXXN_FORMAT** (*optional*) where **XXX*** must be replaced with **DOMAIN**, **MAILBOX**, **ALIAS**, **SENDER**, **FORWARD** or **GROUP**. E.g. **LDAP_DOMAIN_SEARCH_BASE** or **LDAP_MAILBOX_FILTER** -The **LDAP_XXX_SEARCH_BASE** is the search base dn. It will default to **LDAP_DEFAULT_SEARCH_BASE** as **LDAP_XXX_SEARCH_SCOPE** will default to **LDAP_DEFAULT_SEARCH_SCOPE** (which defaults to *sub*). +The **LDAP_XXX_SEARCH_BASE** is the search base DN. It will default to **LDAP_DEFAULT_SEARCH_BASE** as **LDAP_XXX_SEARCH_SCOPE** will default to **LDAP_DEFAULT_SEARCH_SCOPE** (which defaults to *sub*). The **LDAP_XXX_FILTER** must be a valid LDAP query filter. For a documentation of LDAP query filters you can look at https://ldap.com/ldap-filters/. For a list of valid replacement tokens please look at http://www.postfix.org/ldap_table.5.html in the section *query_filter*. Some examples: @@ -822,14 +822,14 @@ LDAP_MAILBOX_FILTER="(&(mail=%s)(objectClass=mailAccount))" LDAP_SENDER_FILTER="(&(|(mail=%s)(mailalias=%s))(objectClass=mailAccount))" ``` -The **LDAP_XXX_ATTRIBUTE** specifies which attribute of the found LDAP objects will be used. Usually these are either *mail*, *uid*, *mailalias* or *mailacceptinggeneralid* but may be completly different ones depending on your LDAP setup. +The **LDAP_XXX_ATTRIBUTE** specifies which attribute of the found LDAP objects will be used. Usually these are either *mail*, *uid*, *mailalias* or *mailacceptinggeneralid* but may be completely different ones depending on your LDAP setup. **LDAP_XXX_FORMAT** can be used to reformat the result. E.g. you can use `LDAP_MAILBOX_FORMAT="/var/mail/vhosts/%d/%s/mail/"` to set a fixed path for the mailbox location if the path is not stored within LDAP. The optional **FORWARD** and **GROUP** lookups are technically identical to the **ALIAS** lookup and could be used interchangeably but are intended for additional alias/group/forward lookups. So you can use aliases using an alias field in your user objects. Forwards as source and destination mapping fields in forwarding objects and group address and group member emails in group objects. But you can also use them in different ways to suit your system. -Then you also have to provide the lookups for dovecot. These will probably be similar to your postfix lookups but may and will differ in some cases. The variables neccessary are **LDAP_DOVECOT_USER_ATTRS**, **LDAP_DOVECOT_USER_FILTER**, **LDAP_DOVECOT_PASS_ATTRS**, **LDAP_DOVECOT_PASS_FILTER**, **LDAP_DOVECOT_ITERATE_ATTRS**, **LDAP_DOVECOT_ITERATE_FILTER**. They correspond directly to the dovecot variables of the same name. While the user and pass attributes and filters are required, the iterate attributes and filters are not. For more detailed information please look at https://wiki.dovecot.org/AuthDatabase/LDAP/Userdb. Note that multiple attribures may be required per query and must be provided in a different form than for postfix! Here are some examples: +Then you also have to provide the lookups for Dovecot. These will probably be similar to your postfix lookups but may and will differ in some cases. The variables necessary are **LDAP_DOVECOT_USER_ATTRS**, **LDAP_DOVECOT_USER_FILTER**, **LDAP_DOVECOT_PASS_ATTRS**, **LDAP_DOVECOT_PASS_FILTER**, **LDAP_DOVECOT_ITERATE_ATTRS**, **LDAP_DOVECOT_ITERATE_FILTER**. They correspond directly to the Dovecot variables of the same name. While the user and pass attributes and filters are required, the iterate attributes and filters are not. For more detailed information please look at https://wiki.dovecot.org/AuthDatabase/LDAP/Userdb. Note that multiple attributes may be required per query and must be provided in a different form than for postfix! Here are some examples: ``` LDAP_DOVECOT_USER_ATTRS="=home=/var/mail/vhosts/%d/%n/,=mail=maildir:/var/mail/vhosts/%d/%n/mail/,mailuserquota=quota=quota_rule=*:bytes=%\$$" @@ -840,7 +840,7 @@ LDAP_DOVECOT_ITERATE_ATTRS="mail=user" LDAP_DOVECOT_ITERATE_FILTER="(objectClass=mailAccount)" ``` -This mailserver also supports the user of master users that are allowed to log into other users mailboxes using their own password. This can be used e.g. for shared mailboxes or external imap services that should be able to connect to all inboxes via imap while not knowing the users passwords. To enable the use of master users set **LDAP_MASTER_USER_ENABLED** to *true*. With **LDAP_MASTER_USER_SEPARATOR** the separator can be specified (default is \*). So you can log in with the username `normaluser@yoursystem.com*masteruser@yoursystem.com` or `normaluser*masteruser` if you only use usernames as logins. The password then has to be the password of the master user. **LDAP_MASTER_USER_SEARCH_BASE**, **LDAP_MASTER_USER_SEARCH_SCOPE**, **LDAP_DOVECOT_MASTER_USER_ATTRS** and **LDAP_DOVECOT_MASTER_USER_FILTER** work analogous to the dovecot user lookups. For more detailed documentation please look at https://wiki.dovecot.org/Authentication/MasterUsers . Note that `%u` is the master user name in this case and `%{login_user}` can be used to get the user name of the user to be loged in. +This mailserver also supports the user of master users that are allowed to log into other users mailboxes using their own password. This can be used e.g. for shared mailboxes or external IMAP services that should be able to connect to all inboxes via IMAP while not knowing the users passwords. To enable the use of master users set **LDAP_MASTER_USER_ENABLED** to *true*. With **LDAP_MASTER_USER_SEPARATOR** the separator can be specified (default is \*). So you can log in with the username `normaluser@yoursystem.com*masteruser@yoursystem.com` or `normaluser*masteruser` if you only use usernames as logins. The password then has to be the password of the master user. **LDAP_MASTER_USER_SEARCH_BASE**, **LDAP_MASTER_USER_SEARCH_SCOPE**, **LDAP_DOVECOT_MASTER_USER_ATTRS** and **LDAP_DOVECOT_MASTER_USER_FILTER** work analogous to the Dovecot user lookups. For more detailed documentation please look at https://wiki.dovecot.org/Authentication/MasterUsers . Note that `%u` is the master user name in this case and `%{login_user}` can be used to get the user name of the user to be logged in. ``` LDAP_MASTER_USER_ENABLED=true @@ -893,7 +893,7 @@ ipv6nat: network_mode: "host" ``` -- Create a record named `mail` of type `AAAA` with your **public** IPv6 address in your DNS provider. +- Create a record named `mail` of type `AAAA` with your **public** IPv6 address with your DNS provider. Done! This is all the configuration needed to enable inbound IPv6 support on this mailserver. @@ -935,7 +935,7 @@ You can read more on how and why [robbertkl/docker-ipv6nat](https://github.com/r │ │ ... ├──sieve │ default.sieve - │ custom.sieve (custom default sieve rules for all users) + │ custom.sieve (custom default Sieve rules for all users) ├──dkim │ ├──domain.tld │ │ private.key @@ -959,14 +959,14 @@ You can read more on how and why [robbertkl/docker-ipv6nat](https://github.com/r │ │ │ │ ├──new │ │ │ │ ... │ │ │ ├──sieve - │ │ │ │ rainloop.user.sieve (if using rainloop webmail) + │ │ │ │ rainloop.user.sieve (if using Rainloop webmail) ```

Back to table of contents :arrow_up_small:

### Override postfix configuration -Postfix default configuration can be overrided providing a custom configuration file at postfix format. This can be +Postfix default configuration can be overridden by providing a custom configuration file in postfix format. This can be used to also add configuration that are not in default configuration. [Postfix documentation](http://www.postfix.org/documentation.html) remains the best place to find configuration options. @@ -979,7 +979,7 @@ To edit services in `master.cf` configuration file, SFP prefixes are available t * `F|` = service field (service/type/field=value) * `P|` = service parameter (service/type/parameter=value) -Example : +Example: ```ini # /mnt/docker/mail/postfix/custom.conf @@ -1015,7 +1015,7 @@ docker logs -f mailserver

Back to table of contents :arrow_up_small:

-### Custom configuration for dovecot +### Custom configuration for Dovecot Sometimes you might want to add additional configuration parameters or override the default ones. You can do so by placing configuration files to the persistent folder `/mnt/docker/mail/dovecot/conf.d`. @@ -1048,7 +1048,7 @@ To block some senders or an entire domain, create a new file named `sender_acces ```bash # /mnt/docker/mail/postfix/sender_access -# Format : +# Format: domain.tld REJECT spam@domain2.tld REJECT @@ -1064,13 +1064,13 @@ NOQUEUE: reject: 554 5.7.1 : Sender address rejected: Acces ### Email client settings -- IMAP/SMTP username : user@domain.tld -- Incoming IMAP server : mail.domain.tld (your FQDN) -- Outgoing SMTP server : mail.domain.tld (your FQDN) -- IMAP port : 993 -- SMTP port : 587 -- IMAP Encryption protocol : SSL/TLS -- SMTP Encryption protocol : STARTTLS +- IMAP/SMTP username: user@domain.tld +- Incoming IMAP server: mail.domain.tld (your FQDN) +- Outgoing SMTP server: mail.domain.tld (your FQDN) +- IMAP port: 993 +- SMTP port: 587 +- IMAP Encryption protocol: SSL/TLS +- SMTP Encryption protocol: STARTTLS

Back to table of contents :arrow_up_small:

@@ -1092,7 +1092,7 @@ NOQUEUE: reject: 554 5.7.1 : Sender address rejected: Acces ### Migration from 1.0 to 1.1 -If you still use 1.0 version (bundled with Spamassassin, Amavisd...etc) which was available with the `latest` tag, you can follow the migration steps here : +If you still use 1.0 version (bundled with Spamassassin, Amavisd...etc) which was available with the `latest` tag, you can follow the migration steps here: https://github.com/hardware/mailserver/wiki/Migrating-from-1.0-stable-to-1.1-stable @@ -1102,8 +1102,8 @@ Or stay with `1.0-legacy` tag (not recommended). ### Community projects -- [ksylvan/docker-mail-server](https://github.com/ksylvan/docker-mail-server) : Ansible playbooks to easily deploy hardware/mailserver. -- [rubentrancoso/mailserver-quicksetup](https://github.com/rubentrancoso/mailserver-quicksetup) : Automatic hardware/mailserver deployment on a digitalocean droplet. +- [ksylvan/docker-mail-server](https://github.com/ksylvan/docker-mail-server): Ansible Playbooks to easily deploy hardware/mailserver. +- [rubentrancoso/mailserver-quicksetup](https://github.com/rubentrancoso/mailserver-quicksetup): Automatic hardware/mailserver deployment on a Digitalocean droplet. - [NickBusey/HomelabOS](https://gitlab.com/NickBusey/HomelabOS) - Automatic deployment on home servers with bastion host relay

Back to table of contents :arrow_up_small:

From f1c4c1ac8402ff981b18f50f54fc28ee8b7247fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Fri, 6 Mar 2020 05:39:15 +0000 Subject: [PATCH 03/50] disable TLS1.1 (#1) disable TLS1.1 by default --- rootfs/etc/postfix/main.cf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rootfs/etc/postfix/main.cf b/rootfs/etc/postfix/main.cf index 49b0a4a6..ca23d7a1 100644 --- a/rootfs/etc/postfix/main.cf +++ b/rootfs/etc/postfix/main.cf @@ -59,8 +59,8 @@ smtp_tls_loglevel = 1 smtp_tls_security_level = dane smtp_dns_support_level = dnssec smtp_tls_CApath = /etc/ssl/certs -smtp_tls_protocols = !TLSv1, !SSLv2, !SSLv3 -smtp_tls_mandatory_protocols = !TLSv1, !SSLv2, !SSLv3 +smtp_tls_protocols = !TLSv1, !TLSv1.1, !SSLv2, !SSLv3 +smtp_tls_mandatory_protocols = !TLSv1, !TLSv1.1, !SSLv2, !SSLv3 smtp_tls_mandatory_ciphers = medium smtp_tls_note_starttls_offer = yes @@ -69,8 +69,8 @@ smtpd_tls_loglevel = 1 smtpd_tls_auth_only = yes smtpd_tls_security_level = may smtpd_tls_received_header = yes -smtpd_tls_protocols = !TLSv1, !SSLv2, !SSLv3 -smtpd_tls_mandatory_protocols = !TLSv1, !SSLv2, !SSLv3 +smtpd_tls_protocols = !TLSv1, !TLSv1.1, !SSLv2, !SSLv3 +smtpd_tls_mandatory_protocols = !TLSv1, !TLSv1.1, !SSLv2, !SSLv3 smtpd_tls_mandatory_ciphers = medium smtpd_tls_exclude_ciphers = aNULL,eNULL,EXPORT,DES,3DES,RC2,RC4,MD5,PSK,SRP,DSS,AECDH,ADH,SEED smtpd_tls_CApath = /etc/ssl/certs From ea318007ce6dfff052e7d3c92cb5bc746e359d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Thu, 12 Mar 2020 16:55:44 +0100 Subject: [PATCH 04/50] update travis. update badges --- .travis.yml | 5 ++- README.md | 116 +++++++++++++++++++++++++++------------------------- 2 files changed, 64 insertions(+), 57 deletions(-) diff --git a/.travis.yml b/.travis.yml index ae09640f..ae2b208b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ -language: bash -sudo: required +os: +- linux +language: shell services: - docker script: diff --git a/README.md b/README.md index d36f739d..bee23676 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,14 @@ :warning: -# This docker image is no longer maintained. +# This docker image is no longer maintained :warning: +## Fork + +This is a fork of [hardware/mailserver](https://github.com/hardware/mailserver). +Thank you very much. + ## hardware/mailserver ### Chat & questions @@ -12,11 +17,11 @@ ### Build -[![](https://travis-ci.org/hardware/mailserver.svg?branch=master)](https://travis-ci.org/hardware/mailserver) [![](https://images.microbadger.com/badges/version/hardware/mailserver:1.1-latest.svg)](https://microbadger.com/images/hardware/mailserver:1.1-latest) +[![Build Status](https://travis-ci.org/mailserver2/mailserver.svg?branch=master)](https://travis-ci.org/mailserver2/mailserver) ### Docker image -[![](https://images.microbadger.com/badges/image/hardware/mailserver:1.1-latest.svg)](https://microbadger.com/images/hardware/mailserver:1.1-latest) [![](https://img.shields.io/docker/automated/hardware/mailserver.svg)](https://hub.docker.com/r/hardware/mailserver/builds/) [![](https://img.shields.io/docker/pulls/hardware/mailserver.svg)](https://hub.docker.com/r/hardware/mailserver/) [![](https://img.shields.io/docker/stars/hardware/mailserver.svg)](https://hub.docker.com/r/hardware/mailserver/) [![](https://img.shields.io/badge/bitcoin-donate-green.svg)](https://keybase.io/hardware) +[![](https://images.microbadger.com/badges/image/mailserver2/mailserver:latest.svg)](https://microbadger.com/images/mailserver2/mailserver:latest) [![](https://img.shields.io/docker/automated/mailserver2/mailserver.svg)](https://hub.docker.com/r/mailserver2/mailserver/builds/) [![](https://img.shields.io/docker/pulls/mailserver2/mailserver.svg)](https://hub.docker.com/r/mailserver2/mailserver/) [![](https://img.shields.io/docker/stars/mailserver2/mailserver.svg)](https://hub.docker.com/r/mailserver2/mailserver/) [![](https://img.shields.io/badge/bitcoin-donate-green.svg)](https://keybase.io/hardware) **hardware/mailserver** is a simple and full-featured mail server build as a set of multiple docker images, including: @@ -40,58 +45,59 @@ ### Summary -- [hardware/mailserver](#hardwaremailserver) - - [Chat & questions](#chat--questions) - - [Build](#build) - - [Docker image](#docker-image) - - [Summary](#summary) - - [System Requirements](#system-requirements) - - [With MariaDB/PostgreSQL and Redis on the same host](#with-mariadbpostgresql-and-redis-on-the-same-host) - - [With MariaDB/PostgreSQL and Redis hosted on another server](#with-mariadbpostgresql-and-redis-hosted-on-another-server) - - [Prerequisites](#prerequisites) - - [Cleaning](#cleaning) - - [Ports](#ports) - - [DNS setup](#dns-setup) - - [DNS records and reverse PTR](#dns-records-and-reverse-ptr) - - [Testing](#testing) - - [Installation](#installation) - - [1 - Prepare your environment](#1---prepare-your-environment) - - [2 - Postfixadmin installation](#2---postfixadmin-installation) - - [3 - Rainloop installation (optional)](#3---rainloop-installation-optional) - - [4 - Done, congratulation ! :tada:](#4---done-congratulation--tada) - - [Rancher Catalog](#rancher-catalog) - - [Ansible Playbooks](#ansible-playbooks) - - [Environment variables](#environment-variables) - - [Automatic GPG encryption of all your emails](#automatic-gpg-encryption-of-all-your-emails) - - [How does it work ?](#how-does-it-work-) - - [Enable automatic GPG encryption](#enable-automatic-gpg-encryption) - - [Import your public key](#import-your-public-key) - - [Import all recipients public keys](#import-all-recipients-public-keys) - - [Specify another GPG keyserver](#specify-another-gpg-keyserver) - - [Run other GPG options](#run-other-gpg-options) - - [Relaying from other networks](#relaying-from-other-networks) - - [SSL certificates](#ssl-certificates) - - [Let's Encrypt certificates generated by Traefik](#lets-encrypt-certificates-generated-by-traefik) - - [Custom certificates](#custom-certificates) - - [Testing](#testing-1) - - [MTA-STS](#mta-sts) - - [Third-party clamav signature databases](#third-party-clamav-signature-databases) - - [Required Ports](#required-ports) - - [Enable clamav-unofficial-sigs](#enable-clamav-unofficial-sigs) - - [Unbound DNS resolver](#unbound-dns-resolver) - - [PostgreSQL support](#postgresql-support) - - [LDAP support](#ldap-support) - - [IPv6 support](#ipv6-support) - - [Persistent files and folders in /mnt/docker/mail Docker volume](#persistent-files-and-folders-in-mntdockermail-docker-volume) - - [Override postfix configuration](#override-postfix-configuration) - - [Custom configuration for Dovecot](#custom-configuration-for-dovecot) - - [Postfix blacklist](#postfix-blacklist) - - [Email client settings](#email-client-settings) - - [Components](#components) - - [Migration from 1.0 to 1.1](#migration-from-10-to-11) - - [Community projects](#community-projects) - - [Some useful Thunderbird extensions](#some-useful-thunderbird-extensions) - - [Donation](#donation) +- [This docker image is no longer maintained](#this-docker-image-is-no-longer-maintained) + - [Fork](#fork) + - [hardware/mailserver](#hardwaremailserver) + - [Chat & questions](#chat--questions) + - [Build](#build) + - [Docker image](#docker-image) + - [Summary](#summary) + - [System Requirements](#system-requirements) + - [With MariaDB/PostgreSQL and Redis on the same host](#with-mariadbpostgresql-and-redis-on-the-same-host) + - [With MariaDB/PostgreSQL and Redis hosted on another server](#with-mariadbpostgresql-and-redis-hosted-on-another-server) + - [Prerequisites](#prerequisites) + - [Cleaning](#cleaning) + - [Ports](#ports) + - [DNS setup](#dns-setup) + - [DNS records and reverse PTR](#dns-records-and-reverse-ptr) + - [Testing](#testing) + - [Installation](#installation) + - [1 - Prepare your environment](#1---prepare-your-environment) + - [2 - Postfixadmin installation](#2---postfixadmin-installation) + - [3 - Rainloop installation (optional)](#3---rainloop-installation-optional) + - [4 - Done, congratulation ! :tada:](#4---done-congratulation--tada) + - [Rancher Catalog](#rancher-catalog) + - [Ansible Playbooks](#ansible-playbooks) + - [Environment variables](#environment-variables) + - [Automatic GPG encryption of all your emails](#automatic-gpg-encryption-of-all-your-emails) + - [How does it work ?](#how-does-it-work) + - [Enable automatic GPG encryption](#enable-automatic-gpg-encryption) + - [Import your public key](#import-your-public-key) + - [Import all recipients public keys](#import-all-recipients-public-keys) + - [Specify another GPG keyserver](#specify-another-gpg-keyserver) + - [Run other GPG options](#run-other-gpg-options) + - [Relaying from other networks](#relaying-from-other-networks) + - [SSL certificates](#ssl-certificates) + - [Let's Encrypt certificates generated by Traefik](#lets-encrypt-certificates-generated-by-traefik) + - [Custom certificates](#custom-certificates) + - [Testing](#testing-1) + - [MTA-STS](#mta-sts) + - [Third-party Clamav signature databases](#third-party-clamav-signature-databases) + - [Required Ports](#required-ports) + - [Enable clamav-unofficial-sigs](#enable-clamav-unofficial-sigs) + - [Unbound DNS resolver](#unbound-dns-resolver) + - [PostgreSQL support](#postgresql-support) + - [LDAP support](#ldap-support) + - [IPv6 support](#ipv6-support) + - [Persistent files and folders in /mnt/docker/mail Docker volume](#persistent-files-and-folders-in-mntdockermail-docker-volume) + - [Override postfix configuration](#override-postfix-configuration) + - [Custom configuration for Dovecot](#custom-configuration-for-dovecot) + - [Postfix blacklist](#postfix-blacklist) + - [Email client settings](#email-client-settings) + - [Components](#components) + - [Migration from 1.0 to 1.1](#migration-from-10-to-11) + - [Community projects](#community-projects) + - [Some useful Thunderbird extensions](#some-useful-thunderbird-extensions) ### System Requirements From 6c12727fbad609d5af202975b40899d8ae7f08a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Sun, 22 Mar 2020 17:13:59 +0000 Subject: [PATCH 05/50] Fix tests - Part I (#4) * Remove test for mirrors.dat which does not exist in newest ClamAV (https://blog.clamav.net/2019/10/clamav-01020-has-been-released.html). Adjust filter for EICAR Anti-Virus Test File message. * Fix different EICAR message in log * Fix wrong variable name. * export DBPASS in setup.sh so that gucci can use it * update test docker images: postgres, redis * update test docker images: osixia/openldap:1.3.0 * clamav virus found milter-reject log message changed --- Makefile | 6 +++--- rootfs/usr/local/bin/setup.sh | 6 +++--- test/tests.bats | 9 ++------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 0ed8f12a..a99fbb72 100644 --- a/Makefile +++ b/Makefile @@ -36,12 +36,12 @@ init: -e POSTGRES_USER=postfix \ -e POSTGRES_PASSWORD=testpasswd \ -v "`pwd`/test/config/postgres":/docker-entrypoint-initdb.d \ - -t postgres:10.5-alpine + -t postgres:12-alpine docker run \ -d \ --name redis \ - -t redis:4.0-alpine + -t redis:5.0-alpine docker run \ -d \ @@ -51,7 +51,7 @@ init: -e LDAP_ADMIN_PASSWORD="testpasswd" \ -e LDAP_TLS=false \ -v "`pwd`/test/config/ldap/struct.ldif":/container/service/slapd/assets/config/bootstrap/ldif/custom/struct.ldif \ - -t osixia/openldap:1.2.2 --copy-service + -t osixia/openldap:1.3.0 --copy-service sleep 10 diff --git a/rootfs/usr/local/bin/setup.sh b/rootfs/usr/local/bin/setup.sh index de486bd5..9c0d7b88 100644 --- a/rootfs/usr/local/bin/setup.sh +++ b/rootfs/usr/local/bin/setup.sh @@ -12,7 +12,7 @@ DEBUG_MODE=${DEBUG_MODE:-false} ADD_DOMAINS=${ADD_DOMAINS:-} -DBPASS=$([ -f "$DBPASS" ] && cat "$DBPASS" || echo "${DBPASS:-}") +export DBPASS=$([ -f "$DBPASS" ] && cat "$DBPASS" || echo "${DBPASS:-}") RSPAMD_PASSWORD=$([ -f "$RSPAMD_PASSWORD" ] && cat "$RSPAMD_PASSWORD" || echo "${RSPAMD_PASSWORD:-}") WHITELIST_SPAM_ADDRESSES=${WHITELIST_SPAM_ADDRESSES:-} OPENDKIM_KEY_LENGTH=${OPENDKIM_KEY_LENGTH:-1024} @@ -190,8 +190,8 @@ if [ "$DBDRIVER" = "ldap" ]; then LDAP_MASTER_USER_SEPARATOR=${LDAP_MASTER_USER_SEPARATOR:-"*"} LDAP_MASTER_USER_SEARCH_BASE=${LDAP_MASTER_USER_SEARCH_BASE:-"${LDAP_DEFAULT_SEARCH_BASE}"} LDAP_MASTER_USER_SEARCH_SCOPE=${LDAP_MASTER_USER_SEARCH_SCOPE:-"${LDAP_DEFAULT_SEARCH_SCOPE}"} - LDAP_DOVECOT_MASTER_USER_ATTRS=${LDAP_DOVECOT_USER_ATTRS:-} - LDAP_DOVECOT_MASTER_USER_FILTER=${LDAP_DOVECOT_USER_FILTER:-} + LDAP_DOVECOT_MASTER_PASS_ATTRS=${LDAP_DOVECOT_MASTER_PASS_ATTRS:-} + LDAP_DOVECOT_MASTER_PASS_FILTER=${LDAP_DOVECOT_MASTER_PASS_FILTER:-} fi # ENVIRONMENT VARIABLES TEMPLATING diff --git a/test/tests.bats b/test/tests.bats index 63721e4b..3eddf282 100644 --- a/test/tests.bats +++ b/test/tests.bats @@ -1323,7 +1323,7 @@ load 'test_helper/bats-assert/load' } @test "checking postfix: milter-reject - clamav virus found" { - run docker exec mailserver_default grep -i 'milter-reject.*clamav: virus found' /var/log/mail.log + run docker exec mailserver_default grep -i 'milter-reject.*Spam message rejected; from=' /var/log/mail.log assert_success } @@ -1615,18 +1615,13 @@ load 'test_helper/bats-assert/load' assert_success } -@test "checking clamav: mirrors.dat exist" { - run docker exec mailserver_default [ -f /var/lib/clamav/mirrors.dat ] - assert_success -} - @test "checking clamav: default lib directory is a symlink" { run docker exec mailserver_default [ -L /var/lib/clamav ] assert_success } @test "checking clamav: Eicar-Test-Signature FOUND" { - run docker exec mailserver_default grep -i 'Eicar-Test-Signature(.*) FOUND' /var/log/mail.log + run docker exec mailserver_default grep -i '(44d88612fea8a8f36de82e1278abb02f:68) FOUND' /var/log/mail.log assert_success } From 0d13461195489b5d44b96631fe2e05e0c3b4b915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Thu, 9 Apr 2020 15:14:41 +0000 Subject: [PATCH 06/50] Make Zeyple work again (#5) Update Zeyple and enable it again + enable tests --- Dockerfile | 2 +- rootfs/etc/{zeyple => }/zeyple.conf | 0 rootfs/usr/local/bin/setup.sh | 91 ++++++++-------- rootfs/usr/local/bin/zeyple.py | 63 +++++++++--- test/tests.bats | 154 ++++++++++++++-------------- 5 files changed, 172 insertions(+), 138 deletions(-) rename rootfs/etc/{zeyple => }/zeyple.conf (100%) diff --git a/Dockerfile b/Dockerfile index bd6c249c..cc082c0b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ RUN apt-get update && apt-get install -y -q --no-install-recommends \ dovecot-core dovecot-imapd dovecot-lmtpd dovecot-pgsql dovecot-mysql dovecot-ldap dovecot-sieve dovecot-managesieved dovecot-pop3d \ fetchmail libdbi-perl libdbd-pg-perl libdbd-mysql-perl liblockfile-simple-perl \ clamav clamav-daemon \ - python3-pip python3-setuptools python3-wheel \ + python3-pip python3-setuptools python3-wheel python3-gpg \ rsyslog dnsutils curl unbound jq rsync \ inotify-tools \ && rm -rf /var/spool/postfix \ diff --git a/rootfs/etc/zeyple/zeyple.conf b/rootfs/etc/zeyple.conf similarity index 100% rename from rootfs/etc/zeyple/zeyple.conf rename to rootfs/etc/zeyple.conf diff --git a/rootfs/usr/local/bin/setup.sh b/rootfs/usr/local/bin/setup.sh index 9c0d7b88..eb99de76 100644 --- a/rootfs/usr/local/bin/setup.sh +++ b/rootfs/usr/local/bin/setup.sh @@ -378,12 +378,7 @@ if [ "$ENABLE_ENCRYPTION" = false ]; then echo "[INFO] Automatic GPG encryption is disabled" sed -i '/content_filter/ s/^/#/' /etc/postfix/main.cf else - # echo "[INFO] Automatic GPG encryption is enabled" - sed -i '/content_filter/ s/^/#/' /etc/postfix/main.cf - echo "[ERROR] Zeyple support has been temporarily disabled in the master branch following the Debian 10 update. Please, use the stable docker tag (1.1-stable) until the issue fixed. More information here : https://github.com/hardware/mailserver/issues/393" - if [ "$TESTING" = false ]; then - touch /etc/setup-error - fi + echo "[INFO] Automatic GPG encryption is enabled" fi # Enable ManageSieve protocol @@ -447,7 +442,7 @@ if [ "$TESTING" = true ]; then sed -i 's|\(sign_local.*=\).*|\1 false;|' /etc/rspamd/local.d/dkim_signing.conf sed -i 's|\(sign_local.*=\).*|\1 false;|' /etc/rspamd/local.d/arc.conf # Zeyple logs are needed for testing (default: logs are redirected to /dev/null) - sed -i 's|\(log_file.*=\).*|\1 /var/log/zeyple.log|' /etc/zeyple/zeyple.conf + sed -i 's|\(log_file.*=\).*|\1 /var/log/zeyple.log|' /etc/zeyple.conf # Disable fetchmail scheduled Task rm -f /etc/cron.d/fetchmail # Ignore temporary dns failure in rspamd @@ -509,47 +504,47 @@ postfix set-permissions &>/dev/null # ZEYPLE # --------------------------------------------------------------------------------------------- -# if [ "$ENABLE_ENCRYPTION" = true ]; then - -# # Add Zeyple user -# adduser --quiet \ -# --system \ -# --group \ -# --home /var/mail/zeyple \ -# --no-create-home \ -# --disabled-login \ -# --gecos "zeyple automatic GPG encryption tool" \ -# zeyple - -# # Create all files and directories needed by Zeyple -# mkdir -p /var/mail/zeyple/keys -# chmod 700 /var/mail/zeyple/keys -# chmod 744 /usr/local/bin/zeyple.py -# chown -R zeyple:zeyple /var/mail/zeyple /usr/local/bin/zeyple.py - -# if [ "$TESTING" = true ]; then - -# touch /var/log/zeyple.log -# chown zeyple:zeyple /var/log/zeyple.log - -# # Generating John Doe GPG key -# s6-setuidgid zeyple gpg --homedir "/var/mail/zeyple/keys" --batch --generate-key <' | wc -l" -# assert_success -# assert_output 1 -# } +@test "checking zeyple: retrieve john doe gpg key in public keyring" { + run docker exec mailserver_reverse /bin/sh -c "s6-setuidgid zeyple gpg --homedir /var/mail/zeyple/keys --with-colons --list-keys | grep 'John Doe (test key) ' | wc -l" + assert_success + assert_output 1 +} -# @test "checking zeyple: retrieve john doe gpg key in public keyring (using custom script)" { -# run docker exec mailserver_reverse /bin/sh -c "encryption.sh --with-colons --list-keys | grep 'John Doe (test key) ' | wc -l" -# assert_success -# assert_output 1 -# } +@test "checking zeyple: retrieve john doe gpg key in public keyring (using custom script)" { + run docker exec mailserver_reverse /bin/sh -c "encryption.sh --with-colons --list-keys | grep 'John Doe (test key) ' | wc -l" + assert_success + assert_output 1 +} -# @test "checking zeyple: 3 emails encrypted in john.doe folder" { -# run docker exec mailserver_reverse /bin/sh -c "grep -i 'multipart/encrypted' /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | wc -l" -# assert_success -# assert_output 3 -# run docker exec mailserver_reverse /bin/sh -c "grep -i 'BEGIN PGP MESSAGE' /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | wc -l" -# assert_success -# assert_output 3 -# run docker exec mailserver_reverse /bin/sh -c "grep -i 'END PGP MESSAGE' /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | wc -l" -# assert_success -# assert_output 3 -# } +@test "checking zeyple: 3 emails encrypted in john.doe folder" { + run docker exec mailserver_reverse /bin/sh -c "grep -i 'multipart/encrypted' /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | wc -l" + assert_success + assert_output 3 + run docker exec mailserver_reverse /bin/sh -c "grep -i 'BEGIN PGP MESSAGE' /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | wc -l" + assert_success + assert_output 3 + run docker exec mailserver_reverse /bin/sh -c "grep -i 'END PGP MESSAGE' /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | wc -l" + assert_success + assert_output 3 +} # # unbound From 8f9ee71ccc7341b67ef5e378dba0beb6d21c8b79 Mon Sep 17 00:00:00 2001 From: Peer Date: Mon, 4 Nov 2019 15:34:02 +0100 Subject: [PATCH 07/50] dkim update --- Makefile | 4 ++- rootfs/etc/rspamd/local.d/arc.conf | 4 +-- rootfs/etc/rspamd/local.d/dkim_signing.conf | 4 +-- rootfs/usr/local/bin/setup.sh | 37 +++++++++++++++------ test/tests.bats | 8 ++--- 5 files changed, 37 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index a99fbb72..237dc237 100644 --- a/Makefile +++ b/Makefile @@ -104,7 +104,8 @@ init: -e ENABLE_POP3=true \ -e ENABLE_ENCRYPTION=true \ -e ENABLE_FETCHMAIL=true \ - -e OPENDKIM_KEY_LENGTH=4096 \ + -e DKIM_KEY_LENGTH=4096 \ + -e DKIM_SELECTOR="other" \ -e TESTING=true \ -v "`pwd`/test/share/tests":/tmp/tests \ -v "`pwd`/test/share/passwd":/tmp/passwd \ @@ -144,6 +145,7 @@ init: -e LDAP_DOVECOT_PASS_FILTER="(&(mail=%u)(objectClass=mailAccount))" \ -e LDAP_DOVECOT_ITERATE_ATTRS="mail=user" \ -e LDAP_DOVECOT_ITERATE_FILTER="(objectClass=mailAccount)" \ + -e DKIM_SELECTOR="mail20190101" \ -e VMAILUID=`id -u` \ -e VMAILGID=`id -g` \ -e RSPAMD_PASSWORD=testpasswd \ diff --git a/rootfs/etc/rspamd/local.d/arc.conf b/rootfs/etc/rspamd/local.d/arc.conf index d877bda9..e9f7e4b8 100644 --- a/rootfs/etc/rspamd/local.d/arc.conf +++ b/rootfs/etc/rspamd/local.d/arc.conf @@ -3,8 +3,8 @@ allow_hdrfrom_mismatch = false; allow_hdrfrom_multiple = true; allow_username_mismatch = true; auth_only = true; -path = "/var/mail/dkim/$domain/private.key"; -selector = "mail"; +path = "/var/mail/dkim/$domain/$selector.private.key"; +selector = "{{ .DKIM_SELECTOR }}"; sign_local = true; symbol = "ARC_SIGNED"; try_fallback = true; diff --git a/rootfs/etc/rspamd/local.d/dkim_signing.conf b/rootfs/etc/rspamd/local.d/dkim_signing.conf index b8dd4c6f..1b7bf606 100644 --- a/rootfs/etc/rspamd/local.d/dkim_signing.conf +++ b/rootfs/etc/rspamd/local.d/dkim_signing.conf @@ -3,8 +3,8 @@ allow_hdrfrom_mismatch = false; allow_hdrfrom_multiple = true; allow_username_mismatch = true; auth_only = true; -path = "/var/mail/dkim/$domain/private.key"; -selector = "mail"; +path = "/var/mail/dkim/$domain/$selector.private.key"; +selector = "{{ .DKIM_SELECTOR }}"; sign_local = true; symbol = "DKIM_SIGNED"; try_fallback = true; diff --git a/rootfs/usr/local/bin/setup.sh b/rootfs/usr/local/bin/setup.sh index eb99de76..140fc0af 100644 --- a/rootfs/usr/local/bin/setup.sh +++ b/rootfs/usr/local/bin/setup.sh @@ -6,6 +6,7 @@ export RECIPIENT_DELIMITER export FETCHMAIL_INTERVAL export RELAY_NETWORKS export PASSWORD_SCHEME +export DKIM_SELECTOR TESTING=${TESTING:-false} DEBUG_MODE=${DEBUG_MODE:-false} @@ -16,6 +17,8 @@ export DBPASS=$([ -f "$DBPASS" ] && cat "$DBPASS" || echo "${DBPASS:-}") RSPAMD_PASSWORD=$([ -f "$RSPAMD_PASSWORD" ] && cat "$RSPAMD_PASSWORD" || echo "${RSPAMD_PASSWORD:-}") WHITELIST_SPAM_ADDRESSES=${WHITELIST_SPAM_ADDRESSES:-} OPENDKIM_KEY_LENGTH=${OPENDKIM_KEY_LENGTH:-1024} +DKIM_KEY_LENGTH=${DKIM_KEY_LENGTH:-$OPENDKIM_KEY_LENGTH} +DKIM_SELECTOR=${DKIM_SELECTOR:-mail} DISABLE_RSPAMD_MODULE=${DISABLE_RSPAMD_MODULE:-} DISABLE_SIEVE=${DISABLE_SIEVE:-false} @@ -58,19 +61,24 @@ for domain in "${domains[@]}"; do mkdir -p /var/mail/dkim/"$domain" if [ -f /var/mail/opendkim/"$domain"/mail.private ]; then - echo "[INFO] Found an old DKIM keys, migrating files to the new location" - mv /var/mail/opendkim/"$domain"/mail.private /var/mail/dkim/"$domain"/private.key - mv /var/mail/opendkim/"$domain"/mail.txt /var/mail/dkim/"$domain"/public.key + echo "[INFO] Found an old OPENDKIM keys, migrating files to the new location" + mv /var/mail/opendkim/"$domain"/mail.private /var/mail/dkim/"$domain"/mail.private.key + mv /var/mail/opendkim/"$domain"/mail.txt /var/mail/dkim/"$domain"/mail.public.key rm -rf /var/mail/opendkim/"$domain" rmdir --ignore-fail-on-non-empty /var/mail/opendkim - elif [ ! -f /var/mail/dkim/"$domain"/private.key ]; then + elif [ -f /var/mail/dkim/"$domain"/private.key ]; then + echo "[INFO] Found an old DKIM keys, migrating files to the new location" + mv /var/mail/dkim/"$domain"/private.key /var/mail/dkim/"$domain"/mail.private.key + mv /var/mail/dkim/"$domain"/public.key /var/mail/dkim/"$domain"/mail.public.key + fi + if [ ! -f /var/mail/dkim/"$domain"/"$DKIM_SELECTOR".private.key ]; then echo "[INFO] Creating DKIM keys for domain $domain" rspamadm dkim_keygen \ - --selector=mail \ + --selector="$DKIM_SELECTOR" \ --domain="$domain" \ - --bits="$OPENDKIM_KEY_LENGTH" \ - --privkey=/var/mail/dkim/"$domain"/private.key \ - > /var/mail/dkim/"$domain"/public.key + --bits="$DKIM_KEY_LENGTH" \ + --privkey=/var/mail/dkim/"$domain"/"$DKIM_SELECTOR".private.key \ + > /var/mail/dkim/"$domain"/"$DKIM_SELECTOR".public.key else echo "[INFO] Found DKIM key pair for domain $domain - skip creation" fi @@ -236,6 +244,8 @@ _envtpl /etc/dovecot/conf.d/90-quota.conf _envtpl /etc/rspamd/local.d/redis.conf _envtpl /etc/rspamd/local.d/settings.conf _envtpl /etc/rspamd/local.d/statistic.conf +_envtpl /etc/rspamd/local.d/dkim_signing.conf +_envtpl /etc/rspamd/local.d/arc.conf _envtpl /etc/cron.d/fetchmail _envtpl /etc/mailname @@ -644,9 +654,12 @@ sed -i "s||${PASSWORD}|g" /etc/rspamd/local.d/worker-controller.inc # Set permissions mkdir -p /var/mail/rspamd /var/log/rspamd /run/rspamd -chown -R _rspamd:_rspamd /var/mail/rspamd /var/log/rspamd /run/rspamd +chown -R _rspamd:_rspamd /var/mail/rspamd /var/log/rspamd /run/rspamd /var/mail/dkim chmod 750 /var/mail/rspamd /var/log/rspamd +chmod 444 /var/mail/dkim/*/*.public.key +chmod 440 /var/mail/dkim/*/*.private.key + modules+=(${DISABLE_RSPAMD_MODULE//,/ }) if [ ${#modules[@]} -gt 0 ]; then @@ -751,8 +764,10 @@ mkdir -p /var/run/fetchmail chmod +x /usr/local/bin/* # Fix old DKIM keys permissions -chown -R vmail:vmail /var/mail/dkim -chmod 444 /var/mail/dkim/*/{private.key,public.key} +# Moved to rspamd +# chown -R vmail:vmail /var/mail/dkim +# chmod 444 /var/mail/dkim/*/*.public.key +# chmod 440 /var/mail/dkim/*/*.private.key # Ensure that hashes are calculated because Postfix require directory # to be set up like this in order to find CA certificates. diff --git a/test/tests.bats b/test/tests.bats index 59130440..9e100524 100644 --- a/test/tests.bats +++ b/test/tests.bats @@ -1244,25 +1244,25 @@ load 'test_helper/bats-assert/load' # @test "checking dkim: all key pairs are generated (default configuration)" { - run docker exec mailserver_default /bin/bash -c "ls -A /var/mail/dkim/*/{private.key,public.key} | wc -l" + run docker exec mailserver_default /bin/bash -c "ls -A /var/mail/dkim/*/mail.{private.key,public.key} | wc -l" assert_success assert_output 6 } @test "checking dkim: all key pairs are generated (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "ls -A /var/mail/dkim/*/{private.key,public.key} | wc -l" + run docker exec mailserver_reverse /bin/bash -c "ls -A /var/mail/dkim/*/other.{private.key,public.key} | wc -l" assert_success assert_output 2 } @test "checking dkim: all key pairs are generated (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "ls -A /var/mail/dkim/*/{private.key,public.key} | wc -l" + run docker exec mailserver_ldap /bin/bash -c "ls -A /var/mail/dkim/*/mail20190101.{private.key,public.key} | wc -l" assert_success assert_output 6 } @test "checking dkim: control the size of the RSA key pair (4096bits)" { - run docker exec mailserver_reverse /bin/bash -c "openssl rsa -in /var/mail/dkim/domain.tld/private.key -text -noout | grep -i 'Private-Key: (4096 bit, 2 primes)'" + run docker exec mailserver_reverse /bin/bash -c "openssl rsa -in /var/mail/dkim/domain.tld/other.private.key -text -noout | grep -i 'Private-Key: (4096 bit, 2 primes)'" assert_success } From b5c3d802510491b2212bcc42d7dfd6cde688dbcf Mon Sep 17 00:00:00 2001 From: PeerD Date: Mon, 4 Nov 2019 15:44:56 +0100 Subject: [PATCH 08/50] Update README.md Updated DKIM sections --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index bee23676..dd628bd8 100644 --- a/README.md +++ b/README.md @@ -170,20 +170,21 @@ A correct DNS setup is required, this step is very important. | postfixadmin | IN | CNAME | any | mail.domain.tld. | | @ | IN | MX | 10 | mail.domain.tld. | | @ | IN | TXT | any | "v=spf1 a mx ip4:SERVER_IPV4 ~all" | -| mail._domainkey | IN | TXT | any | "v=DKIM1; k=rsa; p=YOUR DKIM Public Key" | +| {{selector}}._domainkey | IN | TXT | any | "v=DKIM1; k=rsa; p=YOUR DKIM Public Key" | | _dmarc | IN | TXT | any | "v=DMARC1; p=reject; rua=mailto:postmaster@domain.tld; ruf=mailto:admin@domain.tld; fo=0; adkim=s; aspf=s; pct=100; rf=afrf; sp=reject" | **Notes:** * Make sure that the **PTR record** of your IP matches the FQDN (default: mail.domain.tld) of your mailserver host. This record is usually set in your web hosting interface. +* {{selector}} defaults to ``mail`` unless changed via ``DKIM_SELECTOR`` * DKIM, SPF and DMARC records are recommended to build a good reputation score. * The DKIM public key will be available on host after the container startup: ``` -/mnt/docker/mail/dkim/domain.tld/public.key +/mnt/docker/mail/dkim/domain.tld/{{selector}}.public.key ``` -To regenerate your public and private keys, remove the `/mnt/docker/mail/dkim/domain.tld` folder. By default a **1024-bit** key is generated, you can increase this size by setting the `OPENDKIM_KEY_LENGTH` environment variable with a higher value. Check your domain registrar support to verify that it supports a TXT record long enough for a key larger than 1024 bits. +To regenerate your public and private keys, remove the `/mnt/docker/mail/dkim/domain.tld` folder. By default a **1024-bit** key is generated, you can increase this size by setting the `DKIM_KEY_LENGTH` environment variable with a higher value. Check your domain registrar support to verify that it supports a TXT record long enough for a key larger than 1024 bits. These DNS record will raise your trust reputation score and reduce abuse of your domain name. You can find more information here: @@ -312,7 +313,8 @@ If you use Ansible, I recommend you to go to see [@ksylvan](https://github.com/k | **VMAILUID** | vmail user id | *optional* | 1024 | **VMAILGID** | vmail group id | *optional* | 1024 | **VMAIL_SUBDIR** | Individual mailbox' subdirectory | *optional* | mail -| **OPENDKIM_KEY_LENGTH** | Size of your DKIM RSA key pair | *optional* | 1024 +| **DKIM_KEY_LENGTH** | Size of your DKIM RSA key pair | *optional* | 1024 +| **DKIM_SELECTOR** | Your DKIM selector | *optional* | `mail` | **DEBUG_MODE** | Enable Postfix, Dovecot, Rspamd and Unbound verbose logging | *optional* | false | **PASSWORD_SCHEME** | Passwords encryption scheme | *optional* | `SHA512-CRYPT` | **DBDRIVER** | Database type: mysql, pgsql, ldap | *optional* | mysql @@ -352,7 +354,7 @@ If you use Ansible, I recommend you to go to see [@ksylvan](https://github.com/k * **FETCHMAIL_INTERVAL** must be a number between **1** and **59** minutes. * Use **DISABLE_DNS_RESOLVER** if you have some DNS troubles and DNSSEC lookup issues with the local DNS resolver. * Use **DISABLE_RSPAMD_MODULE** to disable any module listed here: https://rspamd.com/doc/modules/ - +* **OPENDKIM_KEY_LENGTH** has been renamed to **DKIM_KEY_LENGTH**, but falls back to **OPENDKIM_KEY_LENGTH** for backwards compatability When using LDAP authentication the following additional variables become available. All *DBUSER*, *DBNAME* and *DBPASS* variables will not be used in this case: @@ -944,8 +946,8 @@ You can read more on how and why [robbertkl/docker-ipv6nat](https://github.com/r │ custom.sieve (custom default Sieve rules for all users) ├──dkim │ ├──domain.tld - │ │ private.key - │ │ public.key + │ │ {{selector}}.private.key + │ │ {{selector}}.public.key ├──ssl │ ├──selfsigned (Auto-generated if no certificate found) │ │ cert.pem From 9cda13d193b8316c58484d9f275f2fd6edec2e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Sat, 11 Apr 2020 16:26:22 +0200 Subject: [PATCH 09/50] delete comment + move comment --- rootfs/usr/local/bin/setup.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/rootfs/usr/local/bin/setup.sh b/rootfs/usr/local/bin/setup.sh index 140fc0af..beb6df85 100644 --- a/rootfs/usr/local/bin/setup.sh +++ b/rootfs/usr/local/bin/setup.sh @@ -657,6 +657,7 @@ mkdir -p /var/mail/rspamd /var/log/rspamd /run/rspamd chown -R _rspamd:_rspamd /var/mail/rspamd /var/log/rspamd /run/rspamd /var/mail/dkim chmod 750 /var/mail/rspamd /var/log/rspamd +# Fix old DKIM keys permissions chmod 444 /var/mail/dkim/*/*.public.key chmod 440 /var/mail/dkim/*/*.private.key @@ -763,12 +764,6 @@ sed -i 's|rsyslog-rotate|rsyslog-rotate \&>/dev/null|g' /etc/logrotate.d/rsyslog mkdir -p /var/run/fetchmail chmod +x /usr/local/bin/* -# Fix old DKIM keys permissions -# Moved to rspamd -# chown -R vmail:vmail /var/mail/dkim -# chmod 444 /var/mail/dkim/*/*.public.key -# chmod 440 /var/mail/dkim/*/*.private.key - # Ensure that hashes are calculated because Postfix require directory # to be set up like this in order to find CA certificates. c_rehash /etc/ssl/certs &>/dev/null From 818213740936b2210c56043157897f6dbb5b57d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Fri, 22 May 2020 14:17:03 +0200 Subject: [PATCH 10/50] fix missing equal sign in LABEL --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index cc082c0b..2529a934 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM mailserver2/debian-mail-overlay:latest -LABEL description "Simple and full-featured mail server using Docker" \ +LABEL description="Simple and full-featured mail server using Docker" \ maintainer="Hardware " ARG DEBIAN_FRONTEND=noninteractive From 08d6bea674ad2006a0736b9292563add5a2b1ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Fri, 5 Jun 2020 10:52:19 +0200 Subject: [PATCH 11/50] Update readme: Added maintenance message. Changed some hardware links to mailserver2 links. Added chat badges. --- README.md | 144 +++++++++++++++++++++++++++--------------------------- 1 file changed, 73 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index dd628bd8..54f95bc2 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,15 @@ -:warning: +## mailserver2/mailserver -# This docker image is no longer maintained +### Fork -:warning: - -## Fork - -This is a fork of [hardware/mailserver](https://github.com/hardware/mailserver). +This is a fork that provides the bare minimum of maintenance for the excellent [hardware/mailserver](https://github.com/hardware/mailserver). Thank you very much. -## hardware/mailserver - ### Chat & questions [![](https://badges.gitter.im/hardware-mailserver/Lobby.svg)](https://gitter.im/hardware-mailserver/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![](https://img.shields.io/badge/chat-discord-9cf)](https://discord.gg/nb8tRGs) +[![](https://img.shields.io/badge/chat-%23mailserver%3Amatrix.org-blue)](https://matrix.to/#/!DbFxWyybGbPHdBNVkM:matrix.org?via=matrix.org) ### Build @@ -23,7 +19,7 @@ Thank you very much. [![](https://images.microbadger.com/badges/image/mailserver2/mailserver:latest.svg)](https://microbadger.com/images/mailserver2/mailserver:latest) [![](https://img.shields.io/docker/automated/mailserver2/mailserver.svg)](https://hub.docker.com/r/mailserver2/mailserver/builds/) [![](https://img.shields.io/docker/pulls/mailserver2/mailserver.svg)](https://hub.docker.com/r/mailserver2/mailserver/) [![](https://img.shields.io/docker/stars/mailserver2/mailserver.svg)](https://hub.docker.com/r/mailserver2/mailserver/) [![](https://img.shields.io/badge/bitcoin-donate-green.svg)](https://keybase.io/hardware) -**hardware/mailserver** is a simple and full-featured mail server build as a set of multiple docker images, including: +**mailserver2/mailserver** is a simple and full-featured mail server build as a set of multiple docker images, including: - **Postfix**: a full-set SMTP email server - **Dovecot**: secure IMAP and POP3 email server @@ -45,59 +41,59 @@ Thank you very much. ### Summary -- [This docker image is no longer maintained](#this-docker-image-is-no-longer-maintained) +- [mailserver2/mailserver](#mailserver2mailserver) - [Fork](#fork) - - [hardware/mailserver](#hardwaremailserver) - - [Chat & questions](#chat--questions) - - [Build](#build) - - [Docker image](#docker-image) - - [Summary](#summary) - - [System Requirements](#system-requirements) - - [With MariaDB/PostgreSQL and Redis on the same host](#with-mariadbpostgresql-and-redis-on-the-same-host) - - [With MariaDB/PostgreSQL and Redis hosted on another server](#with-mariadbpostgresql-and-redis-hosted-on-another-server) - - [Prerequisites](#prerequisites) - - [Cleaning](#cleaning) - - [Ports](#ports) - - [DNS setup](#dns-setup) - - [DNS records and reverse PTR](#dns-records-and-reverse-ptr) - - [Testing](#testing) - - [Installation](#installation) - - [1 - Prepare your environment](#1---prepare-your-environment) - - [2 - Postfixadmin installation](#2---postfixadmin-installation) - - [3 - Rainloop installation (optional)](#3---rainloop-installation-optional) - - [4 - Done, congratulation ! :tada:](#4---done-congratulation--tada) - - [Rancher Catalog](#rancher-catalog) - - [Ansible Playbooks](#ansible-playbooks) - - [Environment variables](#environment-variables) - - [Automatic GPG encryption of all your emails](#automatic-gpg-encryption-of-all-your-emails) - - [How does it work ?](#how-does-it-work) - - [Enable automatic GPG encryption](#enable-automatic-gpg-encryption) - - [Import your public key](#import-your-public-key) - - [Import all recipients public keys](#import-all-recipients-public-keys) - - [Specify another GPG keyserver](#specify-another-gpg-keyserver) - - [Run other GPG options](#run-other-gpg-options) - - [Relaying from other networks](#relaying-from-other-networks) - - [SSL certificates](#ssl-certificates) - - [Let's Encrypt certificates generated by Traefik](#lets-encrypt-certificates-generated-by-traefik) - - [Custom certificates](#custom-certificates) - - [Testing](#testing-1) - - [MTA-STS](#mta-sts) - - [Third-party Clamav signature databases](#third-party-clamav-signature-databases) - - [Required Ports](#required-ports) - - [Enable clamav-unofficial-sigs](#enable-clamav-unofficial-sigs) - - [Unbound DNS resolver](#unbound-dns-resolver) - - [PostgreSQL support](#postgresql-support) - - [LDAP support](#ldap-support) - - [IPv6 support](#ipv6-support) - - [Persistent files and folders in /mnt/docker/mail Docker volume](#persistent-files-and-folders-in-mntdockermail-docker-volume) - - [Override postfix configuration](#override-postfix-configuration) - - [Custom configuration for Dovecot](#custom-configuration-for-dovecot) - - [Postfix blacklist](#postfix-blacklist) - - [Email client settings](#email-client-settings) - - [Components](#components) - - [Migration from 1.0 to 1.1](#migration-from-10-to-11) - - [Community projects](#community-projects) - - [Some useful Thunderbird extensions](#some-useful-thunderbird-extensions) + - [Chat & questions](#chat--questions) + - [Build](#build) + - [Docker image](#docker-image) + - [Summary](#summary) + - [System Requirements](#system-requirements) + - [With MariaDB/PostgreSQL and Redis on the same host](#with-mariadbpostgresql-and-redis-on-the-same-host) + - [With MariaDB/PostgreSQL and Redis hosted on another server](#with-mariadbpostgresql-and-redis-hosted-on-another-server) + - [Prerequisites](#prerequisites) + - [Cleaning](#cleaning) + - [Ports](#ports) + - [DNS setup](#dns-setup) + - [DNS records and reverse PTR](#dns-records-and-reverse-ptr) + - [Testing](#testing) + - [Installation](#installation) + - [1 - Prepare your environment](#1---prepare-your-environment) + - [2 - Postfixadmin installation](#2---postfixadmin-installation) + - [3 - Rainloop installation (optional)](#3---rainloop-installation-optional) + - [4 - Done, congratulation ! :tada:](#4---done-congratulation--tada) + - [Rancher Catalog](#rancher-catalog) + - [Ansible Playbooks](#ansible-playbooks) + - [Environment variables](#environment-variables) + - [Automatic GPG encryption of all your emails](#automatic-gpg-encryption-of-all-your-emails) + - [How does it work ?](#how-does-it-work-) + - [Enable automatic GPG encryption](#enable-automatic-gpg-encryption) + - [Import your public key](#import-your-public-key) + - [Import all recipients public keys](#import-all-recipients-public-keys) + - [Specify another GPG keyserver](#specify-another-gpg-keyserver) + - [Run other GPG options](#run-other-gpg-options) + - [Relaying from other networks](#relaying-from-other-networks) + - [SSL certificates](#ssl-certificates) + - [Let's Encrypt certificates generated by Traefik](#lets-encrypt-certificates-generated-by-traefik) + - [Custom certificates](#custom-certificates) + - [Testing](#testing-1) + - [MTA-STS](#mta-sts) + - [Third-party Clamav signature databases](#third-party-clamav-signature-databases) + - [Required Ports](#required-ports) + - [Enable clamav-unofficial-sigs](#enable-clamav-unofficial-sigs) + - [Unbound DNS resolver](#unbound-dns-resolver) + - [PostgreSQL support](#postgresql-support) + - [LDAP support](#ldap-support) + - [IPv6 support](#ipv6-support) + - [Persistent files and folders in /mnt/docker/mail Docker volume](#persistent-files-and-folders-in-mntdockermail-docker-volume) + - [Override postfix configuration](#override-postfix-configuration) + - [Custom configuration for Dovecot](#custom-configuration-for-dovecot) + - [Postfix blacklist](#postfix-blacklist) + - [Email client settings](#email-client-settings) + - [Components](#components) + - [Migration from 1.0 to 1.1](#migration-from-10-to-11) + - [Migration from hardware/mailserver to mailserver2/mailserver](#migration-from-hardwaremailserver-to-mailserver2mailserver) + - [Community projects](#community-projects) + - [Some useful Thunderbird extensions](#some-useful-thunderbird-extensions) ### System Requirements @@ -219,9 +215,9 @@ docker network create http_network # Create the required folders and files mkdir -p /mnt/docker/traefik/acme && cd /mnt/docker \ -&& curl https://raw.githubusercontent.com/hardware/mailserver/master/docker-compose.sample.yml -o docker-compose.yml \ -&& curl https://raw.githubusercontent.com/hardware/mailserver/master/sample.env -o .env \ -&& curl https://raw.githubusercontent.com/hardware/mailserver/master/traefik.sample.toml -o traefik/traefik.toml \ +&& curl https://raw.githubusercontent.com/mailserver2/mailserver/master/docker-compose.sample.yml -o docker-compose.yml \ +&& curl https://raw.githubusercontent.com/mailserver2/mailserver/master/sample.env -o .env \ +&& curl https://raw.githubusercontent.com/mailserver2/mailserver/master/traefik.sample.toml -o traefik/traefik.toml \ && touch traefik/acme/acme.json \ && chmod 600 docker-compose.yml .env traefik/traefik.toml traefik/acme/acme.json ``` @@ -237,18 +233,18 @@ docker-compose up -d PostfixAdmin is a web based interface used to manage mailboxes, virtual domains and aliases. * Docker image: https://github.com/hardware/postfixadmin -* How to setup: [Postfixadmin initial configuration](https://github.com/hardware/mailserver/wiki/Postfixadmin-initial-configuration) +* How to setup: [Postfixadmin initial configuration](https://github.com/mailserver2/mailserver/wiki/Postfixadmin-initial-configuration) #### 3 - Rainloop installation (optional) Rainloop is a simple, modern and fast web mail front end with Sieve scripts support (filters and vacation message), GPG and a modern user interface. * Docker image: https://github.com/hardware/rainloop -* How to setup: [Rainloop initial configuration](https://github.com/hardware/mailserver/wiki/Rainloop-initial-configuration) +* How to setup: [Rainloop initial configuration](https://github.com/mailserver2/mailserver/wiki/Rainloop-initial-configuration) #### 4 - Done, congratulation ! :tada: -At first launch, the container takes few minutes to generate SSL certificates (if needed), DKIM keypair generation and update Clamav database, all of this takes some time (1/2 minutes). This image comes with a snake-oil self-signed certificate, please use your own trusted certificates. [See below](https://github.com/hardware/mailserver#ssl-certificates) for configuration. +At first launch, the container takes few minutes to generate SSL certificates (if needed), DKIM keypair generation and update Clamav database, all of this takes some time (1/2 minutes). This image comes with a snake-oil self-signed certificate, please use your own trusted certificates. [See below](https://github.com/mailserver2/mailserver#ssl-certificates) for configuration. **List of webservices available:** @@ -485,7 +481,7 @@ To use Let's Encrypt certificates generated by Traefik, mount a new docker volum ```yml mailserver: - image: hardware/mailserver + image: mailserver2/mailserver volumes: - /mnt/docker/traefik/acme:/etc/letsencrypt/acme ... @@ -572,7 +568,7 @@ You can use Let's Encrypt or any other certification authority. Setup your `dock ```yml mailserver: - image: hardware/mailserver + image: mailserver2/mailserver volumes: - /mnt/docker/ssl:/etc/letsencrypt ... @@ -674,7 +670,7 @@ Readme: https://github.com/extremeshok/clamav-unofficial-sigs #### Enable clamav-unofficial-sigs -Create your `user.conf` file under `/mnt/docker/mail/clamav-unofficial-sigs` directory to configure clamav-unofficial-sigs updater. This file override the default configuration specified in [os.conf](https://github.com/hardware/mailserver/blob/master/rootfs/etc/clamav/unofficial-sigs/os.conf) and [master.conf](https://github.com/hardware/mailserver/blob/master/rootfs/etc/clamav/unofficial-sigs/master.conf). Don't forget, once you have completed the configuration of this file, set the value of `user_configuration_complete` to `yes` otherwise the script will not be able to execute. +Create your `user.conf` file under `/mnt/docker/mail/clamav-unofficial-sigs` directory to configure clamav-unofficial-sigs updater. This file override the default configuration specified in [os.conf](https://github.com/mailserver2/mailserver/blob/master/rootfs/etc/clamav/unofficial-sigs/os.conf) and [master.conf](https://github.com/mailserver2/mailserver/blob/master/rootfs/etc/clamav/unofficial-sigs/master.conf). Don't forget, once you have completed the configuration of this file, set the value of `user_configuration_complete` to `yes` otherwise the script will not be able to execute. As [Yara rules are broken with Clamav = 0.100](https://github.com/extremeshok/clamav-unofficial-sigs/issues/203), we disable Yara rules for now. ```ini @@ -1108,6 +1104,12 @@ Or stay with `1.0-legacy` tag (not recommended).

Back to table of contents :arrow_up_small:

+### Migration from hardware/mailserver to mailserver2/mailserver + +Follow the migration steps here: + + + ### Community projects - [ksylvan/docker-mail-server](https://github.com/ksylvan/docker-mail-server): Ansible Playbooks to easily deploy hardware/mailserver. From 529aa22460c4320a4702db043ca33f5eae14ecae Mon Sep 17 00:00:00 2001 From: Andrew Savinykh <658865+AndrewSav@users.noreply.github.com> Date: Sat, 13 Jun 2020 09:57:40 +1200 Subject: [PATCH 12/50] Added VERSIONING.md (#11) --- VERSIONING.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 VERSIONING.md diff --git a/VERSIONING.md b/VERSIONING.md new file mode 100644 index 00000000..9c025a4c --- /dev/null +++ b/VERSIONING.md @@ -0,0 +1,32 @@ +# About Versioning + +This guidelines apply to versioning of docker images in mailserver2 repos: + +- mailserver +- debian-mail-overlay +- postfixadmin +- rainloop + +Historically the old [hardware/debian-mail-overlay](https://hub.docker.com/r/hardware/debian-mail-overlay/tags) image seemed to be versioned after `rspamd` it includes. However, apart of `rspamd` the image contains a lot of other differently versioned software. In this regard, it's more logical to have an independent version, and note down the version of individual software component in the commit messages and/or release history. + +For `rainloop` and `postfixadmin` it makes sense to continue to version the images after the respective software versions, since it's not likely that they may require a version bump for any other reason. + +In certain case it's also possible for the `Dockerfile` itself to change without individual components updates. Finally, it could be that the `Dockerfile` does not change either, but the change is effected by update in an upstream software repository, for example `clamav` updates in `mailserver` are handled this way. + +Follow these steps when an update is ready to be pushed to Docker Hub: + +- Tag the current GitHub commit in the `mailserver2` repo with the next version, e.g. `git tag -a v1.0.1 -m "update postfixadmin to version 1.0.1, ... other changes here"` +- Build the new image +- Label it with the version you tagged the current commit with +- Push this image version with the label above as well as with the `latest` label + +## Build Example + +``` +docker build . -t debian-mail-overlay +docker tag debian-mail-overlay mailserver2/debian-mail-overlay:0.0.0 +docker push mailserver2/debian-mail-overlay:0.0.0 +docker tag debian-mail-overlay mailserver2/debian-mail-overlay:latest +docker push mailserver2/debian-mail-overlay:latest +``` + From e7e7b378592e4f958ae54679c17eb318fddcb4c6 Mon Sep 17 00:00:00 2001 From: Andrew Savinykh <658865+AndrewSav@users.noreply.github.com> Date: Sat, 13 Jun 2020 10:09:48 +1200 Subject: [PATCH 13/50] updated enviroment, compose and docker files with version references --- Dockerfile | 5 ++--- docker-compose.sample.yml | 6 +++--- sample.env | 14 +++++--------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2529a934..be4764eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,6 @@ -FROM mailserver2/debian-mail-overlay:latest +FROM mailserver2/debian-mail-overlay:1.0.0 -LABEL description="Simple and full-featured mail server using Docker" \ - maintainer="Hardware " +LABEL description="Simple and full-featured mail server using Docker" ARG DEBIAN_FRONTEND=noninteractive ENV PYTHONUNBUFFERED=1 diff --git a/docker-compose.sample.yml b/docker-compose.sample.yml index 6ade8f9f..021a991b 100644 --- a/docker-compose.sample.yml +++ b/docker-compose.sample.yml @@ -33,7 +33,7 @@ services: - http_network mailserver: - image: hardware/mailserver:${MAILSERVER_DOCKER_TAG} + image: mailserver2/mailserver:${MAILSERVER_DOCKER_TAG} restart: ${RESTART_MODE} domainname: ${MAILSERVER_DOMAIN} # Mail server A/MX/FQDN & reverse PTR = mail.domain.tld. hostname: ${MAILSERVER_HOSTNAME} @@ -82,7 +82,7 @@ services: # http://postfixadmin.sourceforge.net/ # Configuration : https://github.com/hardware/mailserver/wiki/Postfixadmin-initial-configuration postfixadmin: - image: hardware/postfixadmin + image: mailserver2/postfixadmin:${POSTFIXADMIN_DOCKER_TAG} restart: ${RESTART_MODE} domainname: ${MAILSERVER_DOMAIN} hostname: ${MAILSERVER_HOSTNAME} @@ -105,7 +105,7 @@ services: # https://www.rainloop.net/ # Configuration : https://github.com/hardware/mailserver/wiki/Rainloop-initial-configuration rainloop: - image: hardware/rainloop + image: mailserver2/rainloop:${RAINLOOP_DOCKER_TAG} restart: ${RESTART_MODE} labels: - traefik.enable=true diff --git a/sample.env b/sample.env index 9990f8a3..13b18634 100644 --- a/sample.env +++ b/sample.env @@ -24,15 +24,11 @@ RSPAMD_PASSWORD= MAILSERVER_HOSTNAME=mail # Mailserver version -# 1.1-stable : Stable version (v1.1-stable github branch) -# 1.1-latest : Latest development build (master github branch) -# Latest development builds have been validated through -# the CI automation system but they are not meant for -# deployment in production. -# 1.1-stable and 1.1-latest are rebuilt weekly on the docker hub but if you -# have any problem with a new build you can switch to 1.1-stable-fallback -# The fallback tag is rebuilt monthly. -MAILSERVER_DOCKER_TAG=1.1-stable +MAILSERVER_DOCKER_TAG=1.1.1 + +POSTFIXADMIN_DOCKER_TAG=3.2.4 + +RAINLOOP_DOCKER_TAG=1.14.0 # Docker volumes parent folder VOLUMES_ROOT_PATH=/mnt/docker From 264bb881ef416724d49f10985b2d2ae0a7800f9c Mon Sep 17 00:00:00 2001 From: Istvan Szabo <4446568+sknight80@users.noreply.github.com> Date: Sat, 18 Jul 2020 10:29:20 -0400 Subject: [PATCH 14/50] Added compression to Dovecot IMAP (#13) * added imap compression to dovecot * when we don't have images, we don't need to fail * refactor zlib and imap compression * fixed test Co-authored-by: Istvan Szabo --- Dockerfile | 2 ++ Makefile | 2 +- rootfs/etc/dovecot/conf.d/10-mail.conf | 7 ++++++- rootfs/etc/dovecot/conf.d/20-imap.conf | 2 +- test/tests.bats | 6 +++--- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index be4764eb..96ec0142 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,8 @@ RUN apt-get update && apt-get install -y -q --no-install-recommends \ python3-pip python3-setuptools python3-wheel python3-gpg \ rsyslog dnsutils curl unbound jq rsync \ inotify-tools \ + # To enable compression in imap + arj bzip2 cabextract cpio file gzip nomarch pax unzip zip \ && rm -rf /var/spool/postfix \ && ln -s /var/mail/postfix/spool /var/spool/postfix \ && apt-get autoremove -y \ diff --git a/Makefile b/Makefile index 237dc237..911237c2 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ init: -docker rm -f \ mariadb postgres redis openldap \ mailserver_default mailserver_reverse mailserver_ecdsa mailserver_ldap mailserver_ldap2 \ - mailserver_traefik_acmev1 mailserver_traefik_acmev2 + mailserver_traefik_acmev1 mailserver_traefik_acmev2 || true sleep 2 diff --git a/rootfs/etc/dovecot/conf.d/10-mail.conf b/rootfs/etc/dovecot/conf.d/10-mail.conf index 5b05c5fb..f1d8eb82 100644 --- a/rootfs/etc/dovecot/conf.d/10-mail.conf +++ b/rootfs/etc/dovecot/conf.d/10-mail.conf @@ -1,4 +1,4 @@ -mail_plugins = $mail_plugins quota +mail_plugins = $mail_plugins quota zlib mail_location = maildir:/var/mail/vhosts/%d/%n/{{ .VMAIL_SUBDIR }} maildir_stat_dirs=yes @@ -13,3 +13,8 @@ first_valid_uid = {{ .VMAILUID }} last_valid_uid = {{ .VMAILUID }} mail_privileged_group = vmail + +plugin { + zlib_save_level = 6 # 1..9 + zlib_save = gz # or bz2, If this config entry missing, compression is disabled. +} \ No newline at end of file diff --git a/rootfs/etc/dovecot/conf.d/20-imap.conf b/rootfs/etc/dovecot/conf.d/20-imap.conf index b722873a..c389e97e 100644 --- a/rootfs/etc/dovecot/conf.d/20-imap.conf +++ b/rootfs/etc/dovecot/conf.d/20-imap.conf @@ -2,7 +2,7 @@ imap_idle_notify_interval = 4 mins protocol imap { - mail_plugins = $mail_plugins imap_quota imap_sieve + mail_plugins = $mail_plugins imap_quota imap_sieve imap_zlib imap_client_workarounds = tb-extra-mailbox-sep mail_max_userip_connections = 20 diff --git a/test/tests.bats b/test/tests.bats index 9e100524..576b383f 100644 --- a/test/tests.bats +++ b/test/tests.bats @@ -1777,13 +1777,13 @@ load 'test_helper/bats-assert/load' } @test "checking zeyple: 3 emails encrypted in john.doe folder" { - run docker exec mailserver_reverse /bin/sh -c "grep -i 'multipart/encrypted' /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | wc -l" + run docker exec mailserver_reverse /bin/sh -c "gzip -cd /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | grep -i 'multipart/encrypted' | wc -l" assert_success assert_output 3 - run docker exec mailserver_reverse /bin/sh -c "grep -i 'BEGIN PGP MESSAGE' /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | wc -l" + run docker exec mailserver_reverse /bin/sh -c "gzip -cd /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | grep -i 'BEGIN PGP MESSAGE' | wc -l" assert_success assert_output 3 - run docker exec mailserver_reverse /bin/sh -c "grep -i 'END PGP MESSAGE' /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | wc -l" + run docker exec mailserver_reverse /bin/sh -c "gzip -cd /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | grep -i 'END PGP MESSAGE' | wc -l" assert_success assert_output 3 } From fee7b16791e536c20f365b1d6910c9c0bdeb4cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Thu, 27 Aug 2020 18:06:53 +0200 Subject: [PATCH 15/50] Add CHANGELOG.md --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..792added --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog + +## v1.1.2 + +### New features + +* Added compression to Dovecot IMAP ([#13](https://github.com/mailserver2/mailserver/pull/13)) + +## v1.1.1 + +* First version of the mailserver2 fork. +* Updated everything to latest versions. + +## v1.1.0 + +* This is the last version of [hardware/mailserver](https://github.com/hardware/mailserver). From 24d80a5b51675301dc42c8ec64c2ba7afc6870f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Thu, 17 Sep 2020 16:09:52 +0200 Subject: [PATCH 16/50] Tests: update osixia/openldap docker image to version 1.4.0 --- CHANGELOG.md | 6 +++++- Makefile | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 792added..1ca20d6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,14 @@ # Changelog +## Unreleased + +* Tests: updated osixia/openldap docker image to version [1.4.0](https://github.com/osixia/docker-openldap/releases/tag/v1.4.0). + ## v1.1.2 ### New features -* Added compression to Dovecot IMAP ([#13](https://github.com/mailserver2/mailserver/pull/13)) +* Added compression to Dovecot IMAP ([#13](https://github.com/mailserver2/mailserver/pull/13)). ## v1.1.1 diff --git a/Makefile b/Makefile index 911237c2..b5684f16 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ init: -e LDAP_ADMIN_PASSWORD="testpasswd" \ -e LDAP_TLS=false \ -v "`pwd`/test/config/ldap/struct.ldif":/container/service/slapd/assets/config/bootstrap/ldif/custom/struct.ldif \ - -t osixia/openldap:1.3.0 --copy-service + -t osixia/openldap:1.4.0 --copy-service sleep 10 From 7e85cccb0f4a9909a0cda35625f567ab1cab0d80 Mon Sep 17 00:00:00 2001 From: Andrew Savinykh <658865+AndrewSav@users.noreply.github.com> Date: Tue, 22 Sep 2020 19:47:57 +1200 Subject: [PATCH 17/50] Upgrade configs to traefik 2 (#17) updated traefik configs to traefik 2 --- README.md | 10 +++- docker-compose.sample.yml | 97 ++++++++++++++++++++++++++------------- file.sample.toml | 11 +++++ sample.env | 5 ++ traefik.sample.toml | 70 ++++++++++++---------------- 5 files changed, 120 insertions(+), 73 deletions(-) create mode 100644 file.sample.toml diff --git a/README.md b/README.md index 54f95bc2..d731d3e9 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ Thank you very much. - [Postfix blacklist](#postfix-blacklist) - [Email client settings](#email-client-settings) - [Components](#components) + - [Migration from Traefik 1 to 2](#migration-from-traefik-1-to-2) - [Migration from 1.0 to 1.1](#migration-from-10-to-11) - [Migration from hardware/mailserver to mailserver2/mailserver](#migration-from-hardwaremailserver-to-mailserver2mailserver) - [Community projects](#community-projects) @@ -218,8 +219,9 @@ mkdir -p /mnt/docker/traefik/acme && cd /mnt/docker \ && curl https://raw.githubusercontent.com/mailserver2/mailserver/master/docker-compose.sample.yml -o docker-compose.yml \ && curl https://raw.githubusercontent.com/mailserver2/mailserver/master/sample.env -o .env \ && curl https://raw.githubusercontent.com/mailserver2/mailserver/master/traefik.sample.toml -o traefik/traefik.toml \ +&& curl https://raw.githubusercontent.com/mailserver2/mailserver/master/file.sample.toml -o traefik/file.toml \ && touch traefik/acme/acme.json \ -&& chmod 600 docker-compose.yml .env traefik/traefik.toml traefik/acme/acme.json +&& chmod 600 docker-compose.yml .env traefik/traefik.toml traefik/file.toml traefik/acme/acme.json ``` Edit the `.env` and `traefik.toml`, adapt to your needs, then start all services: @@ -1094,6 +1096,12 @@ NOQUEUE: reject: 554 5.7.1 : Sender address rejected: Acces

Back to table of contents :arrow_up_small:

+### Migration from Traefik 1 to 2 + +Migrating from traefik 1 to 2 does not change any mailserver images that are used, only the traefik image version. You do need to update traefik labels on all containers as per [docker-compose.sample.yml](docker-compose.sample.yml). When you do this, either delete your acme.json and let traefik request the new certificate, or use the [tool](https://github.com/traefik/traefik-migration-tool) to convert from v1 to v2. + +

Back to table of contents :arrow_up_small:

+ ### Migration from 1.0 to 1.1 If you still use 1.0 version (bundled with Spamassassin, Amavisd...etc) which was available with the `latest` tag, you can follow the migration steps here: diff --git a/docker-compose.sample.yml b/docker-compose.sample.yml index 021a991b..91ff0ed0 100644 --- a/docker-compose.sample.yml +++ b/docker-compose.sample.yml @@ -1,4 +1,4 @@ -version: "3" +version: '3.7' # IPv4 only # docker network create http_network @@ -14,35 +14,35 @@ networks: external: false services: + traefik: - image: traefik:1.7-alpine + image: "traefik:${TRAEFIK_DOCKER_TAG}" restart: ${RESTART_MODE} + networks: + - http_network ports: + # This allows incoming connection on 80 to be forwarder to port 80 of traefik - "80:80" + # This allows incoming connection on 443 to be forwarder to port 443 of traefik - "443:443" - labels: - - traefik.enable=true - - traefik.frontend.rule=Host:mail.${MAILSERVER_DOMAIN} - - traefik.port=8080 - - traefik.docker.network=http_network + # As above. Browse to port 8080 http to see trafik dashboard + # - "8080:8080" volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - - ${VOLUMES_ROOT_PATH}/traefik/traefik.toml:/traefik.toml:ro - - ${VOLUMES_ROOT_PATH}/traefik/acme:/etc/traefik/acme - networks: - - http_network + # static config + - "${VOLUMES_ROOT_PATH}/traefik/traefik.toml:/traefik.toml" + # dynamic config + - "${VOLUMES_ROOT_PATH}/traefik/file.toml:/file.toml" + # let's encrypt data + - "${VOLUMES_ROOT_PATH}/traefik/acme:/acme" + # This is required for the docker provider of traefik to work (read container labels, etc) + - "/var/run/docker.sock:/var/run/docker.sock:ro" mailserver: image: mailserver2/mailserver:${MAILSERVER_DOCKER_TAG} restart: ${RESTART_MODE} domainname: ${MAILSERVER_DOMAIN} # Mail server A/MX/FQDN & reverse PTR = mail.domain.tld. hostname: ${MAILSERVER_HOSTNAME} - labels: - - traefik.enable=true - - traefik.frontend.rule=Host:spam.${MAILSERVER_DOMAIN} - - traefik.port=11334 - - traefik.docker.network=http_network - # extra_hosts: - Required for external database (on other server or for local databases on host without docker) + # extra_hosts: - Required for external database (on other server or for local database on host) # - "mariadb:xx.xx.xx.xx" - Replace with IP address of MariaDB server # - "redis:xx.xx.xx.xx" - Replace with IP address of Redis server ports: @@ -54,10 +54,11 @@ services: - "993:993" # IMAPS SSL/TLS - Optional - For webmails/desktop clients # - "995:995" # POP3S SSL/TLS - Optional - For webmails/desktop clients - "4190:4190" # SIEVE STARTTLS - Optional - Recommended for mail filtering + # - "11334:11334" # HTTP - Optional - Rspamd WebUI environment: - DBPASS=${DATABASE_USER_PASSWORD} # MariaDB database password (required) - RSPAMD_PASSWORD=${RSPAMD_PASSWORD} # Rspamd WebUI password (required) - # - ADD_DOMAINS=aa.tld, www.bb.tld... # Add additional domains separated by commas (needed for dkim keys etc.) + - ADD_DOMAINS=${ADD_DOMAINS} # Add additional domains separated by commas (needed for dkim keys etc.) # - DEBUG_MODE=true # Enable Postfix, Dovecot, Rspamd and Unbound verbose logging # - ENABLE_POP3=true # Enable POP3 protocol # - ENABLE_FETCHMAIL=true # Enable fetchmail forwarding @@ -68,8 +69,22 @@ services: # # Full list : https://github.com/hardware/mailserver#environment-variables # + labels: + - "traefik.enable=true" + - "traefik.docker.network=http_network" + - "traefik.http.routers.spam.entrypoints=websecure" + - "traefik.http.routers.spam.rule=Host(`spam.${MAILSERVER_DOMAIN}`)" + - "traefik.http.routers.spam.service=spam" + - "traefik.http.routers.spam.tls=true" + - "traefik.http.routers.spam.tls.certresolver=letsencrypt" + - "traefik.http.routers.spam.tls.domains[0].main=${MAILSERVER_HOSTNAME}.${MAILSERVER_DOMAIN}" + - "traefik.http.routers.spam.tls.domains[0].sans=spam.${MAILSERVER_DOMAIN}" + - "traefik.http.routers.spam.tls.options=default" + - "traefik.http.services.spam.loadbalancer.server.port=11334" + - "traefik.http.services.spam.loadbalancer.server.scheme=http" volumes: - ${VOLUMES_ROOT_PATH}/mail:/var/mail + - ${VOLUMES_ROOT_PATH}/traefik/acme:/etc/letsencrypt/acme depends_on: - mariadb - redis @@ -87,10 +102,17 @@ services: domainname: ${MAILSERVER_DOMAIN} hostname: ${MAILSERVER_HOSTNAME} labels: - - traefik.enable=true - - traefik.frontend.rule=Host:postfixadmin.${MAILSERVER_DOMAIN} - - traefik.port=8888 - - traefik.docker.network=http_network + - "traefik.enable=true" + - "traefik.docker.network=http_network" + - "traefik.http.routers.postfixadmin.entrypoints=websecure" + - "traefik.http.routers.postfixadmin.rule=Host(`postfixadmin.${MAILSERVER_DOMAIN}`)" + - "traefik.http.routers.postfixadmin.service=postfixadmin" + - "traefik.http.routers.postfixadmin.tls=true" + - "traefik.http.routers.postfixadmin.tls.certresolver=letsencrypt" + - "traefik.http.routers.postfixadmin.tls.domains[0].main=postfixadmin.${MAILSERVER_DOMAIN}" + - "traefik.http.routers.postfixadmin.tls.options=default" + - "traefik.http.services.postfixadmin.loadbalancer.server.port=8888" + - "traefik.http.services.postfixadmin.loadbalancer.server.scheme=http" environment: - DBPASS=${DATABASE_USER_PASSWORD} depends_on: @@ -108,12 +130,21 @@ services: image: mailserver2/rainloop:${RAINLOOP_DOCKER_TAG} restart: ${RESTART_MODE} labels: - - traefik.enable=true - - traefik.port=8888 - - traefik.frontend.rule=Host:webmail.${MAILSERVER_DOMAIN} - - traefik.docker.network=http_network + - "traefik.enable=true" + - "traefik.docker.network=http_network" + - "traefik.http.routers.rainloop.entrypoints=websecure" + - "traefik.http.routers.rainloop.rule=Host(`webmail.${MAILSERVER_DOMAIN}`)" + - "traefik.http.routers.rainloop.service=rainloop" + - "traefik.http.routers.rainloop.tls=true" + - "traefik.http.routers.rainloop.tls.certresolver=letsencrypt" + - "traefik.http.routers.rainloop.tls.domains[0].main=webmail.${MAILSERVER_DOMAIN}" + - "traefik.http.routers.rainloop.tls.options=default" + - "traefik.http.services.rainloop.loadbalancer.server.port=8888" + - "traefik.http.services.rainloop.loadbalancer.server.scheme=http" volumes: - ${VOLUMES_ROOT_PATH}/rainloop:/rainloop/data + #environment: + #LOG_TO_STDOUT: "true" depends_on: - mailserver - mariadb @@ -140,7 +171,7 @@ services: # https://github.com/docker-library/mariadb # https://mariadb.org/ mariadb: - image: mariadb:10.2 + image: mariadb:10.5 restart: ${RESTART_MODE} # Info : These variables are ignored when the volume already exists (if databases was created before). environment: @@ -153,14 +184,16 @@ services: networks: - mail_network - # Database - # https://github.com/docker-library/redis + # Cache Database + # https://github.com/docker-library/redis. # https://redis.io/ redis: - image: redis:4.0-alpine + image: redis:6.0-alpine restart: ${RESTART_MODE} command: redis-server --appendonly yes + sysctls: + - net.core.somaxconn=1024 volumes: - - ${VOLUMES_ROOT_PATH}/redis/db:/data + - ${VOLUMES_ROOT_PATH}/redis/db/:/data networks: - mail_network diff --git a/file.sample.toml b/file.sample.toml new file mode 100644 index 00000000..4c6182c9 --- /dev/null +++ b/file.sample.toml @@ -0,0 +1,11 @@ +[tls.options.default] +minVersion = "VersionTLS12" +cipherSuites = [ + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" +] + diff --git a/sample.env b/sample.env index 13b18634..81595cb5 100644 --- a/sample.env +++ b/sample.env @@ -30,9 +30,14 @@ POSTFIXADMIN_DOCKER_TAG=3.2.4 RAINLOOP_DOCKER_TAG=1.14.0 +TRAEFIK_DOCKER_TAG=2.2.11 + # Docker volumes parent folder VOLUMES_ROOT_PATH=/mnt/docker # Docker containers restart mode # https://docs.docker.com/compose/compose-file/#restart RESTART_MODE=unless-stopped + +# Add additional domains separated by commas (needed for dkim keys etc.) +ADD_DOMAINS= diff --git a/traefik.sample.toml b/traefik.sample.toml index 81e1996d..04394bb2 100644 --- a/traefik.sample.toml +++ b/traefik.sample.toml @@ -1,40 +1,30 @@ -defaultEntryPoints = ["https","http"] - -[api] -entryPoint = "traefik" -dashboard = true - -[entryPoints] - [entryPoints.http] - address = ":80" - [entryPoints.http.redirect] - entryPoint = "https" - [entryPoints.https] - address = ":443" - [entryPoints.https.tls] - minVersion = "VersionTLS12" - cipherSuites = [ - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" - ] - [entryPoints.traefik] - address = ":8080" - [entryPoints.traefik.auth.basic] - users = ["admin:{SHA}jLIjfQZ5yojbZGTqxg2pY0VROWQ="] - -[acme] -email = "{{ EMAIL }}" -storage = "/etc/traefik/acme/acme.json" -entryPoint = "https" -onHostRule = true - [acme.tlsChallenge] - -[docker] -endpoint = "unix:///var/run/docker.sock" -domain = "{{ DOMAIN }}" -watch = true -exposedbydefault = false +## static configuration + +[entryPoints.websecure] +address = ":443" + +[entryPoints.web] +address = ":80" +[entryPoints.web.http.redirections.entryPoint] +to = "websecure" + +[providers.docker] +exposedByDefault=false + +[providers.file] +filename = "/file.toml" + + +[log] +#level = "DEBUG" + +[accessLog] + +#[api] +#insecure = true + +[certificatesResolvers.letsencrypt.acme] +email = "admin@domain.com" +#caserver= "https://acme-staging-v02.api.letsencrypt.org/directory" +storage = "/acme/acme.json" +[certificatesResolvers.letsencrypt.acme.tlsChallenge] From ff89129421fa58a3f5e01eaca4946cd8feb1da43 Mon Sep 17 00:00:00 2001 From: Istvan Szabo <4446568+sknight80@users.noreply.github.com> Date: Tue, 22 Sep 2020 09:50:07 -0400 Subject: [PATCH 18/50] bump image version to 1.0.1 (#20) Bump to new base image version --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 96ec0142..0cac7a7c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.0 +FROM mailserver2/debian-mail-overlay:1.0.1 LABEL description="Simple and full-featured mail server using Docker" From 878001ce4d3651e9fcf27b82eee02bb784dffe2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Sun, 4 Oct 2020 15:33:46 +0200 Subject: [PATCH 19/50] update to mailserver2/debian-mail-overlay:1.0.2 --- CHANGELOG.md | 6 ++++++ Dockerfile | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ca20d6d..913af142 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,13 @@ ## Unreleased +* Bump to new base image version 1.0.2 with rspamd 2.6 + +## v1.1.3 + * Tests: updated osixia/openldap docker image to version [1.4.0](https://github.com/osixia/docker-openldap/releases/tag/v1.4.0). +* Upgrade configs to traefik 2 ([#17](https://github.com/mailserver2/mailserver/pull/17)) +* Bump to new base image version 1.0.1 with new s6 ## v1.1.2 diff --git a/Dockerfile b/Dockerfile index 0cac7a7c..3d66a3ea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.1 +FROM mailserver2/debian-mail-overlay:1.0.2 LABEL description="Simple and full-featured mail server using Docker" From 66212f3cf4b96feaec85c0962a381dba3a591410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Thu, 1 Oct 2020 17:38:39 +0200 Subject: [PATCH 20/50] adjust test to new virus log message --- test/tests.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests.bats b/test/tests.bats index 576b383f..f2dd39a3 100644 --- a/test/tests.bats +++ b/test/tests.bats @@ -1323,7 +1323,7 @@ load 'test_helper/bats-assert/load' } @test "checking postfix: milter-reject - clamav virus found" { - run docker exec mailserver_default grep -i 'milter-reject.*Spam message rejected; from=' /var/log/mail.log + run docker exec mailserver_default grep -i 'milter-reject.*virus found: "{HEX}EICAR.TEST.3.UNOFFICIAL"; from=' /var/log/mail.log assert_success } From e19c75b22b12d74dfc35635df4a1038a23acb1d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Mon, 5 Oct 2020 16:40:12 +0200 Subject: [PATCH 21/50] update to mailserver2/debian-mail-overlay:1.0.3 --- CHANGELOG.md | 1 + Dockerfile | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 913af142..8c3c1131 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Bump to new base image version 1.0.2 with rspamd 2.6 +* Bump to new base image version 1.0.3 with updated Skalibs 2.9.3.0 and Execline 2.6.1.1 ## v1.1.3 diff --git a/Dockerfile b/Dockerfile index 3d66a3ea..a5ade4c4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.2 +FROM mailserver2/debian-mail-overlay:1.0.3 LABEL description="Simple and full-featured mail server using Docker" From c8e1ef35d2e7d8a4c3dc1e379de34b8aadfeaba5 Mon Sep 17 00:00:00 2001 From: Rafael Cossovan Date: Mon, 5 Oct 2020 12:09:45 -0300 Subject: [PATCH 22/50] Fix certificate watching (auto renew). --- rootfs/usr/local/bin/certs_helper.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rootfs/usr/local/bin/certs_helper.sh b/rootfs/usr/local/bin/certs_helper.sh index 19437c2b..30110432 100644 --- a/rootfs/usr/local/bin/certs_helper.sh +++ b/rootfs/usr/local/bin/certs_helper.sh @@ -144,8 +144,8 @@ elif [ "$1" = "update_certs" ]; then _normalize_certs "$NORMALIZED_CERT_PATH" [ $? -ne 0 ] && exit 1 - # Compare Old and New key - if cmp --silent "$NORMALIZED_CERT_PATH"/privkey.pem "$LIVE_CERT_PATH"/privkey.pem; then + # Compare old and new certificates + if cmp --silent "$NORMALIZED_CERT_PATH"/fullchain.pem "$LIVE_CERT_PATH"/fullchain.pem; then echo "[INFO] Live Certificates match" rm -rf "$CERT_TEMP_PATH" exit 1 From 6f8a45a2805718734f744e10b738eb7b3b912850 Mon Sep 17 00:00:00 2001 From: Andrew Savinykh <658865+AndrewSav@users.noreply.github.com> Date: Sun, 11 Oct 2020 22:10:49 +1300 Subject: [PATCH 23/50] Update ISSUE_TEMPLATE.md --- .github/ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 3c8e8ed0..7cc55fa6 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -23,7 +23,7 @@ _Please delete options that are not relevant._ #### Docker information ``` docker info -docker images hardware/mailserver --digests --filter "dangling=false" +docker images mailserver2/mailserver --digests --filter "dangling=false" ``` #### Description From efda6445a02c034272ab43d72d355a77a1910472 Mon Sep 17 00:00:00 2001 From: Andrew Savinykh <658865+AndrewSav@users.noreply.github.com> Date: Fri, 16 Oct 2020 07:47:43 +1300 Subject: [PATCH 24/50] clarified tagging a bit more in VERSIONING.md (#21) * clarified tagging a bit more in VERSIONING.md * added a step to create a GitHub release. Added Semantic Versioning as preferable versioning scheme --- VERSIONING.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/VERSIONING.md b/VERSIONING.md index 9c025a4c..006ac544 100644 --- a/VERSIONING.md +++ b/VERSIONING.md @@ -7,7 +7,7 @@ This guidelines apply to versioning of docker images in mailserver2 repos: - postfixadmin - rainloop -Historically the old [hardware/debian-mail-overlay](https://hub.docker.com/r/hardware/debian-mail-overlay/tags) image seemed to be versioned after `rspamd` it includes. However, apart of `rspamd` the image contains a lot of other differently versioned software. In this regard, it's more logical to have an independent version, and note down the version of individual software component in the commit messages and/or release history. +Historically the old [hardware/debian-mail-overlay](https://hub.docker.com/r/hardware/debian-mail-overlay/tags) image seemed to be versioned after `rspamd` it includes. However, apart of `rspamd` the image contains a lot of other differently versioned software. In this regard, it's more logical to have an independent version, and note down the version of individual software component in the commit messages and/or release history. Where possible, use [Semantic Versioning](https://semver.org/). For `rainloop` and `postfixadmin` it makes sense to continue to version the images after the respective software versions, since it's not likely that they may require a version bump for any other reason. @@ -15,8 +15,11 @@ In certain case it's also possible for the `Dockerfile` itself to change without Follow these steps when an update is ready to be pushed to Docker Hub: +- Make sure you are on the master branch and that the HEAD is pointing to the lastest commit (`git checkout master`). `git log --name-status HEAD^..HEAD` should give you the same commit number that the latest commit you see at - Tag the current GitHub commit in the `mailserver2` repo with the next version, e.g. `git tag -a v1.0.1 -m "update postfixadmin to version 1.0.1, ... other changes here"` -- Build the new image +- Push tags with `git push --tags` +- Create a release corresponding to the tag under "Releases" section of GitHub repository +- Build the new docker image (see below for an example) - Label it with the version you tagged the current commit with - Push this image version with the label above as well as with the `latest` label From d35be258674173eebdf756fc10e173868c60a5f0 Mon Sep 17 00:00:00 2001 From: Istvan Szabo <4446568+sknight80@users.noreply.github.com> Date: Thu, 15 Oct 2020 14:52:00 -0400 Subject: [PATCH 25/50] Optional vhosts directory ownership setup (#25) * add disable vhosts ownership set config variable * add new config option to readme.md Co-authored-by: Istvan Szabo --- CHANGELOG.md | 1 + README.md | 255 +++++++++++++++++----------------- docker-compose.sample.yml | 1 + rootfs/usr/local/bin/setup.sh | 9 +- 4 files changed, 138 insertions(+), 128 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c3c1131..473591e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Bump to new base image version 1.0.2 with rspamd 2.6 * Bump to new base image version 1.0.3 with updated Skalibs 2.9.3.0 and Execline 2.6.1.1 +* `DISABLE_VHOSTS_OWNERSHIP_SET` configuration option ## v1.1.3 diff --git a/README.md b/README.md index d731d3e9..1bf465e2 100644 --- a/README.md +++ b/README.md @@ -104,15 +104,15 @@ Please check, if your system meets the following minimum requirements: | Type | Without ClamAV | With ClamAV | | ---- | -------------- | ----------- | -| CPU | 1 GHz | 1 GHz | -| RAM | 1.5 GiB | 2 GiB | +| CPU | 1 GHz | 1 GHz | +| RAM | 1.5 GiB | 2 GiB | #### With MariaDB/PostgreSQL and Redis hosted on another server | Type | Without ClamAV | With ClamAV | | ---- | -------------- | ----------- | -| CPU | 1 GHz | 1 GHz | -| RAM | 512 MiB | 1 GiB | +| CPU | 1 GHz | 1 GHz | +| RAM | 512 MiB | 1 GiB |

Back to table of contents :arrow_up_small:

@@ -138,18 +138,18 @@ If this command returns any results please remove or stop the application runnin If you have a firewall, unblock the following ports, according to your needs: -| Service | Software | Protocol | Port | -| ------- | -------- | -------- | ---- | -| SMTP | Postfix | TCP | 25 | -| HTTP | Nginx | TCP | 80 | -| POP3 | Dovecot | TCP | 110 | -| IMAP | Dovecot | TCP | 143 | -| HTTPS | Nginx | TCP | 443 | -| SMTPS | Postfix | TCP | 465 | -| Submission | Postfix | TCP | 587 | -| IMAPS | Dovecot | TCP | 993 | -| POP3S | Dovecot | TCP | 995 | -| ManageSieve | Dovecot | TCP | 4190 | +| Service | Software | Protocol | Port | +| ----------- | -------- | -------- | ---- | +| SMTP | Postfix | TCP | 25 | +| HTTP | Nginx | TCP | 80 | +| POP3 | Dovecot | TCP | 110 | +| IMAP | Dovecot | TCP | 143 | +| HTTPS | Nginx | TCP | 443 | +| SMTPS | Postfix | TCP | 465 | +| Submission | Postfix | TCP | 587 | +| IMAPS | Dovecot | TCP | 993 | +| POP3S | Dovecot | TCP | 995 | +| ManageSieve | Dovecot | TCP | 4190 | #### DNS setup @@ -159,16 +159,16 @@ I recommend you to use [hardware/nsd-dnssec](https://github.com/hardware/nsd-dns A correct DNS setup is required, this step is very important. -| HOSTNAME | CLASS | TYPE | PRIORITY | VALUE | -| -------- | ----- | ---- | -------- | ----- | -| mail | IN | A/AAAA | any | 1.2.3.4 | -| spam | IN | CNAME | any | mail.domain.tld. | -| webmail | IN | CNAME | any | mail.domain.tld. | -| postfixadmin | IN | CNAME | any | mail.domain.tld. | -| @ | IN | MX | 10 | mail.domain.tld. | -| @ | IN | TXT | any | "v=spf1 a mx ip4:SERVER_IPV4 ~all" | -| {{selector}}._domainkey | IN | TXT | any | "v=DKIM1; k=rsa; p=YOUR DKIM Public Key" | -| _dmarc | IN | TXT | any | "v=DMARC1; p=reject; rua=mailto:postmaster@domain.tld; ruf=mailto:admin@domain.tld; fo=0; adkim=s; aspf=s; pct=100; rf=afrf; sp=reject" | +| HOSTNAME | CLASS | TYPE | PRIORITY | VALUE | +| ----------------------- | ----- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| mail | IN | A/AAAA | any | 1.2.3.4 | +| spam | IN | CNAME | any | mail.domain.tld. | +| webmail | IN | CNAME | any | mail.domain.tld. | +| postfixadmin | IN | CNAME | any | mail.domain.tld. | +| @ | IN | MX | 10 | mail.domain.tld. | +| @ | IN | TXT | any | "v=spf1 a mx ip4:SERVER_IPV4 ~all" | +| {{selector}}._domainkey | IN | TXT | any | "v=DKIM1; k=rsa; p=YOUR DKIM Public Key" | +| _dmarc | IN | TXT | any | "v=DMARC1; p=reject; rua=mailto:postmaster@domain.tld; ruf=mailto:admin@domain.tld; fo=0; adkim=s; aspf=s; pct=100; rf=afrf; sp=reject" | **Notes:** @@ -250,12 +250,12 @@ At first launch, the container takes few minutes to generate SSL certificates (i **List of webservices available:** -| Service | URI | -| ------- | --- | -| **Traefik dashboard** | https://mail.domain.tld/ | -| **Rspamd dashboard** | https://spam.domain.tld/ | -| **Administration** | https://postfixadmin.domain.tld/ | -| **Webmail** | https://webmail.domain.tld/ | +| Service | URI | +| --------------------- | -------------------------------- | +| **Traefik dashboard** | https://mail.domain.tld/ | +| **Rspamd dashboard** | https://spam.domain.tld/ | +| **Administration** | https://postfixadmin.domain.tld/ | +| **Webmail** | https://webmail.domain.tld/ | Traefik dashboard use a basic authentication (user:admin, password:12345), the password can be encoded in MD5, SHA1 and BCrypt. You can use [htpasswd ](https://httpd.apache.org/docs/2.4/programs/htpasswd.html) to generate those ones. Users can be specified directly in the `traefik.toml` file. Rspamd dashboard use the password defined in your `docker-compose.yml`. @@ -306,42 +306,43 @@ If you use Ansible, I recommend you to go to see [@ksylvan](https://github.com/k ### Environment variables -| Variable | Description | Type | Default value | -| -------- | ----------- | ---- | ------------- | -| **VMAILUID** | vmail user id | *optional* | 1024 -| **VMAILGID** | vmail group id | *optional* | 1024 -| **VMAIL_SUBDIR** | Individual mailbox' subdirectory | *optional* | mail -| **DKIM_KEY_LENGTH** | Size of your DKIM RSA key pair | *optional* | 1024 -| **DKIM_SELECTOR** | Your DKIM selector | *optional* | `mail` -| **DEBUG_MODE** | Enable Postfix, Dovecot, Rspamd and Unbound verbose logging | *optional* | false -| **PASSWORD_SCHEME** | Passwords encryption scheme | *optional* | `SHA512-CRYPT` -| **DBDRIVER** | Database type: mysql, pgsql, ldap | *optional* | mysql -| **DBHOST** | Database instance ip/hostname | *optional* | mariadb -| **DBPORT** | Database instance port | *optional* | 3306 / 389 (sql/ldap) -| **DBUSER** | Database username | *optional* | postfix -| **DBNAME** | Database name | *optional* | postfix -| **DBPASS** | Database password or location of a file containing it | **required** *\*1)* | null -| **REDIS_HOST** | Redis instance ip/hostname | *optional* | redis -| **REDIS_PORT** | Redis instance port | *optional* | 6379 -| **REDIS_PASS** | Redis database password or location of a file containing it | *optional* | null -| **REDIS_NUMB** | Redis database number | *optional* | 0 -| **RSPAMD_PASSWORD** | Rspamd WebUI and controller password or location of a file containing it | **required** | null -| **ADD_DOMAINS** | Add additional domains to the mailserver separated by commas (needed for dkim keys etc.) | *optional* | null -| **RELAY_NETWORKS** | Additional IPs or networks the mailserver relays without authentication | *optional* | null -| **WHITELIST_SPAM_ADDRESSES** | List of whitelisted email addresses separated by commas | *optional* | null -| **DISABLE_RSPAMD_MODULE** | List of disabled modules separated by commas | *optional* | null -| **DISABLE_CLAMAV** | Disable virus scanning | *optional* | false -| **DISABLE_SIEVE** | Disable ManageSieve protocol | *optional* | false -| **DISABLE_SIGNING** | Disable DKIM/ARC signing | *optional* | false -| **DISABLE_GREYLISTING** | Disable greylisting policy | *optional* | false -| **DISABLE_RATELIMITING** | Disable rate limiting policy | *optional* | true -| **DISABLE_DNS_RESOLVER** | Disable the local DNS resolver | *optional* | false -| **DISABLE_SSL_WATCH** | Disable watching of `acme.json` and the Let's Encrypt directory | *optional* | false -| **ENABLE_POP3** | Enable POP3 protocol | *optional* | false -| **ENABLE_FETCHMAIL** | Enable fetchmail forwarding | *optional* | false -| **ENABLE_ENCRYPTION** | Enable automatic GPG encryption | *optional* | false -| **FETCHMAIL_INTERVAL** | Fetchmail polling interval | *optional* | 10 -| **RECIPIENT_DELIMITER** | RFC 5233 subaddress extension separator (single character only) | *optional* | + +| Variable | Description | Type | Default value | +| -------------------------------- | ---------------------------------------------------------------------------------------- | ------------------- | --------------------- | +| **VMAILUID** | vmail user id | *optional* | 1024 | +| **VMAILGID** | vmail group id | *optional* | 1024 | +| **VMAIL_SUBDIR** | Individual mailbox' subdirectory | *optional* | mail | +| **DKIM_KEY_LENGTH** | Size of your DKIM RSA key pair | *optional* | 1024 | +| **DKIM_SELECTOR** | Your DKIM selector | *optional* | `mail` | +| **DEBUG_MODE** | Enable Postfix, Dovecot, Rspamd and Unbound verbose logging | *optional* | false | +| **PASSWORD_SCHEME** | Passwords encryption scheme | *optional* | `SHA512-CRYPT` | +| **DBDRIVER** | Database type: mysql, pgsql, ldap | *optional* | mysql | +| **DBHOST** | Database instance ip/hostname | *optional* | mariadb | +| **DBPORT** | Database instance port | *optional* | 3306 / 389 (sql/ldap) | +| **DBUSER** | Database username | *optional* | postfix | +| **DBNAME** | Database name | *optional* | postfix | +| **DBPASS** | Database password or location of a file containing it | **required** *\*1)* | null | +| **REDIS_HOST** | Redis instance ip/hostname | *optional* | redis | +| **REDIS_PORT** | Redis instance port | *optional* | 6379 | +| **REDIS_PASS** | Redis database password or location of a file containing it | *optional* | null | +| **REDIS_NUMB** | Redis database number | *optional* | 0 | +| **RSPAMD_PASSWORD** | Rspamd WebUI and controller password or location of a file containing it | **required** | null | +| **ADD_DOMAINS** | Add additional domains to the mailserver separated by commas (needed for dkim keys etc.) | *optional* | null | +| **RELAY_NETWORKS** | Additional IPs or networks the mailserver relays without authentication | *optional* | null | +| **WHITELIST_SPAM_ADDRESSES** | List of whitelisted email addresses separated by commas | *optional* | null | +| **DISABLE_RSPAMD_MODULE** | List of disabled modules separated by commas | *optional* | null | +| **DISABLE_CLAMAV** | Disable virus scanning | *optional* | false | +| **DISABLE_SIEVE** | Disable ManageSieve protocol | *optional* | false | +| **DISABLE_SIGNING** | Disable DKIM/ARC signing | *optional* | false | +| **DISABLE_GREYLISTING** | Disable greylisting policy | *optional* | false | +| **DISABLE_RATELIMITING** | Disable rate limiting policy | *optional* | true | +| **DISABLE_DNS_RESOLVER** | Disable the local DNS resolver | *optional* | false | +| **DISABLE_SSL_WATCH** | Disable watching of `acme.json` and the Let's Encrypt directory | *optional* | false | +| **DISABLE_VHOSTS_OWNERSHIP_SET** | Disable set permission on `vhosts` folder | *optional* | false | +| **ENABLE_POP3** | Enable POP3 protocol | *optional* | false | +| **ENABLE_FETCHMAIL** | Enable fetchmail forwarding | *optional* | false | +| **ENABLE_ENCRYPTION** | Enable automatic GPG encryption | *optional* | false | +| **FETCHMAIL_INTERVAL** | Fetchmail polling interval | *optional* | 10 | +| **RECIPIENT_DELIMITER** | RFC 5233 subaddress extension separator (single character only) | *optional* | + | \*1) **DBPASS** is NOT required when using LDAP authentication @@ -356,58 +357,58 @@ If you use Ansible, I recommend you to go to see [@ksylvan](https://github.com/k When using LDAP authentication the following additional variables become available. All *DBUSER*, *DBNAME* and *DBPASS* variables will not be used in this case: -| Variable | Description | Type | Default value | -| -------- | ----------- | ---- | ------------- | -| **LDAP_TLS_ENABLED** | Enable TLS on LDAP | *optional* | false -| **LDAP_TLS_CA_FILE** | The TLS CA File | **required** if **LDAP_TLS_ENABLED** | -| **LDAP_TLS_FORCE** | Force TLS connections | **required** if **LDAP_TLS_ENABLED** | false -| **LDAP_BIND** | Bind to LDAP Server | *optional* | true -| **LDAP_BIND_DN** | The DN to bind to | **required** if **LDAP_BIND** | -| **LDAP_BIND_PW** | LDAP password or location of a file containing it | **required** if **LDAP_BIND** | -| **LDAP_DEFAULT_SEARCH_BASE** | The base DN for all lookups | **required** | -| **LDAP_DEFAULT_SEARCH_SCOPE** | The default scope for all lookups (sub, base or one) | *optional* | sub -| **LDAP_DOMAIN_SEARCH_BASE** | The search base for domain lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} -| **LDAP_DOMAIN_SEARCH_SCOPE** | The search scope for domain lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} -| **LDAP_DOMAIN_FILTER** | The search filter for domain lookups | **required** | -| **LDAP_DOMAIN_ATTRIBUTE** | The attributes for domain lookup | **required** | -| **LDAP_DOMAIN_FORMAT** | The format for domain lookups | *optional* | -| **LDAP_MAILBOX_SEARCH_BASE** | The search base for mailbox lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} -| **LDAP_MAILBOX_SEARCH_SCOPE** | The search scope for mailbox lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} -| **LDAP_MAILBOX_FILTER** | The search filter for mailbox lookups | **required** | -| **LDAP_MAILBOX_ATTRIBUTE** | The attributes for mailbox lookup | **required** | -| **LDAP_MAILBOX_FORMAT** | The format for domain mailbox | *optional* | -| **LDAP_ALIAS_SEARCH_BASE** | The search base for domain lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} -| **LDAP_ALIAS_SEARCH_SCOPE** | The search scope for domain lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} -| **LDAP_ALIAS_FILTER** | The search filter for domain lookups | **required** | -| **LDAP_ALIAS_ATTRIBUTE** | The attributes for domain lookup | **required** | -| **LDAP_ALIAS_FORMAT** | The format for domain lookups | *optional* | -| **LDAP_FORWARD_SEARCH_BASE** | The search base for forward lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} -| **LDAP_FORWARD_SEARCH_SCOPE** | The search scope for forward lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} -| **LDAP_FORWARD_FILTER** | The search filter for forward lookups | *optional* | -| **LDAP_FORWARD_ATTRIBUTE** | The attributes for forward lookup | *optional* | -| **LDAP_FORWARD_FORMAT** | The format for forward lookups | *optional* | -| **LDAP_GROUP_SEARCH_BASE** | The search base for group lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} -| **LDAP_GROUP_SEARCH_SCOPE** | The search scope for group lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} -| **LDAP_GROUP_FILTER** | The search filter for group lookups | *optional* | -| **LDAP_GROUP_ATTRIBUTE** | The attributes for group lookup | *optional* | -| **LDAP_GROUP_FORMAT** | The format for group lookups | *optional* | -| **LDAP_SENDER_SEARCH_BASE** | The search base for sender lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} -| **LDAP_SENDER_SEARCH_SCOPE** | The search scope for sender lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} -| **LDAP_SENDER_FILTER** | The search filter for sender lookups | **required** | -| **LDAP_SENDER_ATTRIBUTE** | The attributes for sender lookup | **required** | -| **LDAP_SENDER_FORMAT** | The format for sender lookups | **required** | -| **LDAP_DOVECOT_USER_ATTRS** | Dovecot user attribute mapping | **required** | -| **LDAP_DOVECOT_USER_FILTER** | Dovecot user search filter | **required** | -| **LDAP_DOVECOT_PASS_ATTRS** | Dovecot user password attribute mapping | **required** | -| **LDAP_DOVECOT_PASS_FILTER** | Dovecot user password filter | **required** | -| **LDAP_DOVECOT_ITERATE_ATTRS** | Dovecot user iterate attributes | *optional* | -| **LDAP_DOVECOT_ITERATE_FILTER** | Dovecot user iterate filters | *optional* | -| **LDAP_MASTER_USER_ENABLED** | Enable LDAP master users | *optional* | false -| **LDAP_MASTER_USER_SEPARATOR** | LDAP master user separator | **required** if **LDAP_MASTER_USER_ENABLED** | \* -| **LDAP_MASTER_USER_SEARCH_BASE** | LDAP master user search base | **required** if **LDAP_MASTER_USER_ENABLED** | ${LDAP_DEFAULT_SEARCH_BASE} -| **LDAP_MASTER_USER_SEARCH_SCOPE** | LDAP master user scope | **required** if **LDAP_MASTER_USER_ENABLED** | ${LDAP_DEFAULT_SEARCH_SCOPE} -| **LDAP_DOVECOT_MASTER_USER_ATTRS** | LDAP master user Dovecot attributes | **required** if **LDAP_MASTER_USER_ENABLED** | -| **LDAP_DOVECOT_MASTER_USER_FILTER** | LDAP master user Dovecot search filter | **required** if **LDAP_MASTER_USER_ENABLED** | +| Variable | Description | Type | Default value | +| ----------------------------------- | ---------------------------------------------------- | -------------------------------------------- | ---------------------------- | +| **LDAP_TLS_ENABLED** | Enable TLS on LDAP | *optional* | false | +| **LDAP_TLS_CA_FILE** | The TLS CA File | **required** if **LDAP_TLS_ENABLED** | +| **LDAP_TLS_FORCE** | Force TLS connections | **required** if **LDAP_TLS_ENABLED** | false | +| **LDAP_BIND** | Bind to LDAP Server | *optional* | true | +| **LDAP_BIND_DN** | The DN to bind to | **required** if **LDAP_BIND** | +| **LDAP_BIND_PW** | LDAP password or location of a file containing it | **required** if **LDAP_BIND** | +| **LDAP_DEFAULT_SEARCH_BASE** | The base DN for all lookups | **required** | +| **LDAP_DEFAULT_SEARCH_SCOPE** | The default scope for all lookups (sub, base or one) | *optional* | sub | +| **LDAP_DOMAIN_SEARCH_BASE** | The search base for domain lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} | +| **LDAP_DOMAIN_SEARCH_SCOPE** | The search scope for domain lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} | +| **LDAP_DOMAIN_FILTER** | The search filter for domain lookups | **required** | +| **LDAP_DOMAIN_ATTRIBUTE** | The attributes for domain lookup | **required** | +| **LDAP_DOMAIN_FORMAT** | The format for domain lookups | *optional* | +| **LDAP_MAILBOX_SEARCH_BASE** | The search base for mailbox lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} | +| **LDAP_MAILBOX_SEARCH_SCOPE** | The search scope for mailbox lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} | +| **LDAP_MAILBOX_FILTER** | The search filter for mailbox lookups | **required** | +| **LDAP_MAILBOX_ATTRIBUTE** | The attributes for mailbox lookup | **required** | +| **LDAP_MAILBOX_FORMAT** | The format for domain mailbox | *optional* | +| **LDAP_ALIAS_SEARCH_BASE** | The search base for domain lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} | +| **LDAP_ALIAS_SEARCH_SCOPE** | The search scope for domain lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} | +| **LDAP_ALIAS_FILTER** | The search filter for domain lookups | **required** | +| **LDAP_ALIAS_ATTRIBUTE** | The attributes for domain lookup | **required** | +| **LDAP_ALIAS_FORMAT** | The format for domain lookups | *optional* | +| **LDAP_FORWARD_SEARCH_BASE** | The search base for forward lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} | +| **LDAP_FORWARD_SEARCH_SCOPE** | The search scope for forward lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} | +| **LDAP_FORWARD_FILTER** | The search filter for forward lookups | *optional* | +| **LDAP_FORWARD_ATTRIBUTE** | The attributes for forward lookup | *optional* | +| **LDAP_FORWARD_FORMAT** | The format for forward lookups | *optional* | +| **LDAP_GROUP_SEARCH_BASE** | The search base for group lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} | +| **LDAP_GROUP_SEARCH_SCOPE** | The search scope for group lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} | +| **LDAP_GROUP_FILTER** | The search filter for group lookups | *optional* | +| **LDAP_GROUP_ATTRIBUTE** | The attributes for group lookup | *optional* | +| **LDAP_GROUP_FORMAT** | The format for group lookups | *optional* | +| **LDAP_SENDER_SEARCH_BASE** | The search base for sender lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} | +| **LDAP_SENDER_SEARCH_SCOPE** | The search scope for sender lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} | +| **LDAP_SENDER_FILTER** | The search filter for sender lookups | **required** | +| **LDAP_SENDER_ATTRIBUTE** | The attributes for sender lookup | **required** | +| **LDAP_SENDER_FORMAT** | The format for sender lookups | **required** | +| **LDAP_DOVECOT_USER_ATTRS** | Dovecot user attribute mapping | **required** | +| **LDAP_DOVECOT_USER_FILTER** | Dovecot user search filter | **required** | +| **LDAP_DOVECOT_PASS_ATTRS** | Dovecot user password attribute mapping | **required** | +| **LDAP_DOVECOT_PASS_FILTER** | Dovecot user password filter | **required** | +| **LDAP_DOVECOT_ITERATE_ATTRS** | Dovecot user iterate attributes | *optional* | +| **LDAP_DOVECOT_ITERATE_FILTER** | Dovecot user iterate filters | *optional* | +| **LDAP_MASTER_USER_ENABLED** | Enable LDAP master users | *optional* | false | +| **LDAP_MASTER_USER_SEPARATOR** | LDAP master user separator | **required** if **LDAP_MASTER_USER_ENABLED** | \* | +| **LDAP_MASTER_USER_SEARCH_BASE** | LDAP master user search base | **required** if **LDAP_MASTER_USER_ENABLED** | ${LDAP_DEFAULT_SEARCH_BASE} | +| **LDAP_MASTER_USER_SEARCH_SCOPE** | LDAP master user scope | **required** if **LDAP_MASTER_USER_ENABLED** | ${LDAP_DEFAULT_SEARCH_SCOPE} | +| **LDAP_DOVECOT_MASTER_USER_ATTRS** | LDAP master user Dovecot attributes | **required** if **LDAP_MASTER_USER_ENABLED** | +| **LDAP_DOVECOT_MASTER_USER_FILTER** | LDAP master user Dovecot search filter | **required** if **LDAP_MASTER_USER_ENABLED** |

Back to table of contents :arrow_up_small:

@@ -582,11 +583,11 @@ Required files in this folder: :bulb: If you only have the fullchain.pem and privkey.pem, the startup script extract automatically the cert.pem and chain.pem from fullchain.pem. -| Filename | Description | -|----------|-------------| -| privkey.pem | Private key for the certificate | -| cert.pem | Server certificate only | -| chain.pem | Root and intermediate certificates only, excluding server certificate | +| Filename | Description | +| ------------- | ----------------------------------------------------------------------------------------------- | +| privkey.pem | Private key for the certificate | +| cert.pem | Server certificate only | +| chain.pem | Root and intermediate certificates only, excluding server certificate | | fullchain.pem | All certificates, including server certificate. This is concatenation of cert.pem and chain.pem | Example with [acme.sh](https://acme.sh): @@ -667,8 +668,8 @@ Readme: https://github.com/extremeshok/clamav-unofficial-sigs | Software | Protocol | Port | | -------- | -------- | ---- | -| Rsync | TCP | 873 | -| Curl | TCP | 443 | +| Rsync | TCP | 873 | +| Curl | TCP | 443 | #### Enable clamav-unofficial-sigs diff --git a/docker-compose.sample.yml b/docker-compose.sample.yml index 91ff0ed0..2e28b794 100644 --- a/docker-compose.sample.yml +++ b/docker-compose.sample.yml @@ -66,6 +66,7 @@ services: # - DISABLE_CLAMAV=true # Disable virus scanning # - DISABLE_SIGNING=true # Disable DKIM/ARC signing # - DISABLE_GREYLISTING=true # Disable greylisting policy + # - DISABLE_VHOSTS_OWNERSHIP_SET=true # Disable vhosts directory ownship set (useful, when you have lots of mailboxes) # # Full list : https://github.com/hardware/mailserver#environment-variables # diff --git a/rootfs/usr/local/bin/setup.sh b/rootfs/usr/local/bin/setup.sh index beb6df85..d3f2b4ec 100644 --- a/rootfs/usr/local/bin/setup.sh +++ b/rootfs/usr/local/bin/setup.sh @@ -35,6 +35,8 @@ FETCHMAIL_INTERVAL=${FETCHMAIL_INTERVAL:-10} RELAY_NETWORKS=${RELAY_NETWORKS:-} PASSWORD_SCHEME=${PASSWORD_SCHEME:-"SHA512-CRYPT"} +DISABLE_VHOSTS_OWNERSHIP_SET=${DISABLE_VHOSTS_OWNERSHIP_SET:-false} + # SSL CERTIFICATES # --------------------------------------------------------------------------------------------- @@ -592,7 +594,12 @@ chown -R vmail:vmail /var/mail/sieve chmod +x /etc/dovecot/sieve/*.sh # Check permissions of vhosts directories -find /var/mail/vhosts ! -user vmail -print0 | xargs -0 -r chown vmail:vmail +if [ "$DISABLE_VHOSTS_OWNERSHIP_SET" = false ]; then + find /var/mail/vhosts ! -user vmail -print0 | xargs -0 -r chown vmail:vmail +else + echo "[INFO] VHOSTS directories permission set is disabled" + echo "[WARNING] If you manually created a directory under the vhost folder, you will not receive any emails on that email address!" +fi # Avoid file_dotlock_open function exception rm -f /var/mail/dovecot/instances From a5ad0ff1b7fdee0b7c6f52deead993ec841dcdba Mon Sep 17 00:00:00 2001 From: Istvan Szabo <4446568+sknight80@users.noreply.github.com> Date: Thu, 29 Oct 2020 12:11:17 -0400 Subject: [PATCH 26/50] Update change log with the new release version (#27) --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 473591e4..a7c63dc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## v1.1.4 + * Bump to new base image version 1.0.2 with rspamd 2.6 * Bump to new base image version 1.0.3 with updated Skalibs 2.9.3.0 and Execline 2.6.1.1 * `DISABLE_VHOSTS_OWNERSHIP_SET` configuration option From 3be6c8602b3800154e7b83391b6a2cf626e173c3 Mon Sep 17 00:00:00 2001 From: Andrew Savinykh <658865+AndrewSav@users.noreply.github.com> Date: Fri, 27 Nov 2020 09:11:20 +1300 Subject: [PATCH 27/50] fixing some more log rotation warnings (#26) --- rootfs/usr/local/bin/setup.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rootfs/usr/local/bin/setup.sh b/rootfs/usr/local/bin/setup.sh index d3f2b4ec..69243dad 100644 --- a/rootfs/usr/local/bin/setup.sh +++ b/rootfs/usr/local/bin/setup.sh @@ -765,7 +765,9 @@ fi # --------------------------------------------------------------------------------------------- # Remove invoke-rc.d warning -sed -i 's|rsyslog-rotate|rsyslog-rotate \&>/dev/null|g' /etc/logrotate.d/rsyslog +sed -i 's|invoke-rc.d rsyslog rotate|s6-svc -h /services/rsyslogd|g' /usr/lib/rsyslog/rsyslog-rotate +sed -i 's|invoke-rc.d clamav-daemon reload-log|s6-svc -h /services/clamd|g' /etc/logrotate.d/clamav-daemon +sed -i 's|invoke-rc.d clamav-freshclam reload-log|s6-svc -h /services/freshclam|g' /etc/logrotate.d/clamav-freshclam # Folders and permissions mkdir -p /var/run/fetchmail From 9e4250acec8b35ee49eeef3bf78603ab235e11b4 Mon Sep 17 00:00:00 2001 From: Andrew Savinykh <658865+AndrewSav@users.noreply.github.com> Date: Fri, 4 Dec 2020 09:37:12 +1300 Subject: [PATCH 28/50] updated change log, and made a note to update change log for release (#29) * updated change log, and made a note to update change log every time during the release process * clarified about location of change log based on the PR discussion --- CHANGELOG.md | 30 ------------------------------ VERSIONING.md | 3 +-- 2 files changed, 1 insertion(+), 32 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index a7c63dc2..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,30 +0,0 @@ -# Changelog - -## Unreleased - -## v1.1.4 - -* Bump to new base image version 1.0.2 with rspamd 2.6 -* Bump to new base image version 1.0.3 with updated Skalibs 2.9.3.0 and Execline 2.6.1.1 -* `DISABLE_VHOSTS_OWNERSHIP_SET` configuration option - -## v1.1.3 - -* Tests: updated osixia/openldap docker image to version [1.4.0](https://github.com/osixia/docker-openldap/releases/tag/v1.4.0). -* Upgrade configs to traefik 2 ([#17](https://github.com/mailserver2/mailserver/pull/17)) -* Bump to new base image version 1.0.1 with new s6 - -## v1.1.2 - -### New features - -* Added compression to Dovecot IMAP ([#13](https://github.com/mailserver2/mailserver/pull/13)). - -## v1.1.1 - -* First version of the mailserver2 fork. -* Updated everything to latest versions. - -## v1.1.0 - -* This is the last version of [hardware/mailserver](https://github.com/hardware/mailserver). diff --git a/VERSIONING.md b/VERSIONING.md index 006ac544..a8e98372 100644 --- a/VERSIONING.md +++ b/VERSIONING.md @@ -18,7 +18,7 @@ Follow these steps when an update is ready to be pushed to Docker Hub: - Make sure you are on the master branch and that the HEAD is pointing to the lastest commit (`git checkout master`). `git log --name-status HEAD^..HEAD` should give you the same commit number that the latest commit you see at - Tag the current GitHub commit in the `mailserver2` repo with the next version, e.g. `git tag -a v1.0.1 -m "update postfixadmin to version 1.0.1, ... other changes here"` - Push tags with `git push --tags` -- Create a release corresponding to the tag under "Releases" section of GitHub repository +- Create a release corresponding to the tag under "Releases" section of GitHub repository. Make sure to include change log, when creating a release - Build the new docker image (see below for an example) - Label it with the version you tagged the current commit with - Push this image version with the label above as well as with the `latest` label @@ -32,4 +32,3 @@ docker push mailserver2/debian-mail-overlay:0.0.0 docker tag debian-mail-overlay mailserver2/debian-mail-overlay:latest docker push mailserver2/debian-mail-overlay:latest ``` - From f931cb576ba20dba02fdcc166992c3322904f4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Tue, 12 Jan 2021 15:20:02 +0100 Subject: [PATCH 29/50] Update base image to mailserver2/debian-mail-overlay:1.0.4 (#31) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index a5ade4c4..33dfec9f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.3 +FROM mailserver2/debian-mail-overlay:1.0.4 LABEL description="Simple and full-featured mail server using Docker" From ab9cac04334ae9397045fbb7a06a864fce1ec3c6 Mon Sep 17 00:00:00 2001 From: Istvan Szabo <4446568+sknight80@users.noreply.github.com> Date: Wed, 17 Mar 2021 15:09:44 -0400 Subject: [PATCH 30/50] [FEATURE-Request] Add rbl_override functionality to Postfix (#36) * add rbl_override * fix typo * address reviewer's comments * fix grammar * update text Co-authored-by: Istvan Szabo --- .gitignore | 1 + Makefile | 2 +- README.md | 32 ++++++++++++++++++++++++++++++++ docker-compose.sample.yml | 2 ++ rbl_override.sample | 1 + rootfs/etc/postfix/main.cf | 1 + rootfs/usr/local/bin/setup.sh | 20 ++++++++++++++++++++ 7 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 rbl_override.sample diff --git a/.gitignore b/.gitignore index 30bb7b33..d94608a3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ docker-compose.pgsql.yml traefik.toml .env certs +.testing diff --git a/Makefile b/Makefile index b5684f16..c1aaff97 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -NAME = hardware/mailserver:testing +NAME = mailserver2/mailserver:testing all: build-no-cache init fixtures run clean all-fast: build init fixtures run clean diff --git a/README.md b/README.md index 1bf465e2..44ff864f 100644 --- a/README.md +++ b/README.md @@ -915,6 +915,7 @@ You can read more on how and why [robbertkl/docker-ipv6nat](https://github.com/r └──mail ├──postfix | custom.conf + | rbl_override | sender_access | ├──spool (Postfix queues directory) │ │ defer @@ -1020,6 +1021,37 @@ docker logs -f mailserver [INFO] Custom Postfix configuration file loaded ``` +

Back to table of contents :arrow_up_small:

+ +### Whitelist Hosts/IP Addresses In Postfix + +If a blacklisted server tries to send mail to your server, you should find something like this in your mail log: + +``` +SMTP error from remote mail server after RCPT TO:: host mail.example.com [4.3.2.1]: 554 5.7.1 Service unavailable; Client host [1.2.3.4] blocked using dnsbl.sorbs.net; Currently Sending Spam See: http://www.sorbs.net/lookup.shtml?1.2.3.4 +``` +In this example, the mail server 1.2.3.4 is blacklisted and therefore blocked. + +To whitelist that server, create the file next to your `docker-compose.yml` file as `rbl_override` then add a list all IP addresses or host names (one per line!) that you want to whitelist as an example: +``` +1.2.3.4 OK +iam-good.com OK +iam-bad.com REJECT +``` +Then open your `docker-compose.yml` file and find your `mailserver` service section and go to the `volumes` part. Uncomment the `rbl_override` line: +``` +... + volumes: + - ${VOLUMES_ROOT_PATH}/mail:/var/mail + - ${VOLUMES_ROOT_PATH}/traefik/acme:/etc/letsencrypt/acme + # Comment out the line below, when you want whitelist some IP Addresses or domains in Postfix (please check the documentation) + # - ${VOLUMES_ROOT_PATH}//rbl_override:/etc/postfix/rbl_override +... +``` +Don't forget to update the `` section based on your environment setup. Then restart your `mailserver`. + + +

Back to table of contents :arrow_up_small:

### Custom configuration for Dovecot diff --git a/docker-compose.sample.yml b/docker-compose.sample.yml index 2e28b794..8ec445d6 100644 --- a/docker-compose.sample.yml +++ b/docker-compose.sample.yml @@ -86,6 +86,8 @@ services: volumes: - ${VOLUMES_ROOT_PATH}/mail:/var/mail - ${VOLUMES_ROOT_PATH}/traefik/acme:/etc/letsencrypt/acme + # Uncomment the line below, when you want whitelist some IP Addresses or domains in Postfix (please check the 'Whitelist Hosts/IP Addresses In Postfix' in README.md for more info) + # - ${VOLUMES_ROOT_PATH}/postfix/rbl_override:/etc/postfix/rbl_override depends_on: - mariadb - redis diff --git a/rbl_override.sample b/rbl_override.sample new file mode 100644 index 00000000..5465454c --- /dev/null +++ b/rbl_override.sample @@ -0,0 +1 @@ +30.5.33.20 OK diff --git a/rootfs/etc/postfix/main.cf b/rootfs/etc/postfix/main.cf index ca23d7a1..beac1cda 100644 --- a/rootfs/etc/postfix/main.cf +++ b/rootfs/etc/postfix/main.cf @@ -197,6 +197,7 @@ smtpd_recipient_restrictions= reject_unknown_recipient_domain, reject_non_fqdn_recipient, reject_unlisted_recipient, + check_client_access hash:/etc/postfix/rbl_override, reject_rbl_client zen.spamhaus.org ## diff --git a/rootfs/usr/local/bin/setup.sh b/rootfs/usr/local/bin/setup.sh index 69243dad..f1cbea3e 100644 --- a/rootfs/usr/local/bin/setup.sh +++ b/rootfs/usr/local/bin/setup.sh @@ -309,6 +309,26 @@ if [ -f /var/mail/postfix/custom.conf ]; then echo "[INFO] Custom Postfix configuration file loaded" fi +# +# Initialize rbl_override configuration +# +if [ -d "/etc/postifx/rbl_override" ]; then + echo "[ERROR] The `/etc/postfix/rbl_override` is a folder instead of a file. Please fix it." + exit 1; +fi + +if [ ! -f "/etc/postfix/rbl_override" ]; then + touch /etc/postfix/rbl_override + echo "[INFO] Created default (empty) rbl_override file" +else + echo "[INFO] /etc/postfix/rbl_override file found" +fi + +if [ -f "/etc/postfix/rbl_override" ]; then + postmap /etc/postfix/rbl_override + echo "[INFO] RBL Override initialized" +fi + # DOVECOT TUNING # --------------------------------------------------------------------------------------------- From 639ec3b3fa145cf7cb4ce5abc40f212ca0331081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Sun, 19 Sep 2021 17:32:31 +0200 Subject: [PATCH 31/50] Update for bullseye debian-mail-overlay (#37) * fix traefik acme cert works correctly tests + fix Eicar-Test-Signature FOUND test * Update base image to mailserver2/debian-mail-overlay:1.0.5 * updated travis badge * Add new test container mailserver_sieve with DEBUG_MODE=true to test for sieve messages in log * Split tests into multiple bats-test-files. Add a target for each mailserver-test-container to Makefile. This allows to run for example only ldap tests with `make ldap`. Running only `make` will execute all tests. * Add a github actions workflow for each mailserver-test-container. The workflows will build the docker image and then run the tests. For example: the workflow `.github/workflows/default.yml` will run `make default` to run tests for the default container. These workflows run only for pull requests into master. * Delete .travis.yml Co-authored-by: Andrew Savinykh <658865+AndrewSav@users.noreply.github.com> --- .github/workflows/default.yml | 22 + .github/workflows/ecdsa.yml | 22 + .github/workflows/ldap.yml | 22 + .github/workflows/ldap2.yml | 22 + .github/workflows/reverse.yml | 22 + .github/workflows/sieve.yml | 22 + .github/workflows/traefik_acmev1.yml | 22 + .github/workflows/traefik_acmev2.yml | 22 + .travis.yml | 7 - Dockerfile | 2 +- Makefile | 375 +++-- README.md | 2 +- test/default.bats | 850 ++++++++++ test/ecdsa.bats | 41 + test/ldap.bats | 511 ++++++ test/ldap2.bats | 225 +++ test/reverse.bats | 575 +++++++ test/sieve.bats | 18 + test/tests.bats | 2172 -------------------------- test/traefik_acmev1.bats | 100 ++ test/traefik_acmev2.bats | 109 ++ 21 files changed, 2863 insertions(+), 2300 deletions(-) create mode 100644 .github/workflows/default.yml create mode 100644 .github/workflows/ecdsa.yml create mode 100644 .github/workflows/ldap.yml create mode 100644 .github/workflows/ldap2.yml create mode 100644 .github/workflows/reverse.yml create mode 100644 .github/workflows/sieve.yml create mode 100644 .github/workflows/traefik_acmev1.yml create mode 100644 .github/workflows/traefik_acmev2.yml delete mode 100644 .travis.yml create mode 100644 test/default.bats create mode 100644 test/ecdsa.bats create mode 100644 test/ldap.bats create mode 100644 test/ldap2.bats create mode 100644 test/reverse.bats create mode 100644 test/sieve.bats delete mode 100644 test/tests.bats create mode 100644 test/traefik_acmev1.bats create mode 100644 test/traefik_acmev2.bats diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml new file mode 100644 index 00000000..5c56a468 --- /dev/null +++ b/.github/workflows/default.yml @@ -0,0 +1,22 @@ +name: default tests + +on: + pull_request: + branches: master + +jobs: + # Run tests + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Build docker image + run: make build-no-cache + + - name: Run tests + run: make default diff --git a/.github/workflows/ecdsa.yml b/.github/workflows/ecdsa.yml new file mode 100644 index 00000000..231a730b --- /dev/null +++ b/.github/workflows/ecdsa.yml @@ -0,0 +1,22 @@ +name: ecdsa tests + +on: + pull_request: + branches: master + +jobs: + # Run tests + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Build docker image + run: make build-no-cache + + - name: Run tests + run: make ecdsa diff --git a/.github/workflows/ldap.yml b/.github/workflows/ldap.yml new file mode 100644 index 00000000..5162ae7b --- /dev/null +++ b/.github/workflows/ldap.yml @@ -0,0 +1,22 @@ +name: ldap tests + +on: + pull_request: + branches: master + +jobs: + # Run tests + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Build docker image + run: make build-no-cache + + - name: Run tests + run: make ldap diff --git a/.github/workflows/ldap2.yml b/.github/workflows/ldap2.yml new file mode 100644 index 00000000..5ffd807f --- /dev/null +++ b/.github/workflows/ldap2.yml @@ -0,0 +1,22 @@ +name: ldap2 tests + +on: + pull_request: + branches: master + +jobs: + # Run tests + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Build docker image + run: make build-no-cache + + - name: Run tests + run: make ldap2 diff --git a/.github/workflows/reverse.yml b/.github/workflows/reverse.yml new file mode 100644 index 00000000..5a7e045f --- /dev/null +++ b/.github/workflows/reverse.yml @@ -0,0 +1,22 @@ +name: reverse tests + +on: + pull_request: + branches: master + +jobs: + # Run tests + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Build docker image + run: make build-no-cache + + - name: Run tests + run: make reverse diff --git a/.github/workflows/sieve.yml b/.github/workflows/sieve.yml new file mode 100644 index 00000000..51c5e3b5 --- /dev/null +++ b/.github/workflows/sieve.yml @@ -0,0 +1,22 @@ +name: sieve tests + +on: + pull_request: + branches: master + +jobs: + # Run tests + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Build docker image + run: make build-no-cache + + - name: Run tests + run: make sieve diff --git a/.github/workflows/traefik_acmev1.yml b/.github/workflows/traefik_acmev1.yml new file mode 100644 index 00000000..0532d090 --- /dev/null +++ b/.github/workflows/traefik_acmev1.yml @@ -0,0 +1,22 @@ +name: traefik_acmev1 tests + +on: + pull_request: + branches: master + +jobs: + # Run tests + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Build docker image + run: make build-no-cache + + - name: Run tests + run: make traefik_acmev1 diff --git a/.github/workflows/traefik_acmev2.yml b/.github/workflows/traefik_acmev2.yml new file mode 100644 index 00000000..d7ea87b2 --- /dev/null +++ b/.github/workflows/traefik_acmev2.yml @@ -0,0 +1,22 @@ +name: traefik_acmev2 tests + +on: + pull_request: + branches: master + +jobs: + # Run tests + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Build docker image + run: make build-no-cache + + - name: Run tests + run: make traefik_acmev2 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ae2b208b..00000000 --- a/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -os: -- linux -language: shell -services: -- docker -script: -- make all diff --git a/Dockerfile b/Dockerfile index 33dfec9f..a5396bb4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.4 +FROM mailserver2/debian-mail-overlay:1.0.5 LABEL description="Simple and full-featured mail server using Docker" diff --git a/Makefile b/Makefile index c1aaff97..6aa2e7be 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,15 @@ NAME = mailserver2/mailserver:testing -all: build-no-cache init fixtures run clean -all-fast: build init fixtures run clean -no-build: init fixtures run clean +all: build-no-cache default reverse ldap ldap2 sieve ecdsa traefik_acmev1 traefik_acmev2 clean +no-build: default reverse ldap ldap2 sieve ecdsa traefik_acmev1 traefik_acmev2 clean +default: init_default fixtures_default run_default stop_default +reverse: init_reverse fixtures_reverse run_reverse stop_reverse +ldap: init_ldap fixtures_ldap run_ldap stop_ldap +ldap2: init_ldap2 fixtures_ldap2 run_ldap2 stop_ldap2 +sieve: init_sieve fixtures_sieve run_sieve stop_sieve +ecdsa: init_ecdsa run_ecdsa stop_ecdsa +traefik_acmev1: init_traefik_acmev1 run_traefik_acmev1 stop_traefik_acmev1 +traefik_acmev2: init_traefik_acmev2 run_traefik_acmev2 stop_traefik_acmev2 build-no-cache: docker build --no-cache -t $(NAME) . @@ -10,14 +17,31 @@ build-no-cache: build: docker build -t $(NAME) . -init: +init_openldap: -docker rm -f \ - mariadb postgres redis openldap \ - mailserver_default mailserver_reverse mailserver_ecdsa mailserver_ldap mailserver_ldap2 \ - mailserver_traefik_acmev1 mailserver_traefik_acmev2 || true + openldap || true + docker run \ + -d \ + --name openldap \ + -e LDAP_ORGANISATION="Test LDAP" \ + -e LDAP_DOMAIN="domain.tld" \ + -e LDAP_ADMIN_PASSWORD="testpasswd" \ + -e LDAP_TLS=false \ + -v "`pwd`/test/config/ldap/struct.ldif":/container/service/slapd/assets/config/bootstrap/ldif/custom/struct.ldif \ + -t osixia/openldap:1.4.0 --copy-service - sleep 2 +init_redis: + -docker rm -f \ + redis || true + docker run \ + -d \ + --name redis \ + -t redis:6.2-alpine + sleep 10 +init_mariadb: + -docker rm -f \ + mariadb || true docker run \ -d \ --name mariadb \ @@ -29,6 +53,9 @@ init: -v "`pwd`/test/config/mariadb/bind.cnf":/etc/mysql/conf.d/bind.cnf \ -t mysql:5.7 +init_postgres: + -docker rm -f \ + postgres || true docker run \ -d \ --name postgres \ @@ -36,84 +63,11 @@ init: -e POSTGRES_USER=postfix \ -e POSTGRES_PASSWORD=testpasswd \ -v "`pwd`/test/config/postgres":/docker-entrypoint-initdb.d \ - -t postgres:12-alpine - - docker run \ - -d \ - --name redis \ - -t redis:5.0-alpine - - docker run \ - -d \ - --name openldap \ - -e LDAP_ORGANISATION="Test LDAP" \ - -e LDAP_DOMAIN="domain.tld" \ - -e LDAP_ADMIN_PASSWORD="testpasswd" \ - -e LDAP_TLS=false \ - -v "`pwd`/test/config/ldap/struct.ldif":/container/service/slapd/assets/config/bootstrap/ldif/custom/struct.ldif \ - -t osixia/openldap:1.4.0 --copy-service - - sleep 10 - - docker run \ - -d \ - --name mailserver_default \ - --link mariadb:mariadb \ - --link redis:redis \ - -e DBPASS=testpasswd \ - -e RSPAMD_PASSWORD=testpasswd \ - -e VMAILUID=`id -u` \ - -e VMAILGID=`id -g` \ - -e ADD_DOMAINS=domain2.tld,domain3.tld \ - -e RECIPIENT_DELIMITER=: \ - -e TESTING=true \ - -v "`pwd`/test/share/tests":/tmp/tests \ - -v "`pwd`/test/share/ssl/rsa":/var/mail/ssl \ - -v "`pwd`/test/share/postfix/custom.conf":/var/mail/postfix/custom.conf \ - -v "`pwd`/test/share/postfix/sender_access":/var/mail/postfix/sender_access \ - -v "`pwd`/test/share/dovecot/conf.d":/var/mail/dovecot/conf.d \ - -v "`pwd`/test/share/clamav/unofficial-sigs/user.conf":/var/mail/clamav-unofficial-sigs/user.conf \ - -h mail.domain.tld \ - -t $(NAME) - - docker run \ - -d \ - --name mailserver_reverse \ - --link postgres:postgres \ - --link redis:redis \ - -e FQDN=mail.domain.tld \ - -e DOMAIN=domain.tld \ - -e DBDRIVER=pgsql \ - -e DBHOST=postgres \ - -e DBPORT=5432 \ - -e DBPASS=/tmp/passwd/postgres \ - -e REDIS_HOST=redis \ - -e REDIS_PORT=6379 \ - -e REDIS_PASS=/tmp/passwd/redis \ - -e RSPAMD_PASSWORD=/tmp/passwd/rspamd \ - -e VMAILUID=`id -u` \ - -e VMAILGID=`id -g` \ - -e VMAIL_SUBDIR=subdir \ - -e RELAY_NETWORKS="192.168.0.0/16 172.16.0.0/12 10.0.0.0/8" \ - -e DISABLE_CLAMAV=true \ - -e DISABLE_SIEVE=true \ - -e DISABLE_SIGNING=true \ - -e DISABLE_GREYLISTING=true \ - -e DISABLE_RATELIMITING=true \ - -e DISABLE_DNS_RESOLVER=true \ - -e ENABLE_POP3=true \ - -e ENABLE_ENCRYPTION=true \ - -e ENABLE_FETCHMAIL=true \ - -e DKIM_KEY_LENGTH=4096 \ - -e DKIM_SELECTOR="other" \ - -e TESTING=true \ - -v "`pwd`/test/share/tests":/tmp/tests \ - -v "`pwd`/test/share/passwd":/tmp/passwd \ - -v "`pwd`/test/share/ssl/rsa":/var/mail/ssl \ - -v "`pwd`/test/share/sieve/custom.sieve":/var/mail/sieve/custom.sieve \ - -v "`pwd`/test/share/letsencrypt":/etc/letsencrypt \ - -t $(NAME) + -t postgres:13-alpine +init_ldap: init_openldap init_redis + -docker rm -f \ + mailserver_ldap || true docker run \ -d \ --name mailserver_ldap \ @@ -161,6 +115,36 @@ init: -h mail.domain.tld \ -t $(NAME) +fixtures_ldap: + # Wait for clamav unofficial sigs database update (ldap) + docker exec mailserver_ldap /bin/sh -c "while [ -f /var/lib/clamav-unofficial-sigs/pid/clamav-unofficial-sigs.pid ] ; do sleep 1 ; done" + # Wait for clamav load databases (ldap) + docker exec mailserver_ldap /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 3310 ; do sleep 1 ; done" + # Wait for rspamd to start (ldap) + docker exec mailserver_ldap /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 11332 ; do sleep 1 ; done" + + docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-user.txt" + docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-user-spam-learning.txt" + docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-valid-user-subaddress.txt" + docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-non-existing-user.txt" + docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias.txt" + docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias-forward.txt" + docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias-group.txt" + docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-spam-to-existing-user.txt" + docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-virus-to-existing-user.txt" + docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/email-templates/internal-user-to-existing-user.txt" + docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/email-templates/internal-rejected-user-to-existing-user.txt" + sleep 2 + docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/sieve/trigger-spam-ham-learning.txt" +run_ldap: + ./test/bats/bin/bats test/ldap.bats +stop_ldap: + -docker rm -f \ + mailserver_ldap || true + +init_ldap2: init_openldap init_redis + -docker rm -f \ + mailserver_ldap2 || true docker run \ -d \ --name mailserver_ldap2 \ @@ -211,7 +195,110 @@ init: -v "`pwd`/test/share/clamav/unofficial-sigs/user.conf":/var/mail/clamav-unofficial-sigs/user.conf \ -h mail.domain.tld \ -t $(NAME) +fixtures_ldap2: + sleep 20 + docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-user.txt" + docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-valid-user-subaddress.txt" + docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-non-existing-user.txt" + docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias.txt" + docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias-forward.txt" + docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias-group.txt" + docker exec mailserver_ldap2 /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/email-templates/internal-user-to-existing-user.txt" + sleep 10 +run_ldap2: + ./test/bats/bin/bats test/ldap2.bats +stop_ldap2: + -docker rm -f \ + mailserver_ldap2 || true + +init_default: init_redis init_mariadb + -docker rm -f \ + mailserver_default || true + + sleep 60 + docker run \ + -d \ + --name mailserver_default \ + --link mariadb:mariadb \ + --link redis:redis \ + -e DBPASS=testpasswd \ + -e RSPAMD_PASSWORD=testpasswd \ + -e VMAILUID=`id -u` \ + -e VMAILGID=`id -g` \ + -e ADD_DOMAINS=domain2.tld,domain3.tld \ + -e RECIPIENT_DELIMITER=: \ + -e TESTING=true \ + -v "`pwd`/test/share/tests":/tmp/tests \ + -v "`pwd`/test/share/ssl/rsa":/var/mail/ssl \ + -v "`pwd`/test/share/postfix/custom.conf":/var/mail/postfix/custom.conf \ + -v "`pwd`/test/share/postfix/sender_access":/var/mail/postfix/sender_access \ + -v "`pwd`/test/share/dovecot/conf.d":/var/mail/dovecot/conf.d \ + -v "`pwd`/test/share/clamav/unofficial-sigs/user.conf":/var/mail/clamav-unofficial-sigs/user.conf \ + -h mail.domain.tld \ + -t $(NAME) + +init_reverse: init_redis init_postgres + -docker rm -f \ + mailserver_reverse || true + sleep 10 + docker run \ + -d \ + --name mailserver_reverse \ + --link postgres:postgres \ + --link redis:redis \ + -e FQDN=mail.domain.tld \ + -e DOMAIN=domain.tld \ + -e DBDRIVER=pgsql \ + -e DBHOST=postgres \ + -e DBPORT=5432 \ + -e DBPASS=/tmp/passwd/postgres \ + -e REDIS_HOST=redis \ + -e REDIS_PORT=6379 \ + -e REDIS_PASS=/tmp/passwd/redis \ + -e RSPAMD_PASSWORD=/tmp/passwd/rspamd \ + -e VMAILUID=`id -u` \ + -e VMAILGID=`id -g` \ + -e VMAIL_SUBDIR=subdir \ + -e RELAY_NETWORKS="192.168.0.0/16 172.16.0.0/12 10.0.0.0/8" \ + -e DISABLE_CLAMAV=true \ + -e DISABLE_SIEVE=true \ + -e DISABLE_SIGNING=true \ + -e DISABLE_GREYLISTING=true \ + -e DISABLE_RATELIMITING=true \ + -e DISABLE_DNS_RESOLVER=true \ + -e ENABLE_POP3=true \ + -e ENABLE_ENCRYPTION=true \ + -e ENABLE_FETCHMAIL=true \ + -e DKIM_KEY_LENGTH=4096 \ + -e DKIM_SELECTOR="other" \ + -e TESTING=true \ + -v "`pwd`/test/share/tests":/tmp/tests \ + -v "`pwd`/test/share/passwd":/tmp/passwd \ + -v "`pwd`/test/share/ssl/rsa":/var/mail/ssl \ + -v "`pwd`/test/share/sieve/custom.sieve":/var/mail/sieve/custom.sieve \ + -v "`pwd`/test/share/letsencrypt":/etc/letsencrypt \ + -t $(NAME) +fixtures_reverse: + sleep 30 + docker exec mailserver_reverse /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-user.txt" + docker exec mailserver_reverse /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-valid-user-subaddress-with-default-separator.txt" + docker exec mailserver_reverse /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-non-existing-user.txt" + docker exec mailserver_reverse /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias.txt" + docker exec mailserver_reverse /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-spam-to-existing-user.txt" + docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/email-templates/internal-user-to-existing-user.txt" + # Wait until all mails have been processed + sleep 10 +run_reverse: + ./test/bats/bin/bats test/reverse.bats +stop_reverse: + -docker rm -f \ + mailserver_reverse || true + +init_ecdsa: init_redis init_mariadb + -docker rm -f \ + mailserver_ecdsa || true + sleep 10 docker run \ -d \ --name mailserver_ecdsa \ @@ -229,7 +316,16 @@ init: -v "`pwd`/test/share/postfix/custom.ecdsa.conf":/var/mail/postfix/custom.conf \ -h mail.domain.tld \ -t $(NAME) + sleep 10 +run_ecdsa: + ./test/bats/bin/bats test/ecdsa.bats +stop_ecdsa: + -docker rm -f \ + mailserver_ecdsa || true +init_traefik_acmev1: init_redis init_mariadb + -docker rm -f \ + mailserver_traefik_acmev1 || true docker run \ -d \ --name mailserver_traefik_acmev1 \ @@ -245,7 +341,16 @@ init: -v "`pwd`/test/share/traefik/acme.v1.json":/etc/letsencrypt/acme/acme.json \ -h mail.domain.tld \ -t $(NAME) +run_traefik_acmev1: + sleep 20 + ./test/bats/bin/bats test/traefik_acmev1.bats +stop_traefik_acmev1: + -docker rm -f \ + mailserver_traefik_acmev1 || true +init_traefik_acmev2: init_redis init_mariadb + -docker rm -f \ + mailserver_traefik_acmev2 || true docker run \ -d \ --name mailserver_traefik_acmev2 \ @@ -261,18 +366,21 @@ init: -v "`pwd`/test/share/traefik/acme.v2.json":/etc/letsencrypt/acme/acme.json \ -h mail.domain.tld \ -t $(NAME) +run_traefik_acmev2: + sleep 20 + ./test/bats/bin/bats test/traefik_acmev2.bats +stop_traefik_acmev2: + -docker rm -f \ + mailserver_traefik_acmev2 || true -fixtures: +fixtures_default: # Wait for clamav unofficial sigs database update (default) docker exec mailserver_default /bin/sh -c "while [ -f /var/lib/clamav-unofficial-sigs/pid/clamav-unofficial-sigs.pid ] ; do sleep 1 ; done" # Wait for clamav load databases (default) docker exec mailserver_default /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 3310 ; do sleep 1 ; done" - - # Wait for clamav unofficial sigs database update (ldap) - docker exec mailserver_ldap /bin/sh -c "while [ -f /var/lib/clamav-unofficial-sigs/pid/clamav-unofficial-sigs.pid ] ; do sleep 1 ; done" - # Wait for clamav load databases (ldap) - docker exec mailserver_ldap /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 3310 ; do sleep 1 ; done" + # Wait for rspamd to start (default) + docker exec mailserver_default /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 11332 ; do sleep 1 ; done" docker exec mailserver_default /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-user.txt" docker exec mailserver_default /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-user-spam-learning.txt" @@ -283,43 +391,72 @@ fixtures: docker exec mailserver_default /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-virus-to-existing-user.txt" docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/email-templates/internal-user-to-existing-user.txt" docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/email-templates/internal-rejected-user-to-existing-user.txt" + sleep 2 + docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/sieve/trigger-spam-ham-learning.txt" + # Wait until all mails have been processed + sleep 10 +run_default: + ./test/bats/bin/bats test/default.bats +stop_default: + -docker rm -f \ + mailserver_default || true - docker exec mailserver_reverse /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-user.txt" - docker exec mailserver_reverse /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-valid-user-subaddress-with-default-separator.txt" - docker exec mailserver_reverse /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-non-existing-user.txt" - docker exec mailserver_reverse /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias.txt" - docker exec mailserver_reverse /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-spam-to-existing-user.txt" - docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/email-templates/internal-user-to-existing-user.txt" +init_sieve: init_redis init_mariadb + -docker rm -f \ + mailserver_sieve || true + docker run \ + -d \ + --name mailserver_sieve \ + --link mariadb:mariadb \ + --link redis:redis \ + -e DBHOST=mariadb \ + -e DBPASS=testpasswd \ + -e RSPAMD_PASSWORD=testpasswd \ + -e VMAILUID=`id -u` \ + -e VMAILGID=`id -g` \ + -e ADD_DOMAINS=domain2.tld,domain3.tld \ + -e RECIPIENT_DELIMITER=: \ + -e TESTING=true \ + -e DEBUG_MODE=true \ + -v "`pwd`/test/share/tests":/tmp/tests \ + -v "`pwd`/test/share/ssl/rsa":/var/mail/ssl \ + -v "`pwd`/test/share/postfix/custom.conf":/var/mail/postfix/custom.conf \ + -v "`pwd`/test/share/postfix/sender_access":/var/mail/postfix/sender_access \ + -v "`pwd`/test/share/dovecot/conf.d":/var/mail/dovecot/conf.d \ + -v "`pwd`/test/share/clamav/unofficial-sigs/user.conf":/var/mail/clamav-unofficial-sigs/user.conf \ + -h mail.domain.tld \ + -t $(NAME) - docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-user.txt" - docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-user-spam-learning.txt" - docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-valid-user-subaddress.txt" - docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-non-existing-user.txt" - docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias.txt" - docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias-forward.txt" - docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias-group.txt" - docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-spam-to-existing-user.txt" - docker exec mailserver_ldap /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-virus-to-existing-user.txt" - docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/email-templates/internal-user-to-existing-user.txt" - docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/email-templates/internal-rejected-user-to-existing-user.txt" +fixtures_sieve: + # Wait for clamav unofficial sigs database update (sieve) + docker exec mailserver_sieve /bin/sh -c "while [ -f /var/lib/clamav-unofficial-sigs/pid/clamav-unofficial-sigs.pid ] ; do sleep 1 ; done" + # Wait for clamav load databases (sieve) + docker exec mailserver_sieve /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 3310 ; do sleep 1 ; done" + # Wait for rspamd to start (sieve) + docker exec mailserver_sieve /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 11332 ; do sleep 1 ; done" - docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-user.txt" - docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-valid-user-subaddress.txt" - docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-non-existing-user.txt" - docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias.txt" - docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias-forward.txt" - docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias-group.txt" - docker exec mailserver_ldap2 /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/email-templates/internal-user-to-existing-user.txt" + docker exec mailserver_sieve /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-user.txt" + docker exec mailserver_sieve /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-user-spam-learning.txt" + docker exec mailserver_sieve /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-valid-user-subaddress.txt" + docker exec mailserver_sieve /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-non-existing-user.txt" + docker exec mailserver_sieve /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-alias.txt" + docker exec mailserver_sieve /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-spam-to-existing-user.txt" + docker exec mailserver_sieve /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-virus-to-existing-user.txt" + docker exec mailserver_sieve /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/email-templates/internal-user-to-existing-user.txt" + docker exec mailserver_sieve /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/email-templates/internal-rejected-user-to-existing-user.txt" sleep 2 - docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/sieve/trigger-spam-ham-learning.txt" - docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/sieve/trigger-spam-ham-learning.txt" + docker exec mailserver_sieve /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/sieve/trigger-spam-ham-learning.txt" # Wait until all mails have been processed sleep 10 -run: - ./test/bats/bin/bats test/tests.bats +run_sieve: + ./test/bats/bin/bats test/sieve.bats + +stop_sieve: + -docker rm -f \ + mailserver_sieve || true clean: docker images --quiet --filter=dangling=true | xargs --no-run-if-empty docker rmi diff --git a/README.md b/README.md index 44ff864f..43530ada 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Thank you very much. ### Build -[![Build Status](https://travis-ci.org/mailserver2/mailserver.svg?branch=master)](https://travis-ci.org/mailserver2/mailserver) +[![Build Status](https://travis-ci.com/mailserver2/mailserver.svg?branch=master)](https://travis-ci.com/mailserver2/mailserver) ### Docker image diff --git a/test/default.bats b/test/default.bats new file mode 100644 index 00000000..0a4d86ac --- /dev/null +++ b/test/default.bats @@ -0,0 +1,850 @@ +load 'test_helper/bats-support/load' +load 'test_helper/bats-assert/load' + +# +# system +# + +@test "checking system: /etc/mailname (docker method) (default)" { + run docker exec mailserver_default cat /etc/mailname + assert_success + assert_output "mail.domain.tld" +} + +@test "checking system: /etc/hostname" { + run docker exec mailserver_default cat /etc/hostname + assert_success + assert_output "mail.domain.tld" +} + +@test "checking system: /etc/hosts" { + run docker exec mailserver_default grep "mail.domain.tld" /etc/hosts + assert_success +} + +@test "checking system: fqdn" { + run docker exec mailserver_default hostname -f + assert_success + assert_output "mail.domain.tld" +} + +@test "checking system: domain" { + run docker exec mailserver_default hostname -d + assert_success + assert_output "domain.tld" +} + +@test "checking system: hostname" { + run docker exec mailserver_default hostname -s + assert_success + assert_output "mail" +} + +@test "checking system: all environment variables have been replaced (default configuration)" { + run docker exec mailserver_default /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd /etc/cron.d /etc/mailname /usr/local/bin" + assert_failure +} + +# +# processes (default configuration) +# + +@test "checking process: s6 (default configuration)" { + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-svscan /services'" + assert_success +} + +@test "checking process: rsyslog (default configuration)" { + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise rsyslogd'" + assert_success + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[r]syslogd -n -f /etc/rsyslog/rsyslog.conf'" + assert_success +} + +@test "checking process: cron (default configuration)" { + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise cron'" + assert_success + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[c]ron -f'" + assert_success +} + +@test "checking process: postfix (default configuration)" { + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise postfix'" + assert_success + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[/]usr/lib/postfix/sbin/master -s'" + assert_success +} + +@test "checking process: dovecot (default configuration)" { + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise dovecot'" + assert_success + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[/]usr/sbin/dovecot -F'" + assert_success +} + +@test "checking process: rspamd (default configuration)" { + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise rspamd'" + assert_success + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[r]spamd: main process'" + assert_success + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[r]spamd: rspamd_proxy process'" + assert_success + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[r]spamd: controller process'" + assert_success +} + +@test "checking process: clamd (default configuration)" { + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise clamd'" + assert_success + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[c]lamd'" + assert_success +} + +@test "checking process: freshclam (default configuration)" { + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise freshclam'" + assert_success + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[f]reshclam -d'" + assert_success +} + +@test "checking process: unbound (default configuration)" { + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise unbound'" + assert_success + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[u]nbound'" + assert_success +} + +@test "checking process: cert_watcher (default configuration)" { + run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise cert_watcher'" + assert_success +} + +# +# processes restarting +# + +@test "checking process: 10 cron tasks to reset all the process counters" { + run docker exec mailserver_default /bin/bash -c "cat /etc/cron.d/counters | wc -l" + assert_success + assert_output 10 +} + +@test "checking process: no service restarted (default configuration)" { + run docker exec mailserver_default cat /tmp/counters/_parent + assert_success + assert_output 0 + run docker exec mailserver_default cat /tmp/counters/clamd + assert_success + assert_output 0 + run docker exec mailserver_default cat /tmp/counters/cron + assert_success + assert_output 0 + run docker exec mailserver_default cat /tmp/counters/dovecot + assert_success + assert_output 0 + run docker exec mailserver_default cat /tmp/counters/freshclam + assert_success + assert_output 0 + run docker exec mailserver_default cat /tmp/counters/postfix + assert_success + assert_output 0 + run docker exec mailserver_default cat /tmp/counters/rspamd + assert_success + assert_output 0 + run docker exec mailserver_default cat /tmp/counters/rsyslogd + assert_success + assert_output 0 + run docker exec mailserver_default cat /tmp/counters/unbound + assert_success + assert_output 0 + run docker exec mailserver_default cat /tmp/counters/cert_watcher + assert_success + assert_output 0 +} + +# +# ports +# + +@test "checking port (25): external port listening (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 25" + assert_success +} + +@test "checking port (53): internal port listening (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 127.0.0.1 53" + assert_success +} + +@test "checking port (110): external port closed (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 110" + assert_failure +} + +@test "checking port (143): external port listening (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 143" + assert_success +} + +@test "checking port (465): external port listening (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 465" + assert_success +} + +@test "checking port (587): external port listening (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 587" + assert_success +} + +@test "checking port (993): external port listening (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 993" + assert_success +} + +@test "checking port (995): external port closed (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 995" + assert_failure +} + +@test "checking port (3310): external port listening (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 3310" + assert_success +} + +@test "checking port (4190): external port listening (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 4190" + assert_success +} + +@test "checking port (8953): internal port listening (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 127.0.0.1 8953" + assert_success +} + +@test "checking port (10025): internal port closed (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 127.0.0.1 10025" + assert_failure +} + +@test "checking port (10026): internal port listening (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 127.0.0.1 10026" + assert_success +} + +@test "checking port (11332): external port listening (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 11332" + assert_success +} + +@test "checking port (11334): external port listening (default configuration)" { + run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 11334" + assert_success +} + +# +# sasl +# + +@test "checking sasl: dovecot auth with good password (default configuration)" { + run docker exec mailserver_default /bin/sh -c "doveadm auth test sarah.connor@domain.tld testpasswd12 | grep 'auth succeeded'" + assert_success +} + +@test "checking sasl: dovecot auth with bad password (default configuration)" { + run docker exec mailserver_default /bin/sh -c "doveadm auth test sarah.connor@domain.tld badpassword | grep 'auth failed'" + assert_success +} + +# +# smtp +# http://www.postfix.org/SASL_README.html#server_test +# + +# Base64 AUTH STRINGS +# AHNhcmFoLmNvbm5vckBkb21haW4udGxkAHRlc3RwYXNzd2QxMg== +# echo -ne '\000sarah.connor@domain.tld\000testpasswd12' | openssl base64 +# AHNhcmFoLmNvbm5vckBkb21haW4udGxkAGJhZHBhc3N3b3Jk +# echo -ne '\000sarah.connor@domain.tld\000badpassword' | openssl base64 +# c2FyYWguY29ubm9yQGRvbWFpbi50bGQ= +# echo -ne 'sarah.connor@domain.tld' | openssl base64 +# dGVzdHBhc3N3ZDEy +# echo -ne 'testpasswd12' | openssl base64 +# YmFkcGFzc3dvcmQ= +# echo -ne 'badpassword' | openssl base64 + +@test "checking smtp (25): STARTTLS AUTH PLAIN works with good password (default configuration)" { + run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:25 -starttls smtp < /tmp/tests/auth/smtp-auth-plain.txt 2>&1 | grep -i 'authentication successful'" + assert_success +} + +@test "checking smtp (25): STARTTLS AUTH PLAIN fails with bad password" { + run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:25 -starttls smtp < /tmp/tests/auth/smtp-auth-plain-wrong.txt 2>&1 | grep -i 'authentication failed'" + assert_success +} + +@test "checking smtp (25): clear auth disabled" { + run docker exec mailserver_default /bin/sh -c "nc -w 2 0.0.0.0 25 < /tmp/tests/auth/smtp-auth-plain.txt | grep -i 'authentication not enabled'" + assert_success +} + +@test "checking submission (587): STARTTLS AUTH LOGIN works with good password (default configuration)" { + run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/auth/smtp-auth-login.txt 2>&1 | grep -i 'authentication successful'" + assert_success +} + +@test "checking submission (587): STARTTLS AUTH LOGIN fails with bad password" { + run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/auth/smtp-auth-login-wrong.txt 2>&1 | grep -i 'authentication failed'" + assert_success +} + +@test "checking submission (587): Auth without STARTTLS fail" { + run docker exec mailserver_default /bin/sh -c "nc -w 2 0.0.0.0 587 < /tmp/tests/auth/smtp-auth-plain.txt | grep -i 'Must issue a STARTTLS command first'" + assert_success +} + +@test "checking smtps (465): SSL/TLS AUTH LOGIN works with good password (default configuration)" { + run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:465 < /tmp/tests/auth/smtp-auth-login.txt 2>&1 | grep -i 'authentication successful'" + assert_success +} + +@test "checking smtps (465): SSL/TLS AUTH LOGIN fails with bad password" { + run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:465 < /tmp/tests/auth/smtp-auth-login-wrong.txt 2>&1 | grep -i 'authentication failed'" + assert_success +} + +@test "checking smtp: john.doe should have received 4 mails (internal + external + subaddress + hostmaster alias) (default configuration)" { + run docker exec mailserver_default /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/john.doe/mail/new/ | wc -l" + assert_success + assert_output 4 +} + +@test "checking smtp: sarah.connor should have received 1 mail (internal spam-ham test) (default configuration)" { + run docker exec mailserver_default /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/sarah.connor/mail/new/ | wc -l" + assert_success + assert_output 1 +} + +@test "checking smtp: sarah.connor should have received 1 spam (with manual IMAP COPY to Spam folder) (default configuration)" { + run docker exec mailserver_default /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/sarah.connor/mail/.Spam/cur/ | wc -l" + assert_success + assert_output 1 +} + +@test "checking smtp: rejects mail to unknown user (default configuration)" { + run docker exec mailserver_default /bin/sh -c "grep ': Recipient address rejected: User unknown in virtual mailbox table' /var/log/mail.log | wc -l" + assert_success + assert_output 1 +} + +@test "checking smtp: delivers mail to existing alias (default configuration)" { + run docker exec mailserver_default /bin/sh -c "grep 'to=, orig_to=' /var/log/mail.log | grep 'status=sent' | wc -l" + assert_success + assert_output 1 +} + +# +# imap +# + +@test "checking imap (143): STARTTLS login works with good password (default configuration)" { + run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:143 -starttls imap < /tmp/tests/auth/imap-auth.txt 2>&1 | grep -i 'logged in'" + assert_success +} + +@test "checking imap (143): STARTTLS login fails with bad password" { + run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:143 -starttls imap < /tmp/tests/auth/imap-auth-wrong.txt 2>&1 | grep -i 'authentication failed'" + assert_success +} + +@test "checking imaps (993): SSL/TLS login works with good password (default configuration)" { + run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/auth/imap-auth.txt 2>&1 | grep -i 'logged in'" + assert_success +} + +@test "checking imaps (993): SSL/TLS login fails with bad password" { + run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/auth/imap-auth-wrong.txt 2>&1 | grep -i 'authentication failed'" + assert_success +} + +# rspamd + +@test "checking rspamd: spam filtered (default configuration)" { + run docker exec mailserver_default /bin/sh -c "grep -i 'Gtube pattern; from= to= ' /var/log/mail.log | wc -l" + assert_success + assert_output 1 +} + +@test "checking rspamd: existing rrd file" { + run docker exec mailserver_default [ -f /var/mail/rspamd/rspamd.rrd ] + assert_success +} + +@test "checking rspamd: 7 messages scanned" { + run docker exec mailserver_default /bin/sh -c "rspamc stat | grep -i 'Messages scanned: 7'" + assert_success +} + +@test "checking rspamd: 5 messages with action no action" { + run docker exec mailserver_default /bin/sh -c "rspamc stat | grep -i 'Messages with action no action: 5'" + assert_success +} + +@test "checking rspamd: 2 messages with action reject" { + run docker exec mailserver_default /bin/sh -c "rspamc stat | grep -i 'Messages with action reject: 2'" + assert_success +} + +@test "checking rspamd: 2 messages learned" { + run docker exec mailserver_default /bin/sh -c "rspamc stat | grep -i 'Messages learned: 2'" + assert_success +} + +@test "checking rspamd: 1 address whitelisted in default configuration" { + run docker exec mailserver_default /bin/bash -c "grep 'postmaster@domain.tld' /etc/rspamd/local.d/settings.conf | wc -l" + assert_success + assert_output 1 +} + +@test "checking rspamd: debug mode disabled (default configuration)" { + run docker exec mailserver_default /bin/sh -c 'rspamadm configdump | grep -E "level = \"warning\";"' + assert_success +} + +# +# accounts +# + +@test "checking accounts: user accounts (default configuration)" { + run docker exec mailserver_default doveadm user '*' + assert_success + [ "${lines[0]}" = "john.doe@domain.tld" ] + [ "${lines[1]}" = "sarah.connor@domain.tld" ] +} + +@test "checking accounts: user quotas (default configuration)" { + run docker exec mailserver_default /bin/bash -c "doveadm quota get -A 2>&1 | grep '1000' | wc -l" + assert_success + assert_output 2 +} + +@test "checking accounts: user mail folders for john.doe" { + run docker exec mailserver_default /bin/bash -c "ls -A /var/mail/vhosts/domain.tld/john.doe/mail/ | grep -E 'cur|new|tmp' | wc -l" + assert_success + assert_output 3 +} + +@test "checking accounts: user mail folders for sarah.connor" { + run docker exec mailserver_default /bin/bash -c "ls -A /var/mail/vhosts/domain.tld/sarah.connor/mail/ | grep -E '.Spam|cur|new|subscriptions|tmp' | wc -l" + assert_success + assert_output 5 +} + +# +# dkim +# + +@test "checking dkim: all key pairs are generated (default configuration)" { + run docker exec mailserver_default /bin/bash -c "ls -A /var/mail/dkim/*/mail.{private.key,public.key} | wc -l" + assert_success + assert_output 6 +} + +# postfix +# + +@test "checking postfix: mynetworks value (default configuration)" { + run docker exec mailserver_default /bin/sh -c "postconf -h mynetworks" + assert_success + assert_output "127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128" +} + +@test "checking postfix: main.cf overrides" { + run docker exec mailserver_default /bin/sh -c "postconf -h max_idle" + assert_success + assert_output "600s" + + run docker exec mailserver_default /bin/sh -c "postconf -h readme_directory" + assert_success + assert_output "/tmp" +} + +@test "checking postfix: headers cleanup" { + run docker exec mailserver_default /bin/sh -c "grep -i 'replace: header Received' /var/log/mail.log | wc -l" + assert_success + assert_output 1 +} + +@test "checking postfix: myorigin value (docker method)" { + run docker exec mailserver_default postconf -h myorigin + assert_success + assert_output "mail.domain.tld" +} + +@test "checking postfix: two milter rejects (GTUBE + EICAR)" { + run docker exec mailserver_default /bin/sh -c "grep -i 'milter-reject' /var/log/mail.log | wc -l" + assert_success + assert_output 2 +} + +@test "checking postfix: milter-reject - clamav virus found" { + run docker exec mailserver_default grep -i 'milter-reject.*virus found: ".*EICAR.*"; from=' /var/log/mail.log + assert_success +} + +@test "checking postfix: check 'etc' files in queue directory" { + run docker exec mailserver_default [ -f /var/mail/postfix/spool/etc/services ] + assert_success + run docker exec mailserver_default [ -f /var/mail/postfix/spool/etc/hosts ] + assert_success + run docker exec mailserver_default [ -f /var/mail/postfix/spool/etc/localtime ] + assert_success +} + +@test "checking postfix: check some folders in queue directory" { + run docker exec mailserver_default [ -d /var/mail/postfix/spool/usr/lib/sasl2 ] + assert_success + run docker exec mailserver_default [ -d /var/mail/postfix/spool/usr/lib/zoneinfo ] + assert_success +} + +@test "checking postfix: check dovecot unix sockets in queue directory" { + run docker exec mailserver_default [ -S /var/mail/postfix/spool/private/dovecot-lmtp ] + assert_success + run docker exec mailserver_default [ -S /var/mail/postfix/spool/private/auth ] + assert_success +} + +@test "checking postfix: check group of 'public' and 'maildrop' folders in queue directory" { + run docker exec mailserver_default /bin/sh -c "stat -c '%G' /var/mail/postfix/spool/public" + assert_success + assert_output "postdrop" + run docker exec mailserver_default /bin/sh -c "stat -c '%G' /var/mail/postfix/spool/maildrop" + assert_success + assert_output "postdrop" +} + +@test "checking postfix: smtp_tls_security_level value (default configuration)" { + run docker exec mailserver_default postconf -h smtp_tls_security_level + assert_success + assert_output "dane" +} + +@test "checking postfix: smtp_dns_support_level value (default configuration)" { + run docker exec mailserver_default postconf -h smtp_dns_support_level + assert_success + assert_output "dnssec" +} + +@test "checking postfix: smtpd_sender_login mysql maps (default configuration)" { + run docker exec mailserver_default /bin/sh -c "postconf -h smtpd_sender_login_maps | grep 'mysql'" + assert_success +} + +@test "checking postfix: verbose mode disabled (default configuration)" { + run docker exec mailserver_default /bin/sh -c "grep 'smtpd -v' /etc/postfix/master.cf | wc -l" + assert_success + assert_output 0 +} + +@test "checking postfix: master.cf custom service parameter" { + run docker exec mailserver_default postconf -P submission/inet/syslog_name + assert_success + assert_output "submission/inet/syslog_name = postfix/submission-custom" +} + +@test "checking postfix: sender access reject john.doe" { + run docker exec mailserver_default grep -i ': Sender address rejected: Access denied' /var/log/mail.log + assert_success +} + +# +# dovecot +# + +@test "checking dovecot: existing instances file" { + run docker exec mailserver_default [ -f /var/mail/dovecot/instances ] + assert_success +} + +@test "checking dovecot: default lib directory is a symlink" { + run docker exec mailserver_default [ -L /var/lib/dovecot ] + assert_success +} + +@test "checking dovecot: password scheme is correct" { + run docker exec mailserver_default /bin/sh -c "grep 'SHA512-CRYPT' /etc/dovecot/dovecot-sql.conf.ext | wc -l" + assert_success + assert_output 1 +} + +@test "checking dovecot: login_greeting value (default configuration)" { + run docker exec mailserver_default /bin/sh -c "doveconf -h login_greeting 2>/dev/null" + assert_success + assert_output "Do. Or do not. There is no try." +} + +@test "checking dovecot: mail_max_userip_connections imap value" { + run docker exec mailserver_default /bin/sh -c "doveconf -h -f protocol=imap mail_max_userip_connections 2>/dev/null" + assert_success + assert_output "100" +} + +@test "checking dovecot: mail_max_userip_connections pop3 value" { + run docker exec mailserver_default /bin/sh -c "doveconf -h -f protocol=pop3 mail_max_userip_connections 2>/dev/null" + assert_success + assert_output "50" +} + +@test "checking dovecot: quota dict mysql (default configuration)" { + run docker exec mailserver_default /bin/sh -c "doveconf dict sqlquota 2>/dev/null | grep 'mysql'" + assert_success +} + +@test "checking dovecot: debug mode disabled (default configuration)" { + run docker exec mailserver_default /bin/sh -c "doveconf -h auth_verbose 2>/dev/null" + assert_success + assert_output "no" + run docker exec mailserver_default /bin/sh -c "doveconf -h auth_verbose_passwords 2>/dev/null" + assert_success + assert_output "no" + run docker exec mailserver_default /bin/sh -c "doveconf -h auth_debug 2>/dev/null" + assert_success + assert_output "no" + run docker exec mailserver_default /bin/sh -c "doveconf -h auth_debug_passwords 2>/dev/null" + assert_success + assert_output "no" + run docker exec mailserver_default /bin/sh -c "doveconf -h mail_debug 2>/dev/null" + assert_success + assert_output "no" + run docker exec mailserver_default /bin/sh -c "doveconf -h verbose_ssl 2>/dev/null" + assert_success + assert_output "no" +} + +# +# clamav +# + +@test "checking clamav: TCP Bound to 3310 port" { + run docker exec mailserver_default grep -i 'TCP: Bound to \[0.0.0.0\]:3310' /var/log/mail.log + assert_success +} + +@test "checking clamav: self checking every 3600 seconds" { + run docker exec mailserver_default grep -i 'clamd\[.*\]: Self checking every 3600 seconds' /var/log/mail.log + assert_success +} + +@test "checking clamav: default lib directory is a symlink" { + run docker exec mailserver_default [ -L /var/lib/clamav ] + assert_success +} + +@test "checking clamav: Eicar-Test-Signature FOUND" { + run docker exec mailserver_default grep -i 'EICAR.*FOUND' /var/log/mail.log + assert_success +} + +@test "checking clamav: 6 database mirrors" { + run docker exec mailserver_default /bin/sh -c "grep 'DatabaseMirror' /etc/clamav/freshclam.conf | wc -l" + assert_success + assert_output 6 +} + +# +# clamav-unofficial-sigs +# + +@test "checking clamav-unofficial-sigs: rsync command exist" { + run docker exec mailserver_default /bin/sh -c "command -v rsync" + assert_success + assert_output "/usr/bin/rsync" +} + +@test "checking clamav-unofficial-sigs: curl command exist" { + run docker exec mailserver_default /bin/sh -c "command -v curl" + assert_success + assert_output "/usr/bin/curl" +} + +@test "checking clamav-unofficial-sigs: clamscan command exist" { + run docker exec mailserver_default /bin/sh -c "command -v clamscan" + assert_success + assert_output "/usr/bin/clamscan" +} + +@test "checking clamav-unofficial-sigs: cron task exist" { + run docker exec mailserver_default [ -f /etc/cron.d/clamav-unofficial-sigs ] + assert_success +} + +@test "checking clamav-unofficial-sigs: logrotate task exist" { + run docker exec mailserver_default [ -f /etc/logrotate.d/clamav-unofficial-sigs ] + assert_success +} + +# @test "checking clamav-unofficial-sigs: TEST 1 — Html.Sanesecurity.TestSig_Type3_Bdy" { +# run docker exec mailserver_default /bin/sh -c "clamscan --database=/var/lib/clamav/phish.ndb - < /tmp/tests/clamav/test1.eml" +# assert_failure +# assert_output --partial "Sanesecurity.TestSig_Type3_Bdy.4.UNOFFICIAL FOUND" +# } + +# @test "checking clamav-unofficial-sigs: TEST 2 — Email.Sanesecurity.TestSig_Type4_Hdr" { +# run docker exec mailserver_default /bin/sh -c "clamscan --database=/var/lib/clamav/phish.ndb - < /tmp/tests/clamav/test2.eml" +# assert_failure +# assert_output --partial "Sanesecurity.TestSig_Type4_Hdr.2.UNOFFICIAL FOUND" +# } + +# @test "checking clamav-unofficial-sigs: TEST 3 — Email.Sanesecurity.TestSig_Type4_Bdy" { +# run docker exec mailserver_default /bin/sh -c "clamscan --database=/var/lib/clamav/phish.ndb - < /tmp/tests/clamav/test3.eml" +# assert_failure +# assert_output --partial "Sanesecurity.TestSig_Type4_Bdy.3.UNOFFICIAL FOUND" +# } + +# +# zeyple +# + +@test "checking zeyple: zeyple.log doesn't exist (default configuration)" { + run docker exec mailserver_default [ -f /var/log/zeyple.log ] + assert_failure +} + +@test "checking zeyple: pubring.kbx doesn't exist (default configuration)" { + run docker exec mailserver_default [ -f /var/mail/zeyple/keys/pubring.kbx ] + assert_failure +} + +@test "checking zeyple: trustdb.gpg doesn't exist (default configuration)" { + run docker exec mailserver_default [ -f /var/mail/zeyple/keys/trustdb.gpg ] + assert_failure +} + +@test "checking zeyple: content_filter value (default configuration)" { + run docker exec mailserver_default /bin/sh -c "postconf -h content_filter" + assert_success + assert_output "" +} + +@test "checking zeyple: user zeyple doesn't exist (default configuration)" { + run docker exec mailserver_default /bin/sh -c "id -u zeyple" + assert_failure +} + +# +# unbound +# + +@test "checking unbound: /etc/resolv.conf (default configuration)" { + run docker exec mailserver_default cat /etc/resolv.conf + assert_success + assert_output "nameserver 127.0.0.1" +} + +@test "checking unbound: /var/mail/postfix/spool/etc/resolv.conf (default configuration)" { + run docker exec mailserver_default cat /var/mail/postfix/spool/etc/resolv.conf + assert_success + assert_output "nameserver 127.0.0.1" +} + +@test "checking unbound: root.hints exist (default configuration)" { + run docker exec mailserver_default [ -f /etc/unbound/root.hints ] + assert_success +} + +@test "checking unbound: root.key exist (default configuration)" { + run docker exec mailserver_default [ -f /etc/unbound/root.key ] + assert_success +} + +@test "checking unbound: unbound_control.key exist" { + run docker exec mailserver_default [ -f /etc/unbound/unbound_control.key ] + assert_success +} + +@test "checking unbound: unbound_control.pem exist" { + run docker exec mailserver_default [ -f /etc/unbound/unbound_control.pem ] + assert_success +} + +@test "checking unbound: unbound_server.key exist" { + run docker exec mailserver_default [ -f /etc/unbound/unbound_server.key ] + assert_success +} + +@test "checking unbound: unbound_server.pem exist" { + run docker exec mailserver_default [ -f /etc/unbound/unbound_server.pem ] + assert_success +} + +@test "checking unbound: server is running and unbound-control works" { + run docker exec mailserver_default /bin/sh -c "unbound-control status" + assert_success + assert_output --partial 'is running' +} + +@test "checking unbound: get stats" { + run docker exec mailserver_default /bin/sh -c "unbound-control stats_noreset" + assert_success +} + +@test "checking unbound: testing DNSSEC validation" { + run docker exec mailserver_default /bin/sh -c "dig com. SOA +nocmd +noall +dnssec +comments | grep 'flags: qr rd ra ad' | wc -l" + assert_success + assert_output 1 +} + +@test "checking unbound: debug mode disabled" { + run docker exec mailserver_default /bin/sh -c "unbound-control status | grep 'verbosity: 0'" + assert_success +} + +# +# ssl +# + +@test "checking ssl: generated default cert works correctly" { + run docker exec mailserver_default /bin/sh -c "timeout 1 openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp | grep 'Verify return code: 18 (self signed certificate)'" + assert_success +} + +@test "checking ssl: default configuration is correct" { + run docker exec mailserver_default /bin/sh -c "grep '/ssl' /etc/postfix/main.cf | wc -l" + assert_success + assert_output 4 + run docker exec mailserver_default /bin/sh -c "grep '/ssl' /etc/dovecot/conf.d/10-ssl.conf | wc -l" + assert_success + assert_output 2 +} + +# +# index files +# + +@test "checking hash tables: existing header_checks and virtual index files" { + run docker exec mailserver_default [ -f /etc/postfix/header_checks.db ] + assert_success + run docker exec mailserver_default [ -f /etc/postfix/virtual.db ] + assert_success +} + +# +# logs +# + +@test "checking logs: /var/log/mail.log in mailserver_default is error free" { + run docker exec mailserver_default grep -i ': error:' /var/log/mail.log + assert_failure + run docker exec mailserver_default grep -i 'is not writable' /var/log/mail.log + assert_failure + run docker exec mailserver_default grep -i 'permission denied' /var/log/mail.log + assert_failure + run docker exec mailserver_default grep -i 'address already in use' /var/log/mail.log + assert_failure +} + +@test "checking logs: /var/log/mail.err in mailserver_default does not exist" { + run docker exec mailserver_default cat /var/log/mail.err + assert_failure + assert_output --partial 'No such file or directory' +} diff --git a/test/ecdsa.bats b/test/ecdsa.bats new file mode 100644 index 00000000..cc77449f --- /dev/null +++ b/test/ecdsa.bats @@ -0,0 +1,41 @@ +load 'test_helper/bats-support/load' +load 'test_helper/bats-assert/load' + +# rspamd + +@test "checking rspamd: 3 modules disabled in ecdsa configuration" { + run docker exec mailserver_ecdsa cat /etc/rspamd/local.d/rbl.conf + assert_success + assert_output "enabled = false;" + run docker exec mailserver_ecdsa cat /etc/rspamd/local.d/mx_check.conf + assert_success + assert_output "enabled = false;" + run docker exec mailserver_ecdsa cat /etc/rspamd/local.d/url_redirector.conf + assert_success + assert_output "enabled = false;" +} + +@test "checking rspamd: 2 addresses whitelisted in ecdsa configuration" { + run docker exec mailserver_ecdsa /bin/bash -c "grep '\"test@example.com\",\"another@domain.tld\"' /etc/rspamd/local.d/settings.conf | wc -l" + assert_success + assert_output 1 +} + +# +# ssl +# + +@test "checking ssl: ECDSA P-384 cert works correctly" { + run docker exec mailserver_ecdsa /bin/sh -c "timeout 1 openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp | grep 'Verify return code: 18 (self signed certificate)'" + assert_success +} + +# +# logs +# + +@test "checking logs: /var/log/mail.err in mailserver_ecdsa does not exist" { + run docker exec mailserver_ecdsa cat /var/log/mail.err + assert_failure + assert_output --partial 'No such file or directory' +} diff --git a/test/ldap.bats b/test/ldap.bats new file mode 100644 index 00000000..a0536824 --- /dev/null +++ b/test/ldap.bats @@ -0,0 +1,511 @@ +load 'test_helper/bats-support/load' +load 'test_helper/bats-assert/load' + +# +# system +# + +@test "checking system: /etc/mailname (env method) (ldap)" { + run docker exec mailserver_ldap cat /etc/mailname + assert_success + assert_output "mail.domain.tld" +} + +@test "checking system: all environment variables have been replaced (ldap configuration)" { + run docker exec mailserver_ldap /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd /etc/cron.d /etc/mailname /usr/local/bin" + assert_failure +} + +# +# processes (ldap configuration) +# + +@test "checking process: s6 (ldap configuration)" { + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-svscan /services'" + assert_success +} + +@test "checking process: rsyslog (ldap configuration)" { + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise rsyslogd'" + assert_success + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[r]syslogd -n -f /etc/rsyslog/rsyslog.conf'" + assert_success +} + +@test "checking process: cron (ldap configuration)" { + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise cron'" + assert_success + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[c]ron -f'" + assert_success +} + +@test "checking process: postfix (ldap configuration)" { + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise postfix'" + assert_success + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[/]usr/lib/postfix/sbin/master -s'" + assert_success +} + +@test "checking process: dovecot (ldap configuration)" { + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise dovecot'" + assert_success + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[/]usr/sbin/dovecot -F'" + assert_success +} + +@test "checking process: rspamd (ldap configuration)" { + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise rspamd'" + assert_success + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[r]spamd: main process'" + assert_success + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[r]spamd: rspamd_proxy process'" + assert_success + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[r]spamd: controller process'" + assert_success +} + +@test "checking process: clamd (ldap configuration)" { + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise clamd'" + assert_success + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[c]lamd'" + assert_success + +} + +@test "checking process: freshclam (ldap configuration)" { + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise freshclam'" + assert_success + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[f]reshclam -d'" + assert_success +} + +@test "checking process: unbound (ldap configuration)" { + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise unbound'" + assert_success + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[u]nbound'" + assert_success +} + +@test "checking process: cert_watcher (ldap configuration)" { + run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise cert_watcher'" + assert_success +} + +# +# processes restarting +# + +@test "checking process: 10 cron tasks to reset all the process counters" { + run docker exec mailserver_ldap /bin/bash -c "cat /etc/cron.d/counters | wc -l" + assert_success + assert_output 10 +} + +@test "checking process: no service restarted (ldap configuration)" { + run docker exec mailserver_ldap cat /tmp/counters/_parent + assert_success + assert_output 0 + run docker exec mailserver_ldap cat /tmp/counters/clamd + assert_success + assert_output 0 + run docker exec mailserver_ldap cat /tmp/counters/cron + assert_success + assert_output 0 + run docker exec mailserver_ldap cat /tmp/counters/dovecot + assert_success + assert_output 0 + run docker exec mailserver_ldap cat /tmp/counters/freshclam + assert_success + assert_output 0 + run docker exec mailserver_ldap cat /tmp/counters/postfix + assert_success + assert_output 0 + run docker exec mailserver_ldap cat /tmp/counters/rspamd + assert_success + assert_output 0 + run docker exec mailserver_ldap cat /tmp/counters/rsyslogd + assert_success + assert_output 0 + run docker exec mailserver_ldap cat /tmp/counters/unbound + assert_success + assert_output 0 + run docker exec mailserver_ldap cat /tmp/counters/cert_watcher + assert_success + assert_output 0 +} + +# +# ports +# + +@test "checking port (25): external port listening (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 25" + assert_success +} + +@test "checking port (53): internal port listening (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 127.0.0.1 53" + assert_success +} + +@test "checking port (110): external port closed (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 110" + assert_failure +} + +@test "checking port (143): external port listening (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 143" + assert_success +} + +@test "checking port (465): external port listening (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 465" + assert_success +} + +@test "checking port (587): external port listening (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 587" + assert_success +} + +@test "checking port (993): external port listening (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 993" + assert_success +} + +@test "checking port (995): external port closed (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 995" + assert_failure +} + +@test "checking port (3310): external port closed (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 3310" + assert_success +} + +@test "checking port (4190): external port closed (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 4190" + assert_success +} + +@test "checking port (8953): internal port listening (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 127.0.0.1 8953" + assert_success +} + +@test "checking port (10025): internal port closed (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 127.0.0.1 10025" + assert_failure +} + +@test "checking port (10026): internal port listening (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 127.0.0.1 10026" + assert_success +} + +@test "checking port (11332): external port listening (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 11332" + assert_success +} + +@test "checking port (11334): external port listening (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 11334" + assert_success +} + +# +# sasl +# + +@test "checking sasl: dovecot auth with good password (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "doveadm auth test sarah.connor@domain.tld testpasswd12 | grep 'auth succeeded'" + assert_success +} + +@test "checking sasl: dovecot auth with bad password (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "doveadm auth test sarah.connor@domain.tld badpassword | grep 'auth failed'" + assert_success +} + +# +# smtp +# http://www.postfix.org/SASL_README.html#server_test +# + +# Base64 AUTH STRINGS +# AHNhcmFoLmNvbm5vckBkb21haW4udGxkAHRlc3RwYXNzd2QxMg== +# echo -ne '\000sarah.connor@domain.tld\000testpasswd12' | openssl base64 +# AHNhcmFoLmNvbm5vckBkb21haW4udGxkAGJhZHBhc3N3b3Jk +# echo -ne '\000sarah.connor@domain.tld\000badpassword' | openssl base64 +# c2FyYWguY29ubm9yQGRvbWFpbi50bGQ= +# echo -ne 'sarah.connor@domain.tld' | openssl base64 +# dGVzdHBhc3N3ZDEy +# echo -ne 'testpasswd12' | openssl base64 +# YmFkcGFzc3dvcmQ= +# echo -ne 'badpassword' | openssl base64 + +@test "checking smtp (25): STARTTLS AUTH PLAIN works with good password (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:25 -starttls smtp < /tmp/tests/auth/smtp-auth-plain.txt 2>&1 | grep -i 'authentication successful'" + assert_success +} + +@test "checking smtps (465): SSL/TLS AUTH LOGIN works with good password (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:465 < /tmp/tests/auth/smtp-auth-login.txt 2>&1 | grep -i 'authentication successful'" + assert_success +} + +@test "checking smtp: john.doe should have received 6 mails (internal + external + subaddress + hostmaster_forward + postmaster_alias + group_alias) (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/john.doe/mail/new/ | wc -l" + assert_success + assert_output 6 +} + +@test "checking smtp: sarah.connor should have received 1 mail (internal spam-ham test) (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/sarah.connor/mail/new/ | wc -l" + assert_success + assert_output 1 +} + +@test "checking smtp: sarah.connor should have received 1 spam (with manual IMAP COPY to Spam folder) (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/sarah.connor/mail/.Spam/cur/ | wc -l" + assert_success + assert_output 1 +} + +@test "checking smtp: john.connor should have received 2 mails (hostmaster_forward + postmaster_alias) (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/john.connor/mail/new/ | wc -l" + assert_success + assert_output 2 +} + +@test "checking smtp: rejects mail to unknown user (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "grep ': Recipient address rejected: User unknown in virtual mailbox table' /var/log/mail.log | wc -l" + assert_success + assert_output 1 +} + +@test "checking smtp: delivers mail to existing forward (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "grep 'to=, orig_to=' /var/log/mail.log | grep 'status=sent' | wc -l" + assert_success + assert_output 1 +} + +@test "checking smtp: delivers mail to existing alias (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "grep 'to=, orig_to=' /var/log/mail.log | grep 'status=sent' | wc -l" + assert_success + assert_output 1 +} + +@test "checking smtp: delivers mail to existing forward (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "grep 'to=, orig_to=' /var/log/mail.log | grep 'status=sent' | wc -l" + assert_success + assert_output 1 +} + +@test "checking smtp: delivers mail to existing alias (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "grep 'to=, orig_to=' /var/log/mail.log | grep 'status=sent' | wc -l" + assert_success + assert_output 1 +} + +# +# imap +# + +@test "checking imap (143): STARTTLS login works with good password (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:143 -starttls imap < /tmp/tests/auth/imap-auth.txt 2>&1 | grep -i 'logged in'" + assert_success +} + +@test "checking imaps (993): SSL/TLS login works with good password (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/auth/imap-auth.txt 2>&1 | grep -i 'logged in'" + assert_success +} + +@test "checking imaps (993): SSL/TLS login fails with good master password on no master config (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/auth/imap-auth-master.txt 2>&1 | grep -i 'authentication failed'" + assert_success +} + +# rspamd + +@test "checking rspamd: spam filtered (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "grep -i 'Gtube pattern; from= to= ' /var/log/mail.log | wc -l" + assert_success + assert_output 1 +} + +# +# accounts +# + +@test "checking accounts: user accounts (ldap configuration)" { + run docker exec mailserver_ldap doveadm user '*' + assert_success + [ "${lines[0]}" = "john.doe@domain.tld" ] + [ "${lines[1]}" = "sarah.connor@domain.tld" ] + [ "${lines[2]}" = "john.connor@domain.tld" ] +} + +@test "checking accounts: user accounts (ldap2 configuration)" { + run docker exec mailserver_ldap doveadm user '*' + assert_success + [ "${lines[0]}" = "john.doe@domain.tld" ] + [ "${lines[1]}" = "sarah.connor@domain.tld" ] + [ "${lines[2]}" = "john.connor@domain.tld" ] +} + +@test "checking accounts: user quotas (ldap configuration)" { + run docker exec mailserver_ldap /bin/bash -c "doveadm quota get -A 2>&1 | grep '1000' | wc -l" + assert_success + assert_output 1 + run docker exec mailserver_ldap /bin/bash -c "doveadm quota get -A 2>&1 | grep '2000' | wc -l" + assert_success + assert_output 1 + run docker exec mailserver_ldap /bin/bash -c "doveadm quota get -A 2>&1 | grep '4000' | wc -l" + assert_success + assert_output 1 +} + +# +# dkim +# + +@test "checking dkim: all key pairs are generated (ldap configuration)" { + run docker exec mailserver_ldap /bin/bash -c "ls -A /var/mail/dkim/*/mail20190101.{private.key,public.key} | wc -l" + assert_success + assert_output 6 +} + +# +# postfix +# + +@test "checking postfix: mynetworks value (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "postconf -h mynetworks" + assert_success + assert_output "127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128" +} + +@test "checking postfix: smtp_tls_security_level value (ldap configuration)" { + run docker exec mailserver_ldap postconf -h smtp_tls_security_level + assert_success + assert_output "dane" +} + +@test "checking postfix: smtp_dns_support_level value (ldap configuration)" { + run docker exec mailserver_ldap postconf -h smtp_dns_support_level + assert_success + assert_output "dnssec" +} + +@test "checking postfix: smtpd_sender_login ldap maps (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "postconf -h smtpd_sender_login_maps | grep 'ldap'" + assert_success +} + +# +# dovecot +# + +@test "checking dovecot: login_greeting value (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "doveconf -h login_greeting 2>/dev/null" + assert_success + assert_output "Do. Or do not. There is no try." +} + +@test "checking dovecot: debug mode disabled (ldap configuration)" { + run docker exec mailserver_ldap /bin/sh -c "doveconf -h auth_verbose 2>/dev/null" + assert_success + assert_output "no" + run docker exec mailserver_ldap /bin/sh -c "doveconf -h auth_verbose_passwords 2>/dev/null" + assert_success + assert_output "no" + run docker exec mailserver_ldap /bin/sh -c "doveconf -h auth_debug 2>/dev/null" + assert_success + assert_output "no" + run docker exec mailserver_ldap /bin/sh -c "doveconf -h auth_debug_passwords 2>/dev/null" + assert_success + assert_output "no" + run docker exec mailserver_ldap /bin/sh -c "doveconf -h mail_debug 2>/dev/null" + assert_success + assert_output "no" + run docker exec mailserver_ldap /bin/sh -c "doveconf -h verbose_ssl 2>/dev/null" + assert_success + assert_output "no" +} + +# +# unbound +# + +@test "checking unbound: /etc/resolv.conf (ldap configuration)" { + run docker exec mailserver_ldap cat /etc/resolv.conf + assert_success + assert_output "nameserver 127.0.0.1" +} + +@test "checking unbound: /var/mail/postfix/spool/etc/resolv.conf (ldap configuration)" { + run docker exec mailserver_ldap cat /var/mail/postfix/spool/etc/resolv.conf + assert_success + assert_output "nameserver 127.0.0.1" +} + +@test "checking unbound: root.hints exist (ldap configuration)" { + run docker exec mailserver_ldap [ -f /etc/unbound/root.hints ] + assert_success +} + +@test "checking unbound: root.key exist (ldap configuration)" { + run docker exec mailserver_ldap [ -f /etc/unbound/root.key ] + assert_success +} + +# +# ldap +# + +@test "checking ldap maps exist in postfix main.cf in ldap configurations" { + run docker exec mailserver_ldap grep -i 'ldap:' /etc/postfix/main.cf + assert_success +} + +@test "checking no sql maps exist in postfix main.cf in ldap configurations" { + run docker exec mailserver_ldap grep -i 'sql' /etc/postfix/main.cf + assert_failure +} + +@test "checking ldap alias, forward and group maps exist in postfix main.cf in ldap configurations" { + run docker exec mailserver_ldap grep -i 'ldap:/etc/postfix/ldap/virtual-alias-maps.cf' /etc/postfix/main.cf + assert_success + run docker exec mailserver_ldap grep -i 'ldap:/etc/postfix/ldap/virtual-forward-maps.cf' /etc/postfix/main.cf + assert_success + run docker exec mailserver_ldap grep -i 'ldap:/etc/postfix/ldap/virtual-group-maps.cf' /etc/postfix/main.cf + assert_success +} + +@test "checking ldap master only exists in ldap2 configurations" { + run docker exec mailserver_ldap grep -i 'master' /etc/dovecot/conf.d/auth-ldap.conf.ext + assert_failure +} + +# +# logs +# + +@test "checking logs: /var/log/mail.log in mailserver_ldap is error free" { + run docker exec mailserver_ldap grep -i ': error:' /var/log/mail.log + assert_failure + run docker exec mailserver_ldap grep -i 'is not writable' /var/log/mail.log + assert_failure + run docker exec mailserver_ldap grep -i 'permission denied' /var/log/mail.log + assert_failure + run docker exec mailserver_ldap grep -i 'address already in use' /var/log/mail.log + assert_failure +} + +@test "checking logs: /var/log/mail.err in mailserver_ldap does not exist" { + run docker exec mailserver_ldap cat /var/log/mail.err + assert_failure + assert_output --partial 'No such file or directory' +} diff --git a/test/ldap2.bats b/test/ldap2.bats new file mode 100644 index 00000000..a9b289f4 --- /dev/null +++ b/test/ldap2.bats @@ -0,0 +1,225 @@ +load 'test_helper/bats-support/load' +load 'test_helper/bats-assert/load' + +# +# system +# + +@test "checking system: all environment variables have been replaced (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd /etc/cron.d /etc/mailname /usr/local/bin" + assert_failure +} + +# +# processes (ldap2 configuration) +# + +@test "checking process: s6 (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-svscan /services'" + assert_success +} + +@test "checking process: rsyslog (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise rsyslogd'" + assert_success + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[r]syslogd -n -f /etc/rsyslog/rsyslog.conf'" + assert_success +} + +@test "checking process: cron (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise cron'" + assert_success + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[c]ron -f'" + assert_success +} + +@test "checking process: postfix (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise postfix'" + assert_success + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[/]usr/lib/postfix/sbin/master -s'" + assert_success +} + +@test "checking process: dovecot (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise dovecot'" + assert_success + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[/]usr/sbin/dovecot -F'" + assert_success +} + +@test "checking process: rspamd (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise rspamd'" + assert_success + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[r]spamd: main process'" + assert_success + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[r]spamd: rspamd_proxy process'" + assert_success + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[r]spamd: controller process'" + assert_success +} + +@test "checking process: clamd (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise clamd'" + assert_success + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[c]lamd'" + assert_failure +} + +@test "checking process: freshclam (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise freshclam'" + assert_success + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[f]reshclam -d'" + assert_failure +} + +@test "checking process: unbound (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise unbound'" + assert_success + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[u]nbound'" + assert_failure +} + +@test "checking process: cert_watcher (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise cert_watcher'" + assert_success +} + +# +# sasl +# + +@test "checking sasl: dovecot auth with good password (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/sh -c "doveadm auth test john.connor@domain.tld testpasswd2 | grep 'auth succeeded'" + assert_success +} + +@test "checking sasl: dovecot auth with bad password (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/sh -c "doveadm auth test john.connor@domain.tld badpassword | grep 'auth failed'" + assert_success +} + +@test "checking sasl: dovecot auth with good master password (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/sh -c "doveadm auth test john.doe@domain.tld*john.connor@domain.tld testpasswd2 | grep 'auth succeeded'" + assert_success +} + +@test "checking sasl: dovecot auth with bad master password (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/sh -c "doveadm auth test john.connor@domain.tld*john.doe@domain.tld testpasswd12 | grep 'auth failed'" + assert_success +} + +@test "checking sasl: dovecot auth with good non-master password (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/sh -c "doveadm auth test sarah.connor@domain.tld*john.doe@domain.tld testpasswd12 | grep 'auth failed'" + assert_success +} + +# +# smtp +# http://www.postfix.org/SASL_README.html#server_test +# + +# Base64 AUTH STRINGS +# AHNhcmFoLmNvbm5vckBkb21haW4udGxkAHRlc3RwYXNzd2QxMg== +# echo -ne '\000sarah.connor@domain.tld\000testpasswd12' | openssl base64 +# AHNhcmFoLmNvbm5vckBkb21haW4udGxkAGJhZHBhc3N3b3Jk +# echo -ne '\000sarah.connor@domain.tld\000badpassword' | openssl base64 +# c2FyYWguY29ubm9yQGRvbWFpbi50bGQ= +# echo -ne 'sarah.connor@domain.tld' | openssl base64 +# dGVzdHBhc3N3ZDEy +# echo -ne 'testpasswd12' | openssl base64 +# YmFkcGFzc3dvcmQ= +# echo -ne 'badpassword' | openssl base64 + +@test "checking smtp: john.doe should have received 4 mails (internal + external + subaddress + postmaster_alias) (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/john.doe/mail/new/ | wc -l" + assert_success + assert_output 4 +} + +@test "checking smtp: john.connor should have received 1 mails (postmaster_alias) (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/john.connor/mail/new/ | wc -l" + assert_success + assert_output 1 +} + +@test "checking smtp: delivers mail to existing alias (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/sh -c "grep 'to=, orig_to=' /var/log/mail.log | grep 'status=sent' | wc -l" + assert_success + assert_output 1 +} + +@test "checking smtp: rejects mail to unknown forward (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/sh -c "grep ': Recipient address rejected: User unknown in virtual mailbox table' /var/log/mail.log | grep 'NOQUEUE: reject' | wc -l" + assert_success + assert_output 1 +} + +@test "checking smtp: rejects mail to unknown group (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/sh -c "grep ': Recipient address rejected: User unknown in virtual mailbox table' /var/log/mail.log | grep 'NOQUEUE: reject' | wc -l" + assert_success + assert_output 1 +} + +# +# imap +# + +@test "checking imaps (993): SSL/TLS login works with good master password (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/auth/imap-auth-master.txt 2>&1 | grep -i 'logged in'" + assert_success +} + +@test "checking imaps (993): SSL/TLS login fails with bad password (ldap2 configuration)" { + run docker exec mailserver_ldap2 /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/auth/imap-auth-master-wrong.txt 2>&1 | grep -i 'authentication failed'" + assert_success +} + +# +# ldap +# + +@test "checking ldap maps exist in postfix main.cf in ldap configurations" { + run docker exec mailserver_ldap2 grep -i 'ldap:' /etc/postfix/main.cf + assert_success +} + +@test "checking no sql maps exist in postfix main.cf in ldap configurations" { + run docker exec mailserver_ldap2 grep -i 'sql' /etc/postfix/main.cf + assert_failure +} + +@test "checking ony ldap alias (not forward and group maps) exist in postfix main.cf in ldap2 configurations" { + run docker exec mailserver_ldap2 grep -i 'ldap:/etc/postfix/ldap/virtual-alias-maps.cf' /etc/postfix/main.cf + assert_success + run docker exec mailserver_ldap2 grep -i 'ldap:/etc/postfix/ldap/virtual-forward-maps.cf' /etc/postfix/main.cf + assert_failure + run docker exec mailserver_ldap2 grep -i 'ldap:/etc/postfix/ldap/virtual-group-maps.cf' /etc/postfix/main.cf + assert_failure +} + +@test "checking ldap master only exists in ldap2 configurations" { + run docker exec mailserver_ldap2 grep -i 'master' /etc/dovecot/conf.d/auth-ldap.conf.ext + assert_success +} + + +# +# logs +# + +@test "checking logs: /var/log/mail.log in mailserver_ldap2 is error free" { + run docker exec mailserver_ldap2 grep -i ': error:' /var/log/mail.log + assert_failure + run docker exec mailserver_ldap2 grep -i 'is not writable' /var/log/mail.log + assert_failure + run docker exec mailserver_ldap2 grep -i 'permission denied' /var/log/mail.log + assert_failure + run docker exec mailserver_ldap2 grep -i 'address already in use' /var/log/mail.log + assert_failure +} + +@test "checking logs: /var/log/mail.err in mailserver_ldap2 does not exist" { + run docker exec mailserver_ldap2 cat /var/log/mail.err + assert_failure + assert_output --partial 'No such file or directory' +} diff --git a/test/reverse.bats b/test/reverse.bats new file mode 100644 index 00000000..968ef125 --- /dev/null +++ b/test/reverse.bats @@ -0,0 +1,575 @@ +load 'test_helper/bats-support/load' +load 'test_helper/bats-assert/load' + +# +# system +# + +@test "checking system: /etc/mailname (env method) (reverse)" { + run docker exec mailserver_reverse cat /etc/mailname + assert_success + assert_output "mail.domain.tld" +} + +@test "checking system: all environment variables have been replaced (reverse configuration)" { + run docker exec mailserver_reverse /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd /etc/cron.d /etc/mailname /usr/local/bin" + assert_failure +} + +# +# processes (reverse configuration) +# + +@test "checking process: s6 (reverse configuration)" { + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-svscan /services'" + assert_success +} + +@test "checking process: rsyslog (reverse configuration)" { + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise rsyslogd'" + assert_success + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[r]syslogd -n -f /etc/rsyslog/rsyslog.conf'" + assert_success +} + +@test "checking process: cron (reverse configuration)" { + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise cron'" + assert_success + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[c]ron -f'" + assert_success +} + +@test "checking process: postfix (reverse configuration)" { + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise postfix'" + assert_success + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[/]usr/lib/postfix/sbin/master -s'" + assert_success +} + +@test "checking process: dovecot (reverse configuration)" { + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise dovecot'" + assert_success + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[/]usr/sbin/dovecot -F'" + assert_success +} + +@test "checking process: rspamd (reverse configuration)" { + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise rspamd'" + assert_success + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[r]spamd: main process'" + assert_success + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[r]spamd: rspamd_proxy process'" + assert_success + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[r]spamd: controller process'" + assert_success +} + +@test "checking process: clamd (reverse configuration)" { + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise clamd'" + assert_success + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[c]lamd'" + assert_failure +} + +@test "checking process: freshclam (reverse configuration)" { + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise freshclam'" + assert_success + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[f]reshclam -d'" + assert_failure +} + +@test "checking process: unbound (reverse configuration)" { + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise unbound'" + assert_success + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[u]nbound'" + assert_failure +} + +@test "checking process: cert_watcher (reverse configuration)" { + run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise cert_watcher'" + assert_success +} + +# +# processes restarting +# + +@test "checking process: 10 cron tasks to reset all the process counters" { + run docker exec mailserver_reverse /bin/bash -c "cat /etc/cron.d/counters | wc -l" + assert_success + assert_output 10 +} + +@test "checking process: no service restarted (reverse configuration)" { + run docker exec mailserver_reverse cat /tmp/counters/_parent + assert_success + assert_output 0 + run docker exec mailserver_reverse cat /tmp/counters/clamd + assert_success + assert_output 0 + run docker exec mailserver_reverse cat /tmp/counters/cron + assert_success + assert_output 0 + run docker exec mailserver_reverse cat /tmp/counters/dovecot + assert_success + assert_output 0 + run docker exec mailserver_reverse cat /tmp/counters/freshclam + assert_success + assert_output 0 + run docker exec mailserver_reverse cat /tmp/counters/postfix + assert_success + assert_output 0 + run docker exec mailserver_reverse cat /tmp/counters/rspamd + assert_success + assert_output 0 + run docker exec mailserver_reverse cat /tmp/counters/rsyslogd + assert_success + assert_output 0 + run docker exec mailserver_reverse cat /tmp/counters/unbound + assert_success + assert_output 0 + run docker exec mailserver_reverse cat /tmp/counters/cert_watcher + assert_success + assert_output 0 +} + +# +# ports +# + +@test "checking port (25): external port listening (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 25" + assert_success +} + +@test "checking port (53): internal port listening (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 127.0.0.1 53" + assert_failure +} + +@test "checking port (110): external port listening (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 110" + assert_success +} + +@test "checking port (143): external port listening (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 143" + assert_success +} + +@test "checking port (465): external port listening (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 465" + assert_success +} + +@test "checking port (587): external port listening (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 587" + assert_success +} + +@test "checking port (993): external port listening (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 993" + assert_success +} + +@test "checking port (995): external port listening (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 995" + assert_success +} + +@test "checking port (3310): external port closed (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 3310" + assert_failure +} + +@test "checking port (4190): external port closed (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 4190" + assert_failure +} + +@test "checking port (8953): internal port listening (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 127.0.0.1 8953" + assert_failure +} + +@test "checking port (10025): internal port listening (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 127.0.0.1 10025" + assert_success +} + +@test "checking port (10026): internal port listening (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 127.0.0.1 10026" + assert_success +} + +@test "checking port (11332): external port listening (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 11332" + assert_success +} + +@test "checking port (11334): external port listening (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 11334" + assert_success +} + +# +# sasl +# + +@test "checking sasl: dovecot auth with good password (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "doveadm auth test sarah.connor@domain.tld testpasswd12 | grep 'auth succeeded'" + assert_success +} + +# +# smtp +# http://www.postfix.org/SASL_README.html#server_test +# + +# Base64 AUTH STRINGS +# AHNhcmFoLmNvbm5vckBkb21haW4udGxkAHRlc3RwYXNzd2QxMg== +# echo -ne '\000sarah.connor@domain.tld\000testpasswd12' | openssl base64 +# AHNhcmFoLmNvbm5vckBkb21haW4udGxkAGJhZHBhc3N3b3Jk +# echo -ne '\000sarah.connor@domain.tld\000badpassword' | openssl base64 +# c2FyYWguY29ubm9yQGRvbWFpbi50bGQ= +# echo -ne 'sarah.connor@domain.tld' | openssl base64 +# dGVzdHBhc3N3ZDEy +# echo -ne 'testpasswd12' | openssl base64 +# YmFkcGFzc3dvcmQ= +# echo -ne 'badpassword' | openssl base64 + +@test "checking smtp (25): STARTTLS AUTH PLAIN works with good password (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:25 -starttls smtp < /tmp/tests/auth/smtp-auth-plain.txt 2>&1 | grep -i 'authentication successful'" + assert_success +} + +@test "checking submission (587): STARTTLS AUTH LOGIN works with good password (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/auth/smtp-auth-login.txt 2>&1 | grep -i 'authentication successful'" + assert_success +} + +@test "checking smtps (465): SSL/TLS AUTH LOGIN works with good password (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:465 < /tmp/tests/auth/smtp-auth-login.txt 2>&1 | grep -i 'authentication successful'" + assert_success +} + +@test "checking smtp: john.doe should have received 4 mails (internal + external + subaddress + hostmaster alias) (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/john.doe/subdir/new/ | wc -l" + assert_success + assert_output 4 +} + +@test "checking smtp: rejects mail to unknown user (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "grep ': Recipient address rejected: User unknown in virtual mailbox table' /var/log/mail.log | wc -l" + assert_success + assert_output 1 +} + +@test "checking smtp: delivers mail to existing alias (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "grep 'to=, orig_to=' /var/log/mail.log | grep 'status=sent' | wc -l" + assert_success + assert_output 1 +} + +# +# imap +# + +@test "checking imap (143): STARTTLS login works with good password (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:143 -starttls imap < /tmp/tests/auth/imap-auth.txt 2>&1 | grep -i 'logged in'" + assert_success +} + +@test "checking imaps (993): SSL/TLS login works with good password (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/auth/imap-auth.txt 2>&1 | grep -i 'logged in'" + assert_success +} + +# +# pop +# + +@test "checking pop3 (110): STARTTLS login works with good password" { + run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:110 -starttls pop3 < /tmp/tests/auth/pop3-auth.txt 2>&1 | grep -i 'ok logged in'" + assert_success +} + +@test "checking pop3 (110): STARTTLS login fails with bad password" { + run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:110 -starttls pop3 < /tmp/tests/auth/pop3-auth-wrong.txt 2>&1 | grep -i 'authentication failed'" + assert_success +} + +@test "checking pop3s (995): SSL/TLS login works with good password" { + run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:995 < /tmp/tests/auth/pop3-auth.txt 2>&1 | grep -i 'ok logged in'" + assert_success +} + +@test "checking pop3s (995): SSL/TLS login fails with bad password" { + run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:995 < /tmp/tests/auth/pop3-auth-wrong.txt 2>&1 | grep -i 'authentication failed'" + assert_success +} + +# rspamd + +@test "checking rspamd: spam filtered (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "grep -i 'Gtube pattern; from= to= ' /var/log/mail.log | wc -l" + assert_success + assert_output 1 +} + +@test "checking rspamd: dkim/arc signing is disabled (reverse configuration)" { + run docker exec mailserver_reverse cat /etc/rspamd/local.d/arc.conf + assert_success + assert_output "enabled = false;" + run docker exec mailserver_reverse cat /etc/rspamd/local.d/dkim_signing.conf + assert_success + assert_output "enabled = false;" +} + +@test "checking rspamd: greylisting policy is disabled (reverse configuration)" { + run docker exec mailserver_reverse cat /etc/rspamd/local.d/greylisting.conf + assert_success + assert_output "enabled = false;" +} + +@test "checking rspamd: ratelimiting policy is disabled (reverse configuration)" { + run docker exec mailserver_reverse cat /etc/rspamd/local.d/ratelimit.conf + assert_success + assert_output "enabled = false;" +} + +# +# accounts +# + +@test "checking accounts: user accounts (reverse configuration)" { + run docker exec mailserver_reverse doveadm user '*' + assert_success + [ "${lines[0]}" = "john.doe@domain.tld" ] + [ "${lines[1]}" = "sarah.connor@domain.tld" ] +} + +@test "checking accounts: user quotas (reverse configuration)" { + run docker exec mailserver_reverse /bin/bash -c "doveadm quota get -A 2>&1 | grep '1000' | wc -l" + assert_success + assert_output 2 +} + +# +# dkim +# + +@test "checking dkim: all key pairs are generated (reverse configuration)" { + run docker exec mailserver_reverse /bin/bash -c "ls -A /var/mail/dkim/*/other.{private.key,public.key} | wc -l" + assert_success + assert_output 2 +} + +@test "checking dkim: control the size of the RSA key pair (4096bits)" { + run docker exec mailserver_reverse /bin/bash -c "openssl rsa -in /var/mail/dkim/domain.tld/other.private.key -text -noout | grep -i 'Private-Key: (4096 bit, 2 primes)'" + assert_success +} + +# +# postfix +# + +@test "checking postfix: mynetworks value (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "postconf -h mynetworks" + assert_success + assert_output "127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8" +} + +@test "checking postfix: myorigin value (env method)" { + run docker exec mailserver_reverse postconf -h myorigin + assert_success + assert_output "mail.domain.tld" +} + +@test "checking postfix: smtp_tls_security_level value (reverse configuration)" { + run docker exec mailserver_reverse postconf -h smtp_tls_security_level + assert_success + assert_output "may" +} + +@test "checking postfix: smtp_dns_support_level value (reverse configuration)" { + run docker exec mailserver_reverse postconf -h smtp_dns_support_level + assert_success + assert_output "" +} + +@test "checking postfix: smtpd_sender_login pgsql maps (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "postconf -h smtpd_sender_login_maps | grep 'pgsql'" + assert_success +} + +# +# dovecot +# + +@test "checking dovecot: custom sieve file is used" { + run docker exec mailserver_reverse /bin/sh -c "wc -l < /var/mail/sieve/default.sieve" + assert_success + assert_output 4 +} + +@test "checking dovecot: login_greeting value (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "doveconf -h login_greeting 2>/dev/null" + assert_success + assert_output "Dovecot (Debian) ready." +} + +@test "checking dovecot: quota dict pgsql (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "doveconf dict sqlquota 2>/dev/null | grep 'pgsql'" + assert_success +} + +# +# clamav-unofficial-sigs +# + +@test "checking clamav-unofficial-sigs: cron task doesn't exist (reverse configuration)" { + run docker exec mailserver_reverse [ -f /etc/cron.d/clamav-unofficial-sigs ] + assert_failure +} + +@test "checking clamav-unofficial-sigs: logrotate task doesn't exist (reverse configuration)" { + run docker exec mailserver_reverse [ -f /etc/logrotate.d/clamav-unofficial-sigs ] + assert_failure +} + +# +# zeyple +# + +@test "checking zeyple: 4 messages delivered via zeyple service" { + run docker exec mailserver_reverse /bin/sh -c "grep -i 'delivered via zeyple service' /var/log/mail.log | wc -l" + assert_success + assert_output 4 +} + +@test "checking zeyple: 'processing outgoing message' 4 times in logs" { + run docker exec mailserver_reverse /bin/sh -c "grep -i 'Processing outgoing message' /var/log/zeyple.log | wc -l" + assert_success + assert_output 4 +} + +@test "checking zeyple: zeyple.py exist (reverse configuration)" { + run docker exec mailserver_reverse [ -f /usr/local/bin/zeyple.py ] + assert_success +} + +@test "checking zeyple: zeyple.log exist (reverse configuration)" { + run docker exec mailserver_reverse [ -f /var/log/zeyple.log ] + assert_success +} + +@test "checking zeyple: pubring.kbx exist (reverse configuration)" { + run docker exec mailserver_reverse [ -f /var/mail/zeyple/keys/pubring.kbx ] + assert_success +} + +@test "checking zeyple: trustdb.gpg exist (reverse configuration)" { + run docker exec mailserver_reverse [ -f /var/mail/zeyple/keys/trustdb.gpg ] + assert_success +} + +@test "checking zeyple: content_filter value (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "postconf -h content_filter" + assert_success + assert_output "zeyple" +} + +@test "checking zeyple: user zeyple exist (reverse configuration)" { + run docker exec mailserver_reverse /bin/sh -c "id -u zeyple" + assert_success +} + +@test "checking zeyple: retrieve john doe gpg key in public keyring" { + run docker exec mailserver_reverse /bin/sh -c "s6-setuidgid zeyple gpg --homedir /var/mail/zeyple/keys --with-colons --list-keys | grep 'John Doe (test key) ' | wc -l" + assert_success + assert_output 1 +} + +@test "checking zeyple: retrieve john doe gpg key in public keyring (using custom script)" { + run docker exec mailserver_reverse /bin/sh -c "encryption.sh --with-colons --list-keys | grep 'John Doe (test key) ' | wc -l" + assert_success + assert_output 1 +} + +@test "checking zeyple: 3 emails encrypted in john.doe folder" { + run docker exec mailserver_reverse /bin/sh -c "gzip -cd /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | grep -i 'multipart/encrypted' | wc -l" + assert_success + assert_output 3 + run docker exec mailserver_reverse /bin/sh -c "gzip -cd /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | grep -i 'BEGIN PGP MESSAGE' | wc -l" + assert_success + assert_output 3 + run docker exec mailserver_reverse /bin/sh -c "gzip -cd /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | grep -i 'END PGP MESSAGE' | wc -l" + assert_success + assert_output 3 +} + +# +# unbound +# + +@test "checking unbound: /etc/resolv.conf (reverse configuration)" { + run docker exec mailserver_reverse cat /etc/resolv.conf + assert_success + refute_output "nameserver 127.0.0.1" +} + +@test "checking unbound: /var/mail/postfix/spool/etc/resolv.conf (reverse configuration)" { + run docker exec mailserver_reverse cat /var/mail/postfix/spool/etc/resolv.conf + assert_success + refute_output "nameserver 127.0.0.1" +} + +@test "checking unbound: root.hints doesn't exist (reverse configuration)" { + run docker exec mailserver_reverse [ ! -f /etc/unbound/root.hints ] + assert_success +} + +@test "checking unbound: root.key doesn't exist (reverse configuration)" { + run docker exec mailserver_reverse [ ! -f /etc/unbound/root.key ] + assert_success +} + +# +# ssl +# + +@test "checking ssl: let's encrypt cert works correctly" { + run docker exec mailserver_reverse /bin/sh -c "timeout 1 openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp | grep 'Verify return code: 10 (certificate has expired)'" + assert_success +} + +@test "checking ssl: let's encrypt configuration is correct" { + run docker exec mailserver_reverse /bin/sh -c "grep '/ssl' /etc/postfix/main.cf | wc -l" + assert_success + assert_output 4 + run docker exec mailserver_reverse /bin/sh -c "grep '/ssl' /etc/dovecot/conf.d/10-ssl.conf | wc -l" + assert_success + assert_output 2 +} + +# +# logs +# + +@test "checking logs: /var/log/mail.log in mailserver_reverse is error free " { + run docker exec mailserver_reverse grep -i ': error:' /var/log/mail.log + assert_failure + run docker exec mailserver_reverse grep -i 'is not writable' /var/log/mail.log + assert_failure + run docker exec mailserver_reverse grep -i 'permission denied' /var/log/mail.log + assert_failure + run docker exec mailserver_reverse grep -i 'address already in use' /var/log/mail.log + assert_failure +} + +@test "checking logs: /var/log/mail.err in mailserver_reverse does not exist" { + run docker exec mailserver_reverse cat /var/log/mail.err + assert_failure + assert_output --partial 'No such file or directory' +} diff --git a/test/sieve.bats b/test/sieve.bats new file mode 100644 index 00000000..0a0965bf --- /dev/null +++ b/test/sieve.bats @@ -0,0 +1,18 @@ +load 'test_helper/bats-support/load' +load 'test_helper/bats-assert/load' + +# +# dovecot +# + +@test "checking dovecot: piped ham message with sieve" { + run docker exec mailserver_sieve /bin/sh -c "grep -i 'Debug: sieve: uid=2: pipe action: piped message to program.*rspamd-pipe-ham.sh' /var/log/mail.log | wc -l" + assert_success + assert_output 1 +} + +@test "checking dovecot: piped spam message with sieve" { + run docker exec mailserver_sieve /bin/sh -c "grep -i 'Debug: sieve: uid=1: pipe action: piped message to program.*rspamd-pipe-spam.sh' /var/log/mail.log | wc -l" + assert_success + assert_output 1 +} diff --git a/test/tests.bats b/test/tests.bats deleted file mode 100644 index f2dd39a3..00000000 --- a/test/tests.bats +++ /dev/null @@ -1,2172 +0,0 @@ -load 'test_helper/bats-support/load' -load 'test_helper/bats-assert/load' - -# -# system -# - -@test "checking system: /etc/mailname (docker method) (default)" { - run docker exec mailserver_default cat /etc/mailname - assert_success - assert_output "mail.domain.tld" -} - -@test "checking system: /etc/mailname (env method) (reverse)" { - run docker exec mailserver_reverse cat /etc/mailname - assert_success - assert_output "mail.domain.tld" -} - -@test "checking system: /etc/mailname (env method) (ldap)" { - run docker exec mailserver_ldap cat /etc/mailname - assert_success - assert_output "mail.domain.tld" -} - -@test "checking system: /etc/hostname" { - run docker exec mailserver_default cat /etc/hostname - assert_success - assert_output "mail.domain.tld" -} - -@test "checking system: /etc/hosts" { - run docker exec mailserver_default grep "mail.domain.tld" /etc/hosts - assert_success -} - -@test "checking system: fqdn" { - run docker exec mailserver_default hostname -f - assert_success - assert_output "mail.domain.tld" -} - -@test "checking system: domain" { - run docker exec mailserver_default hostname -d - assert_success - assert_output "domain.tld" -} - -@test "checking system: hostname" { - run docker exec mailserver_default hostname -s - assert_success - assert_output "mail" -} - -@test "checking system: all environment variables have been replaced (default configuration)" { - run docker exec mailserver_default /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd /etc/cron.d /etc/mailname /usr/local/bin" - assert_failure -} - -@test "checking system: all environment variables have been replaced (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd /etc/cron.d /etc/mailname /usr/local/bin" - assert_failure -} - -@test "checking system: all environment variables have been replaced (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd /etc/cron.d /etc/mailname /usr/local/bin" - assert_failure -} - -@test "checking system: all environment variables have been replaced (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd /etc/cron.d /etc/mailname /usr/local/bin" - assert_failure -} - -# -# processes (default configuration) -# - -@test "checking process: s6 (default configuration)" { - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-svscan /services'" - assert_success -} - -@test "checking process: rsyslog (default configuration)" { - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise rsyslogd'" - assert_success - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[r]syslogd -n -f /etc/rsyslog/rsyslog.conf'" - assert_success -} - -@test "checking process: cron (default configuration)" { - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise cron'" - assert_success - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[c]ron -f'" - assert_success -} - -@test "checking process: postfix (default configuration)" { - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise postfix'" - assert_success - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[/]usr/lib/postfix/sbin/master -s'" - assert_success -} - -@test "checking process: dovecot (default configuration)" { - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise dovecot'" - assert_success - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[/]usr/sbin/dovecot -F'" - assert_success -} - -@test "checking process: rspamd (default configuration)" { - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise rspamd'" - assert_success - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[r]spamd: main process'" - assert_success - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[r]spamd: rspamd_proxy process'" - assert_success - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[r]spamd: controller process'" - assert_success -} - -@test "checking process: clamd (default configuration)" { - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise clamd'" - assert_success - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[c]lamd'" - assert_success -} - -@test "checking process: freshclam (default configuration)" { - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise freshclam'" - assert_success - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[f]reshclam -d'" - assert_success -} - -@test "checking process: unbound (default configuration)" { - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise unbound'" - assert_success - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[u]nbound'" - assert_success -} - -@test "checking process: cert_watcher (default configuration)" { - run docker exec mailserver_default /bin/bash -c "ps aux --forest | grep '[s]6-supervise cert_watcher'" - assert_success -} - -# -# processes (reverse configuration) -# - -@test "checking process: s6 (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-svscan /services'" - assert_success -} - -@test "checking process: rsyslog (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise rsyslogd'" - assert_success - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[r]syslogd -n -f /etc/rsyslog/rsyslog.conf'" - assert_success -} - -@test "checking process: cron (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise cron'" - assert_success - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[c]ron -f'" - assert_success -} - -@test "checking process: postfix (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise postfix'" - assert_success - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[/]usr/lib/postfix/sbin/master -s'" - assert_success -} - -@test "checking process: dovecot (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise dovecot'" - assert_success - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[/]usr/sbin/dovecot -F'" - assert_success -} - -@test "checking process: rspamd (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise rspamd'" - assert_success - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[r]spamd: main process'" - assert_success - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[r]spamd: rspamd_proxy process'" - assert_success - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[r]spamd: controller process'" - assert_success -} - -@test "checking process: clamd (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise clamd'" - assert_success - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[c]lamd'" - assert_failure -} - -@test "checking process: freshclam (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise freshclam'" - assert_success - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[f]reshclam -d'" - assert_failure -} - -@test "checking process: unbound (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise unbound'" - assert_success - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[u]nbound'" - assert_failure -} - -@test "checking process: cert_watcher (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "ps aux --forest | grep '[s]6-supervise cert_watcher'" - assert_success -} - -# -# processes (ldap configuration) -# - -@test "checking process: s6 (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-svscan /services'" - assert_success -} - -@test "checking process: rsyslog (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise rsyslogd'" - assert_success - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[r]syslogd -n -f /etc/rsyslog/rsyslog.conf'" - assert_success -} - -@test "checking process: cron (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise cron'" - assert_success - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[c]ron -f'" - assert_success -} - -@test "checking process: postfix (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise postfix'" - assert_success - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[/]usr/lib/postfix/sbin/master -s'" - assert_success -} - -@test "checking process: dovecot (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise dovecot'" - assert_success - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[/]usr/sbin/dovecot -F'" - assert_success -} - -@test "checking process: rspamd (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise rspamd'" - assert_success - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[r]spamd: main process'" - assert_success - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[r]spamd: rspamd_proxy process'" - assert_success - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[r]spamd: controller process'" - assert_success -} - -@test "checking process: clamd (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise clamd'" - assert_success - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[c]lamd'" - assert_success - -} - -@test "checking process: freshclam (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise freshclam'" - assert_success - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[f]reshclam -d'" - assert_success -} - -@test "checking process: unbound (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise unbound'" - assert_success - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[u]nbound'" - assert_success -} - -@test "checking process: cert_watcher (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "ps aux --forest | grep '[s]6-supervise cert_watcher'" - assert_success -} - -# -# processes (ldap2 configuration) -# - -@test "checking process: s6 (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-svscan /services'" - assert_success -} - -@test "checking process: rsyslog (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise rsyslogd'" - assert_success - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[r]syslogd -n -f /etc/rsyslog/rsyslog.conf'" - assert_success -} - -@test "checking process: cron (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise cron'" - assert_success - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[c]ron -f'" - assert_success -} - -@test "checking process: postfix (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise postfix'" - assert_success - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[/]usr/lib/postfix/sbin/master -s'" - assert_success -} - -@test "checking process: dovecot (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise dovecot'" - assert_success - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[/]usr/sbin/dovecot -F'" - assert_success -} - -@test "checking process: rspamd (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise rspamd'" - assert_success - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[r]spamd: main process'" - assert_success - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[r]spamd: rspamd_proxy process'" - assert_success - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[r]spamd: controller process'" - assert_success -} - -@test "checking process: clamd (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise clamd'" - assert_success - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[c]lamd'" - assert_failure -} - -@test "checking process: freshclam (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise freshclam'" - assert_success - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[f]reshclam -d'" - assert_failure -} - -@test "checking process: unbound (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise unbound'" - assert_success - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep -v 's6' | grep '[u]nbound'" - assert_failure -} - -@test "checking process: cert_watcher (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/bash -c "ps aux --forest | grep '[s]6-supervise cert_watcher'" - assert_success -} - -# -# processes restarting -# - -@test "checking process: 10 cron tasks to reset all the process counters" { - run docker exec mailserver_default /bin/bash -c "cat /etc/cron.d/counters | wc -l" - assert_success - assert_output 10 - run docker exec mailserver_reverse /bin/bash -c "cat /etc/cron.d/counters | wc -l" - assert_success - assert_output 10 - run docker exec mailserver_ldap /bin/bash -c "cat /etc/cron.d/counters | wc -l" - assert_success - assert_output 10 -} - -@test "checking process: no service restarted (default configuration)" { - run docker exec mailserver_default cat /tmp/counters/_parent - assert_success - assert_output 0 - run docker exec mailserver_default cat /tmp/counters/clamd - assert_success - assert_output 0 - run docker exec mailserver_default cat /tmp/counters/cron - assert_success - assert_output 0 - run docker exec mailserver_default cat /tmp/counters/dovecot - assert_success - assert_output 0 - run docker exec mailserver_default cat /tmp/counters/freshclam - assert_success - assert_output 0 - run docker exec mailserver_default cat /tmp/counters/postfix - assert_success - assert_output 0 - run docker exec mailserver_default cat /tmp/counters/rspamd - assert_success - assert_output 0 - run docker exec mailserver_default cat /tmp/counters/rsyslogd - assert_success - assert_output 0 - run docker exec mailserver_default cat /tmp/counters/unbound - assert_success - assert_output 0 - run docker exec mailserver_default cat /tmp/counters/cert_watcher - assert_success - assert_output 0 -} - -@test "checking process: no service restarted (reverse configuration)" { - run docker exec mailserver_reverse cat /tmp/counters/_parent - assert_success - assert_output 0 - run docker exec mailserver_reverse cat /tmp/counters/clamd - assert_success - assert_output 0 - run docker exec mailserver_reverse cat /tmp/counters/cron - assert_success - assert_output 0 - run docker exec mailserver_reverse cat /tmp/counters/dovecot - assert_success - assert_output 0 - run docker exec mailserver_reverse cat /tmp/counters/freshclam - assert_success - assert_output 0 - run docker exec mailserver_reverse cat /tmp/counters/postfix - assert_success - assert_output 0 - run docker exec mailserver_reverse cat /tmp/counters/rspamd - assert_success - assert_output 0 - run docker exec mailserver_reverse cat /tmp/counters/rsyslogd - assert_success - assert_output 0 - run docker exec mailserver_reverse cat /tmp/counters/unbound - assert_success - assert_output 0 - run docker exec mailserver_reverse cat /tmp/counters/cert_watcher - assert_success - assert_output 0 -} - -@test "checking process: no service restarted (ldap configuration)" { - run docker exec mailserver_ldap cat /tmp/counters/_parent - assert_success - assert_output 0 - run docker exec mailserver_ldap cat /tmp/counters/clamd - assert_success - assert_output 0 - run docker exec mailserver_ldap cat /tmp/counters/cron - assert_success - assert_output 0 - run docker exec mailserver_ldap cat /tmp/counters/dovecot - assert_success - assert_output 0 - run docker exec mailserver_ldap cat /tmp/counters/freshclam - assert_success - assert_output 0 - run docker exec mailserver_ldap cat /tmp/counters/postfix - assert_success - assert_output 0 - run docker exec mailserver_ldap cat /tmp/counters/rspamd - assert_success - assert_output 0 - run docker exec mailserver_ldap cat /tmp/counters/rsyslogd - assert_success - assert_output 0 - run docker exec mailserver_ldap cat /tmp/counters/unbound - assert_success - assert_output 0 - run docker exec mailserver_ldap cat /tmp/counters/cert_watcher - assert_success - assert_output 0 -} - -# -# ports -# - -@test "checking port (25): external port listening (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 25" - assert_success -} - -@test "checking port (25): external port listening (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 25" - assert_success -} - -@test "checking port (25): external port listening (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 25" - assert_success -} - -@test "checking port (53): internal port listening (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 127.0.0.1 53" - assert_success -} - -@test "checking port (53): internal port listening (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 127.0.0.1 53" - assert_failure -} - -@test "checking port (53): internal port listening (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 127.0.0.1 53" - assert_success -} - -@test "checking port (110): external port closed (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 110" - assert_failure -} - -@test "checking port (110): external port listening (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 110" - assert_success -} - -@test "checking port (110): external port closed (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 110" - assert_failure -} - -@test "checking port (143): external port listening (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 143" - assert_success -} - -@test "checking port (143): external port listening (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 143" - assert_success -} - -@test "checking port (143): external port listening (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 143" - assert_success -} - -@test "checking port (465): external port listening (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 465" - assert_success -} - -@test "checking port (465): external port listening (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 465" - assert_success -} - -@test "checking port (465): external port listening (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 465" - assert_success -} - -@test "checking port (587): external port listening (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 587" - assert_success -} - -@test "checking port (587): external port listening (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 587" - assert_success -} - -@test "checking port (587): external port listening (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 587" - assert_success -} - -@test "checking port (993): external port listening (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 993" - assert_success -} - -@test "checking port (993): external port listening (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 993" - assert_success -} - -@test "checking port (993): external port listening (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 993" - assert_success -} - -@test "checking port (995): external port closed (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 995" - assert_failure -} - -@test "checking port (995): external port listening (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 995" - assert_success -} - -@test "checking port (995): external port closed (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 995" - assert_failure -} - -@test "checking port (3310): external port listening (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 3310" - assert_success -} - -@test "checking port (3310): external port closed (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 3310" - assert_failure -} - -@test "checking port (3310): external port closed (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 3310" - assert_success -} - -@test "checking port (4190): external port listening (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 4190" - assert_success -} - -@test "checking port (4190): external port closed (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 4190" - assert_failure -} - -@test "checking port (4190): external port closed (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 4190" - assert_success -} - -@test "checking port (8953): internal port listening (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 127.0.0.1 8953" - assert_success -} - -@test "checking port (8953): internal port listening (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 127.0.0.1 8953" - assert_failure -} - -@test "checking port (8953): internal port listening (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 127.0.0.1 8953" - assert_success -} - -@test "checking port (10025): internal port closed (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 127.0.0.1 10025" - assert_failure -} - -@test "checking port (10025): internal port listening (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 127.0.0.1 10025" - assert_success -} - -@test "checking port (10025): internal port closed (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 127.0.0.1 10025" - assert_failure -} - -@test "checking port (10026): internal port listening (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 127.0.0.1 10026" - assert_success -} - -@test "checking port (10026): internal port listening (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 127.0.0.1 10026" - assert_success -} - -@test "checking port (10026): internal port listening (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 127.0.0.1 10026" - assert_success -} - -@test "checking port (11332): external port listening (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 11332" - assert_success -} - -@test "checking port (11332): external port listening (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 11332" - assert_success -} - -@test "checking port (11332): external port listening (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 11332" - assert_success -} - -@test "checking port (11334): external port listening (default configuration)" { - run docker exec mailserver_default /bin/sh -c "nc -z 0.0.0.0 11334" - assert_success -} - -@test "checking port (11334): external port listening (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "nc -z 0.0.0.0 11334" - assert_success -} - -@test "checking port (11334): external port listening (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "nc -z 0.0.0.0 11334" - assert_success -} - -# -# sasl -# - -@test "checking sasl: dovecot auth with good password (default configuration)" { - run docker exec mailserver_default /bin/sh -c "doveadm auth test sarah.connor@domain.tld testpasswd12 | grep 'auth succeeded'" - assert_success -} - -@test "checking sasl: dovecot auth with good password (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "doveadm auth test sarah.connor@domain.tld testpasswd12 | grep 'auth succeeded'" - assert_success -} - -@test "checking sasl: dovecot auth with good password (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "doveadm auth test sarah.connor@domain.tld testpasswd12 | grep 'auth succeeded'" - assert_success -} - -@test "checking sasl: dovecot auth with bad password (default configuration)" { - run docker exec mailserver_default /bin/sh -c "doveadm auth test sarah.connor@domain.tld badpassword | grep 'auth failed'" - assert_success -} - -@test "checking sasl: dovecot auth with bad password (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "doveadm auth test sarah.connor@domain.tld badpassword | grep 'auth failed'" - assert_success -} - -@test "checking sasl: dovecot auth with good password (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/sh -c "doveadm auth test john.connor@domain.tld testpasswd2 | grep 'auth succeeded'" - assert_success -} - -@test "checking sasl: dovecot auth with bad password (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/sh -c "doveadm auth test john.connor@domain.tld badpassword | grep 'auth failed'" - assert_success -} - -@test "checking sasl: dovecot auth with good master password (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/sh -c "doveadm auth test john.doe@domain.tld*john.connor@domain.tld testpasswd2 | grep 'auth succeeded'" - assert_success -} - -@test "checking sasl: dovecot auth with bad master password (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/sh -c "doveadm auth test john.connor@domain.tld*john.doe@domain.tld testpasswd12 | grep 'auth failed'" - assert_success -} - -@test "checking sasl: dovecot auth with good non-master password (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/sh -c "doveadm auth test sarah.connor@domain.tld*john.doe@domain.tld testpasswd12 | grep 'auth failed'" - assert_success -} - -# -# smtp -# http://www.postfix.org/SASL_README.html#server_test -# - -# Base64 AUTH STRINGS -# AHNhcmFoLmNvbm5vckBkb21haW4udGxkAHRlc3RwYXNzd2QxMg== -# echo -ne '\000sarah.connor@domain.tld\000testpasswd12' | openssl base64 -# AHNhcmFoLmNvbm5vckBkb21haW4udGxkAGJhZHBhc3N3b3Jk -# echo -ne '\000sarah.connor@domain.tld\000badpassword' | openssl base64 -# c2FyYWguY29ubm9yQGRvbWFpbi50bGQ= -# echo -ne 'sarah.connor@domain.tld' | openssl base64 -# dGVzdHBhc3N3ZDEy -# echo -ne 'testpasswd12' | openssl base64 -# YmFkcGFzc3dvcmQ= -# echo -ne 'badpassword' | openssl base64 - -@test "checking smtp (25): STARTTLS AUTH PLAIN works with good password (default configuration)" { - run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:25 -starttls smtp < /tmp/tests/auth/smtp-auth-plain.txt 2>&1 | grep -i 'authentication successful'" - assert_success -} - -@test "checking smtp (25): STARTTLS AUTH PLAIN works with good password (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:25 -starttls smtp < /tmp/tests/auth/smtp-auth-plain.txt 2>&1 | grep -i 'authentication successful'" - assert_success -} - -@test "checking smtp (25): STARTTLS AUTH PLAIN works with good password (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:25 -starttls smtp < /tmp/tests/auth/smtp-auth-plain.txt 2>&1 | grep -i 'authentication successful'" - assert_success -} - -@test "checking smtp (25): STARTTLS AUTH PLAIN fails with bad password" { - run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:25 -starttls smtp < /tmp/tests/auth/smtp-auth-plain-wrong.txt 2>&1 | grep -i 'authentication failed'" - assert_success -} - -@test "checking smtp (25): clear auth disabled" { - run docker exec mailserver_default /bin/sh -c "nc -w 2 0.0.0.0 25 < /tmp/tests/auth/smtp-auth-plain.txt | grep -i 'authentication not enabled'" - assert_success -} - -@test "checking submission (587): STARTTLS AUTH LOGIN works with good password (default configuration)" { - run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/auth/smtp-auth-login.txt 2>&1 | grep -i 'authentication successful'" - assert_success -} - -@test "checking submission (587): STARTTLS AUTH LOGIN works with good password (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/auth/smtp-auth-login.txt 2>&1 | grep -i 'authentication successful'" - assert_success -} - -@test "checking submission (587): STARTTLS AUTH LOGIN fails with bad password" { - run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp < /tmp/tests/auth/smtp-auth-login-wrong.txt 2>&1 | grep -i 'authentication failed'" - assert_success -} - -@test "checking submission (587): Auth without STARTTLS fail" { - run docker exec mailserver_default /bin/sh -c "nc -w 2 0.0.0.0 587 < /tmp/tests/auth/smtp-auth-plain.txt | grep -i 'Must issue a STARTTLS command first'" - assert_success -} - -@test "checking smtps (465): SSL/TLS AUTH LOGIN works with good password (default configuration)" { - run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:465 < /tmp/tests/auth/smtp-auth-login.txt 2>&1 | grep -i 'authentication successful'" - assert_success -} - -@test "checking smtps (465): SSL/TLS AUTH LOGIN works with good password (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:465 < /tmp/tests/auth/smtp-auth-login.txt 2>&1 | grep -i 'authentication successful'" - assert_success -} - -@test "checking smtps (465): SSL/TLS AUTH LOGIN works with good password (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:465 < /tmp/tests/auth/smtp-auth-login.txt 2>&1 | grep -i 'authentication successful'" - assert_success -} - -@test "checking smtps (465): SSL/TLS AUTH LOGIN fails with bad password" { - run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:465 < /tmp/tests/auth/smtp-auth-login-wrong.txt 2>&1 | grep -i 'authentication failed'" - assert_success -} - -@test "checking smtp: john.doe should have received 4 mails (internal + external + subaddress + hostmaster alias) (default configuration)" { - run docker exec mailserver_default /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/john.doe/mail/new/ | wc -l" - assert_success - assert_output 4 -} - -@test "checking smtp: john.doe should have received 4 mails (internal + external + subaddress + hostmaster alias) (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/john.doe/subdir/new/ | wc -l" - assert_success - assert_output 4 -} - -@test "checking smtp: john.doe should have received 6 mails (internal + external + subaddress + hostmaster_forward + postmaster_alias + group_alias) (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/john.doe/mail/new/ | wc -l" - assert_success - assert_output 6 -} - -@test "checking smtp: john.doe should have received 4 mails (internal + external + subaddress + postmaster_alias) (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/john.doe/mail/new/ | wc -l" - assert_success - assert_output 4 -} - -@test "checking smtp: sarah.connor should have received 1 mail (internal spam-ham test) (default configuration)" { - run docker exec mailserver_default /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/sarah.connor/mail/new/ | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: sarah.connor should have received 1 mail (internal spam-ham test) (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/sarah.connor/mail/new/ | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: sarah.connor should have received 1 spam (with manual IMAP COPY to Spam folder) (default configuration)" { - run docker exec mailserver_default /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/sarah.connor/mail/.Spam/cur/ | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: sarah.connor should have received 1 spam (with manual IMAP COPY to Spam folder) (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/sarah.connor/mail/.Spam/cur/ | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: john.connor should have received 2 mails (hostmaster_forward + postmaster_alias) (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/john.connor/mail/new/ | wc -l" - assert_success - assert_output 2 -} - -@test "checking smtp: john.connor should have received 1 mails (postmaster_alias) (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/sh -c "ls -A /var/mail/vhosts/domain.tld/john.connor/mail/new/ | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: rejects mail to unknown user (default configuration)" { - run docker exec mailserver_default /bin/sh -c "grep ': Recipient address rejected: User unknown in virtual mailbox table' /var/log/mail.log | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: rejects mail to unknown user (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "grep ': Recipient address rejected: User unknown in virtual mailbox table' /var/log/mail.log | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: rejects mail to unknown user (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "grep ': Recipient address rejected: User unknown in virtual mailbox table' /var/log/mail.log | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: delivers mail to existing alias (default configuration)" { - run docker exec mailserver_default /bin/sh -c "grep 'to=, orig_to=' /var/log/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: delivers mail to existing alias (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "grep 'to=, orig_to=' /var/log/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: delivers mail to existing forward (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "grep 'to=, orig_to=' /var/log/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: delivers mail to existing alias (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "grep 'to=, orig_to=' /var/log/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: delivers mail to existing forward (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "grep 'to=, orig_to=' /var/log/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: delivers mail to existing alias (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "grep 'to=, orig_to=' /var/log/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: delivers mail to existing alias (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/sh -c "grep 'to=, orig_to=' /var/log/mail.log | grep 'status=sent' | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: rejects mail to unknown forward (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/sh -c "grep ': Recipient address rejected: User unknown in virtual mailbox table' /var/log/mail.log | grep 'NOQUEUE: reject' | wc -l" - assert_success - assert_output 1 -} - -@test "checking smtp: rejects mail to unknown group (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/sh -c "grep ': Recipient address rejected: User unknown in virtual mailbox table' /var/log/mail.log | grep 'NOQUEUE: reject' | wc -l" - assert_success - assert_output 1 -} - -# -# imap -# - -@test "checking imap (143): STARTTLS login works with good password (default configuration)" { - run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:143 -starttls imap < /tmp/tests/auth/imap-auth.txt 2>&1 | grep -i 'logged in'" - assert_success -} - -@test "checking imap (143): STARTTLS login works with good password (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:143 -starttls imap < /tmp/tests/auth/imap-auth.txt 2>&1 | grep -i 'logged in'" - assert_success -} - -@test "checking imap (143): STARTTLS login works with good password (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:143 -starttls imap < /tmp/tests/auth/imap-auth.txt 2>&1 | grep -i 'logged in'" - assert_success -} - -@test "checking imap (143): STARTTLS login fails with bad password" { - run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:143 -starttls imap < /tmp/tests/auth/imap-auth-wrong.txt 2>&1 | grep -i 'authentication failed'" - assert_success -} - -@test "checking imaps (993): SSL/TLS login works with good password (default configuration)" { - run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/auth/imap-auth.txt 2>&1 | grep -i 'logged in'" - assert_success -} - -@test "checking imaps (993): SSL/TLS login works with good password (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/auth/imap-auth.txt 2>&1 | grep -i 'logged in'" - assert_success -} - -@test "checking imaps (993): SSL/TLS login works with good password (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/auth/imap-auth.txt 2>&1 | grep -i 'logged in'" - assert_success -} - -@test "checking imaps (993): SSL/TLS login fails with bad password" { - run docker exec mailserver_default /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/auth/imap-auth-wrong.txt 2>&1 | grep -i 'authentication failed'" - assert_success -} - -@test "checking imaps (993): SSL/TLS login fails with good master password on no master config (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/auth/imap-auth-master.txt 2>&1 | grep -i 'authentication failed'" - assert_success -} - -@test "checking imaps (993): SSL/TLS login works with good master password (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/auth/imap-auth-master.txt 2>&1 | grep -i 'logged in'" - assert_success -} - -@test "checking imaps (993): SSL/TLS login fails with bad password (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:993 < /tmp/tests/auth/imap-auth-master-wrong.txt 2>&1 | grep -i 'authentication failed'" - assert_success -} - -# -# pop -# - -@test "checking pop3 (110): STARTTLS login works with good password" { - run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:110 -starttls pop3 < /tmp/tests/auth/pop3-auth.txt 2>&1 | grep -i 'ok logged in'" - assert_success -} - -@test "checking pop3 (110): STARTTLS login fails with bad password" { - run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:110 -starttls pop3 < /tmp/tests/auth/pop3-auth-wrong.txt 2>&1 | grep -i 'authentication failed'" - assert_success -} - -@test "checking pop3s (995): SSL/TLS login works with good password" { - run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:995 < /tmp/tests/auth/pop3-auth.txt 2>&1 | grep -i 'ok logged in'" - assert_success -} - -@test "checking pop3s (995): SSL/TLS login fails with bad password" { - run docker exec mailserver_reverse /bin/sh -c "openssl s_client -ign_eof -connect 0.0.0.0:995 < /tmp/tests/auth/pop3-auth-wrong.txt 2>&1 | grep -i 'authentication failed'" - assert_success -} - -# rspamd - -@test "checking rspamd: spam filtered (default configuration)" { - run docker exec mailserver_default /bin/sh -c "grep -i 'Gtube pattern; from= to= ' /var/log/mail.log | wc -l" - assert_success - assert_output 1 -} - -@test "checking rspamd: spam filtered (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "grep -i 'Gtube pattern; from= to= ' /var/log/mail.log | wc -l" - assert_success - assert_output 1 -} - -@test "checking rspamd: spam filtered (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "grep -i 'Gtube pattern; from= to= ' /var/log/mail.log | wc -l" - assert_success - assert_output 1 -} - -@test "checking rspamd: existing rrd file" { - run docker exec mailserver_default [ -f /var/mail/rspamd/rspamd.rrd ] - assert_success -} - -@test "checking rspamd: 7 messages scanned" { - run docker exec mailserver_default /bin/sh -c "rspamc stat | grep -i 'Messages scanned: 7'" - assert_success -} - -@test "checking rspamd: 5 messages with action no action" { - run docker exec mailserver_default /bin/sh -c "rspamc stat | grep -i 'Messages with action no action: 5'" - assert_success -} - -@test "checking rspamd: 2 messages with action reject" { - run docker exec mailserver_default /bin/sh -c "rspamc stat | grep -i 'Messages with action reject: 2'" - assert_success -} - -@test "checking rspamd: 2 messages learned" { - run docker exec mailserver_default /bin/sh -c "rspamc stat | grep -i 'Messages learned: 2'" - assert_success -} - -@test "checking rspamd: dkim/arc signing is disabled (reverse configuration)" { - run docker exec mailserver_reverse cat /etc/rspamd/local.d/arc.conf - assert_success - assert_output "enabled = false;" - run docker exec mailserver_reverse cat /etc/rspamd/local.d/dkim_signing.conf - assert_success - assert_output "enabled = false;" -} - -@test "checking rspamd: greylisting policy is disabled (reverse configuration)" { - run docker exec mailserver_reverse cat /etc/rspamd/local.d/greylisting.conf - assert_success - assert_output "enabled = false;" -} - -@test "checking rspamd: ratelimiting policy is disabled (reverse configuration)" { - run docker exec mailserver_reverse cat /etc/rspamd/local.d/ratelimit.conf - assert_success - assert_output "enabled = false;" -} - -@test "checking rspamd: 3 modules disabled in ecdsa configuration" { - run docker exec mailserver_ecdsa cat /etc/rspamd/local.d/rbl.conf - assert_success - assert_output "enabled = false;" - run docker exec mailserver_ecdsa cat /etc/rspamd/local.d/mx_check.conf - assert_success - assert_output "enabled = false;" - run docker exec mailserver_ecdsa cat /etc/rspamd/local.d/url_redirector.conf - assert_success - assert_output "enabled = false;" -} - -@test "checking rspamd: 2 addresses whitelisted in ecdsa configuration" { - run docker exec mailserver_ecdsa /bin/bash -c "grep '\"test@example.com\",\"another@domain.tld\"' /etc/rspamd/local.d/settings.conf | wc -l" - assert_success - assert_output 1 -} - -@test "checking rspamd: 1 address whitelisted in default configuration" { - run docker exec mailserver_default /bin/bash -c "grep 'postmaster@domain.tld' /etc/rspamd/local.d/settings.conf | wc -l" - assert_success - assert_output 1 -} - -@test "checking rspamd: debug mode disabled (default configuration)" { - run docker exec mailserver_default /bin/sh -c 'rspamadm configdump | grep -E "level = \"warning\";"' - assert_success -} - -@test "checking rspamd: debug mode disabled (traefik_acmev1 configuration)" { - run docker exec mailserver_traefik_acmev1 /bin/sh -c 'rspamadm configdump | grep -E "level = \"warning\";"' - assert_success -} - -@test "checking rspamd: debug mode enabled (traefik_acmev2 configuration)" { - run docker exec mailserver_traefik_acmev2 /bin/sh -c 'rspamadm configdump | grep -E "level = \"info\";"' - assert_success -} - -# -# accounts -# - -@test "checking accounts: user accounts (default configuration)" { - run docker exec mailserver_default doveadm user '*' - assert_success - [ "${lines[0]}" = "john.doe@domain.tld" ] - [ "${lines[1]}" = "sarah.connor@domain.tld" ] -} - -@test "checking accounts: user accounts (reverse configuration)" { - run docker exec mailserver_reverse doveadm user '*' - assert_success - [ "${lines[0]}" = "john.doe@domain.tld" ] - [ "${lines[1]}" = "sarah.connor@domain.tld" ] -} - -@test "checking accounts: user accounts (ldap configuration)" { - run docker exec mailserver_ldap doveadm user '*' - assert_success - [ "${lines[0]}" = "john.doe@domain.tld" ] - [ "${lines[1]}" = "sarah.connor@domain.tld" ] - [ "${lines[2]}" = "john.connor@domain.tld" ] -} - -@test "checking accounts: user accounts (ldap2 configuration)" { - run docker exec mailserver_ldap doveadm user '*' - assert_success - [ "${lines[0]}" = "john.doe@domain.tld" ] - [ "${lines[1]}" = "sarah.connor@domain.tld" ] - [ "${lines[2]}" = "john.connor@domain.tld" ] -} - -@test "checking accounts: user quotas (default configuration)" { - run docker exec mailserver_default /bin/bash -c "doveadm quota get -A 2>&1 | grep '1000' | wc -l" - assert_success - assert_output 2 -} - -@test "checking accounts: user quotas (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "doveadm quota get -A 2>&1 | grep '1000' | wc -l" - assert_success - assert_output 2 -} - -@test "checking accounts: user quotas (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "doveadm quota get -A 2>&1 | grep '1000' | wc -l" - assert_success - assert_output 1 - run docker exec mailserver_ldap /bin/bash -c "doveadm quota get -A 2>&1 | grep '2000' | wc -l" - assert_success - assert_output 1 - run docker exec mailserver_ldap /bin/bash -c "doveadm quota get -A 2>&1 | grep '4000' | wc -l" - assert_success - assert_output 1 -} - -@test "checking accounts: user mail folders for john.doe" { - run docker exec mailserver_default /bin/bash -c "ls -A /var/mail/vhosts/domain.tld/john.doe/mail/ | grep -E 'cur|new|tmp' | wc -l" - assert_success - assert_output 3 -} - -@test "checking accounts: user mail folders for sarah.connor" { - run docker exec mailserver_default /bin/bash -c "ls -A /var/mail/vhosts/domain.tld/sarah.connor/mail/ | grep -E '.Spam|cur|new|subscriptions|tmp' | wc -l" - assert_success - assert_output 5 -} - -# -# dkim -# - -@test "checking dkim: all key pairs are generated (default configuration)" { - run docker exec mailserver_default /bin/bash -c "ls -A /var/mail/dkim/*/mail.{private.key,public.key} | wc -l" - assert_success - assert_output 6 -} - -@test "checking dkim: all key pairs are generated (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "ls -A /var/mail/dkim/*/other.{private.key,public.key} | wc -l" - assert_success - assert_output 2 -} - -@test "checking dkim: all key pairs are generated (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "ls -A /var/mail/dkim/*/mail20190101.{private.key,public.key} | wc -l" - assert_success - assert_output 6 -} - -@test "checking dkim: control the size of the RSA key pair (4096bits)" { - run docker exec mailserver_reverse /bin/bash -c "openssl rsa -in /var/mail/dkim/domain.tld/other.private.key -text -noout | grep -i 'Private-Key: (4096 bit, 2 primes)'" - assert_success -} - -# -# postfix -# - -@test "checking postfix: mynetworks value (default configuration)" { - run docker exec mailserver_default /bin/sh -c "postconf -h mynetworks" - assert_success - assert_output "127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128" -} - -@test "checking postfix: mynetworks value (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "postconf -h mynetworks" - assert_success - assert_output "127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8" -} - -@test "checking postfix: mynetworks value (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "postconf -h mynetworks" - assert_success - assert_output "127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128" -} - -@test "checking postfix: main.cf overrides" { - run docker exec mailserver_default /bin/sh -c "postconf -h max_idle" - assert_success - assert_output "600s" - - run docker exec mailserver_default /bin/sh -c "postconf -h readme_directory" - assert_success - assert_output "/tmp" -} - -@test "checking postfix: headers cleanup" { - run docker exec mailserver_default /bin/sh -c "grep -i 'replace: header Received' /var/log/mail.log | wc -l" - assert_success - assert_output 1 -} - -@test "checking postfix: myorigin value (docker method)" { - run docker exec mailserver_default postconf -h myorigin - assert_success - assert_output "mail.domain.tld" -} - -@test "checking postfix: myorigin value (env method)" { - run docker exec mailserver_reverse postconf -h myorigin - assert_success - assert_output "mail.domain.tld" -} - -@test "checking postfix: two milter rejects (GTUBE + EICAR)" { - run docker exec mailserver_default /bin/sh -c "grep -i 'milter-reject' /var/log/mail.log | wc -l" - assert_success - assert_output 2 -} - -@test "checking postfix: milter-reject - clamav virus found" { - run docker exec mailserver_default grep -i 'milter-reject.*virus found: "{HEX}EICAR.TEST.3.UNOFFICIAL"; from=' /var/log/mail.log - assert_success -} - -@test "checking postfix: check 'etc' files in queue directory" { - run docker exec mailserver_default [ -f /var/mail/postfix/spool/etc/services ] - assert_success - run docker exec mailserver_default [ -f /var/mail/postfix/spool/etc/hosts ] - assert_success - run docker exec mailserver_default [ -f /var/mail/postfix/spool/etc/localtime ] - assert_success -} - -@test "checking postfix: check some folders in queue directory" { - run docker exec mailserver_default [ -d /var/mail/postfix/spool/usr/lib/sasl2 ] - assert_success - run docker exec mailserver_default [ -d /var/mail/postfix/spool/usr/lib/zoneinfo ] - assert_success -} - -@test "checking postfix: check dovecot unix sockets in queue directory" { - run docker exec mailserver_default [ -S /var/mail/postfix/spool/private/dovecot-lmtp ] - assert_success - run docker exec mailserver_default [ -S /var/mail/postfix/spool/private/auth ] - assert_success -} - -@test "checking postfix: check group of 'public' and 'maildrop' folders in queue directory" { - run docker exec mailserver_default /bin/sh -c "stat -c '%G' /var/mail/postfix/spool/public" - assert_success - assert_output "postdrop" - run docker exec mailserver_default /bin/sh -c "stat -c '%G' /var/mail/postfix/spool/maildrop" - assert_success - assert_output "postdrop" -} - -@test "checking postfix: smtp_tls_security_level value (default configuration)" { - run docker exec mailserver_default postconf -h smtp_tls_security_level - assert_success - assert_output "dane" -} - -@test "checking postfix: smtp_tls_security_level value (reverse configuration)" { - run docker exec mailserver_reverse postconf -h smtp_tls_security_level - assert_success - assert_output "may" -} - -@test "checking postfix: smtp_tls_security_level value (ldap configuration)" { - run docker exec mailserver_ldap postconf -h smtp_tls_security_level - assert_success - assert_output "dane" -} - -@test "checking postfix: smtp_dns_support_level value (default configuration)" { - run docker exec mailserver_default postconf -h smtp_dns_support_level - assert_success - assert_output "dnssec" -} - -@test "checking postfix: smtp_dns_support_level value (reverse configuration)" { - run docker exec mailserver_reverse postconf -h smtp_dns_support_level - assert_success - assert_output "" -} - -@test "checking postfix: smtp_dns_support_level value (ldap configuration)" { - run docker exec mailserver_ldap postconf -h smtp_dns_support_level - assert_success - assert_output "dnssec" -} - -@test "checking postfix: smtpd_sender_login mysql maps (default configuration)" { - run docker exec mailserver_default /bin/sh -c "postconf -h smtpd_sender_login_maps | grep 'mysql'" - assert_success -} - -@test "checking postfix: smtpd_sender_login pgsql maps (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "postconf -h smtpd_sender_login_maps | grep 'pgsql'" - assert_success -} - -@test "checking postfix: smtpd_sender_login ldap maps (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "postconf -h smtpd_sender_login_maps | grep 'ldap'" - assert_success -} - -@test "checking postfix: verbose mode disabled (default configuration)" { - run docker exec mailserver_default /bin/sh -c "grep 'smtpd -v' /etc/postfix/master.cf | wc -l" - assert_success - assert_output 0 -} - -@test "checking postfix: verbose mode enabled (traefik_acmev1 configuration)" { - run docker exec mailserver_traefik_acmev1 /bin/sh -c "grep 'smtpd -v' /etc/postfix/master.cf | wc -l" - assert_success - assert_output 3 -} - -@test "checking postfix: verbose mode enabled (traefik_acmev2 configuration)" { - run docker exec mailserver_traefik_acmev2 /bin/sh -c "grep 'smtpd -v' /etc/postfix/master.cf | wc -l" - assert_success - assert_output 3 -} - -@test "checking postfix: master.cf custom service parameter" { - run docker exec mailserver_default postconf -P submission/inet/syslog_name - assert_success - assert_output "submission/inet/syslog_name = postfix/submission-custom" -} - -@test "checking postfix: sender access reject john.doe" { - run docker exec mailserver_default grep -i ': Sender address rejected: Access denied' /var/log/mail.log - assert_success -} - -# -# dovecot -# - -@test "checking dovecot: existing instances file" { - run docker exec mailserver_default [ -f /var/mail/dovecot/instances ] - assert_success -} - -@test "checking dovecot: default lib directory is a symlink" { - run docker exec mailserver_default [ -L /var/lib/dovecot ] - assert_success -} - -@test "checking dovecot: password scheme is correct" { - run docker exec mailserver_default /bin/sh -c "grep 'SHA512-CRYPT' /etc/dovecot/dovecot-sql.conf.ext | wc -l" - assert_success - assert_output 1 -} - -@test "checking dovecot: piped ham message with sieve" { - run docker exec mailserver_default /bin/sh -c "grep -i 'sieve: pipe action: piped message to program.*rspamd-pipe-ham.sh' /var/log/mail.log | wc -l" - assert_success - assert_output 1 -} - -@test "checking dovecot: piped spam message with sieve" { - run docker exec mailserver_default /bin/sh -c "grep -i 'sieve: pipe action: piped message to program.*rspamd-pipe-spam.sh' /var/log/mail.log | wc -l" - assert_success - assert_output 1 -} - -@test "checking dovecot: custom sieve file is used" { - run docker exec mailserver_reverse /bin/sh -c "wc -l < /var/mail/sieve/default.sieve" - assert_success - assert_output 4 -} - -@test "checking dovecot: login_greeting value (default configuration)" { - run docker exec mailserver_default /bin/sh -c "doveconf -h login_greeting 2>/dev/null" - assert_success - assert_output "Do. Or do not. There is no try." -} - -@test "checking dovecot: login_greeting value (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "doveconf -h login_greeting 2>/dev/null" - assert_success - assert_output "Dovecot (Debian) ready." -} - -@test "checking dovecot: login_greeting value (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "doveconf -h login_greeting 2>/dev/null" - assert_success - assert_output "Do. Or do not. There is no try." -} - -@test "checking dovecot: mail_max_userip_connections imap value" { - run docker exec mailserver_default /bin/sh -c "doveconf -h -f protocol=imap mail_max_userip_connections 2>/dev/null" - assert_success - assert_output "100" -} - -@test "checking dovecot: mail_max_userip_connections pop3 value" { - run docker exec mailserver_default /bin/sh -c "doveconf -h -f protocol=pop3 mail_max_userip_connections 2>/dev/null" - assert_success - assert_output "50" -} - -@test "checking dovecot: quota dict mysql (default configuration)" { - run docker exec mailserver_default /bin/sh -c "doveconf dict sqlquota 2>/dev/null | grep 'mysql'" - assert_success -} - -@test "checking dovecot: quota dict pgsql (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "doveconf dict sqlquota 2>/dev/null | grep 'pgsql'" - assert_success -} - -@test "checking dovecot: debug mode disabled (default configuration)" { - run docker exec mailserver_default /bin/sh -c "doveconf -h auth_verbose 2>/dev/null" - assert_success - assert_output "no" - run docker exec mailserver_default /bin/sh -c "doveconf -h auth_verbose_passwords 2>/dev/null" - assert_success - assert_output "no" - run docker exec mailserver_default /bin/sh -c "doveconf -h auth_debug 2>/dev/null" - assert_success - assert_output "no" - run docker exec mailserver_default /bin/sh -c "doveconf -h auth_debug_passwords 2>/dev/null" - assert_success - assert_output "no" - run docker exec mailserver_default /bin/sh -c "doveconf -h mail_debug 2>/dev/null" - assert_success - assert_output "no" - run docker exec mailserver_default /bin/sh -c "doveconf -h verbose_ssl 2>/dev/null" - assert_success - assert_output "no" -} - -@test "checking dovecot: debug mode disabled (ldap configuration)" { - run docker exec mailserver_ldap /bin/sh -c "doveconf -h auth_verbose 2>/dev/null" - assert_success - assert_output "no" - run docker exec mailserver_ldap /bin/sh -c "doveconf -h auth_verbose_passwords 2>/dev/null" - assert_success - assert_output "no" - run docker exec mailserver_ldap /bin/sh -c "doveconf -h auth_debug 2>/dev/null" - assert_success - assert_output "no" - run docker exec mailserver_ldap /bin/sh -c "doveconf -h auth_debug_passwords 2>/dev/null" - assert_success - assert_output "no" - run docker exec mailserver_ldap /bin/sh -c "doveconf -h mail_debug 2>/dev/null" - assert_success - assert_output "no" - run docker exec mailserver_ldap /bin/sh -c "doveconf -h verbose_ssl 2>/dev/null" - assert_success - assert_output "no" -} - -@test "checking dovecot: debug mode enabled (traefik_acmev1 configuration)" { - run docker exec mailserver_traefik_acmev1 /bin/sh -c "doveconf -h auth_verbose 2>/dev/null" - assert_success - assert_output "yes" - run docker exec mailserver_traefik_acmev1 /bin/sh -c "doveconf -h auth_verbose_passwords 2>/dev/null" - assert_success - assert_output "sha1" - run docker exec mailserver_traefik_acmev1 /bin/sh -c "doveconf -h auth_debug 2>/dev/null" - assert_success - assert_output "yes" - run docker exec mailserver_traefik_acmev1 /bin/sh -c "doveconf -h auth_debug_passwords 2>/dev/null" - assert_success - assert_output "yes" - run docker exec mailserver_traefik_acmev1 /bin/sh -c "doveconf -h mail_debug 2>/dev/null" - assert_success - assert_output "yes" - run docker exec mailserver_traefik_acmev1 /bin/sh -c "doveconf -h verbose_ssl 2>/dev/null" - assert_success - assert_output "yes" -} - -@test "checking dovecot: debug mode enabled (traefik_acmev2 configuration)" { - run docker exec mailserver_traefik_acmev2 /bin/sh -c "doveconf -h auth_verbose 2>/dev/null" - assert_success - assert_output "yes" - run docker exec mailserver_traefik_acmev2 /bin/sh -c "doveconf -h auth_verbose_passwords 2>/dev/null" - assert_success - assert_output "sha1" - run docker exec mailserver_traefik_acmev2 /bin/sh -c "doveconf -h auth_debug 2>/dev/null" - assert_success - assert_output "yes" - run docker exec mailserver_traefik_acmev2 /bin/sh -c "doveconf -h auth_debug_passwords 2>/dev/null" - assert_success - assert_output "yes" - run docker exec mailserver_traefik_acmev2 /bin/sh -c "doveconf -h mail_debug 2>/dev/null" - assert_success - assert_output "yes" - run docker exec mailserver_traefik_acmev2 /bin/sh -c "doveconf -h verbose_ssl 2>/dev/null" - assert_success - assert_output "yes" -} - -# -# clamav -# - -@test "checking clamav: TCP Bound to 3310 port" { - run docker exec mailserver_default grep -i 'TCP: Bound to \[0.0.0.0\]:3310' /var/log/mail.log - assert_success -} - -@test "checking clamav: self checking every 3600 seconds" { - run docker exec mailserver_default grep -i 'clamd\[.*\]: Self checking every 3600 seconds' /var/log/mail.log - assert_success -} - -@test "checking clamav: default lib directory is a symlink" { - run docker exec mailserver_default [ -L /var/lib/clamav ] - assert_success -} - -@test "checking clamav: Eicar-Test-Signature FOUND" { - run docker exec mailserver_default grep -i '(44d88612fea8a8f36de82e1278abb02f:68) FOUND' /var/log/mail.log - assert_success -} - -@test "checking clamav: 6 database mirrors" { - run docker exec mailserver_default /bin/sh -c "grep 'DatabaseMirror' /etc/clamav/freshclam.conf | wc -l" - assert_success - assert_output 6 -} - -# -# clamav-unofficial-sigs -# - -@test "checking clamav-unofficial-sigs: rsync command exist" { - run docker exec mailserver_default /bin/sh -c "command -v rsync" - assert_success - assert_output "/usr/bin/rsync" -} - -@test "checking clamav-unofficial-sigs: curl command exist" { - run docker exec mailserver_default /bin/sh -c "command -v curl" - assert_success - assert_output "/usr/bin/curl" -} - -@test "checking clamav-unofficial-sigs: clamscan command exist" { - run docker exec mailserver_default /bin/sh -c "command -v clamscan" - assert_success - assert_output "/usr/bin/clamscan" -} - -@test "checking clamav-unofficial-sigs: cron task exist" { - run docker exec mailserver_default [ -f /etc/cron.d/clamav-unofficial-sigs ] - assert_success -} - -@test "checking clamav-unofficial-sigs: logrotate task exist" { - run docker exec mailserver_default [ -f /etc/logrotate.d/clamav-unofficial-sigs ] - assert_success -} - -@test "checking clamav-unofficial-sigs: cron task doesn't exist (reverse configuration)" { - run docker exec mailserver_reverse [ -f /etc/cron.d/clamav-unofficial-sigs ] - assert_failure -} - -@test "checking clamav-unofficial-sigs: logrotate task doesn't exist (reverse configuration)" { - run docker exec mailserver_reverse [ -f /etc/logrotate.d/clamav-unofficial-sigs ] - assert_failure -} - -# @test "checking clamav-unofficial-sigs: TEST 1 — Html.Sanesecurity.TestSig_Type3_Bdy" { -# run docker exec mailserver_default /bin/sh -c "clamscan --database=/var/lib/clamav/phish.ndb - < /tmp/tests/clamav/test1.eml" -# assert_failure -# assert_output --partial "Sanesecurity.TestSig_Type3_Bdy.4.UNOFFICIAL FOUND" -# } - -# @test "checking clamav-unofficial-sigs: TEST 2 — Email.Sanesecurity.TestSig_Type4_Hdr" { -# run docker exec mailserver_default /bin/sh -c "clamscan --database=/var/lib/clamav/phish.ndb - < /tmp/tests/clamav/test2.eml" -# assert_failure -# assert_output --partial "Sanesecurity.TestSig_Type4_Hdr.2.UNOFFICIAL FOUND" -# } - -# @test "checking clamav-unofficial-sigs: TEST 3 — Email.Sanesecurity.TestSig_Type4_Bdy" { -# run docker exec mailserver_default /bin/sh -c "clamscan --database=/var/lib/clamav/phish.ndb - < /tmp/tests/clamav/test3.eml" -# assert_failure -# assert_output --partial "Sanesecurity.TestSig_Type4_Bdy.3.UNOFFICIAL FOUND" -# } - -# -# zeyple -# - -@test "checking zeyple: 4 messages delivered via zeyple service" { - run docker exec mailserver_reverse /bin/sh -c "grep -i 'delivered via zeyple service' /var/log/mail.log | wc -l" - assert_success - assert_output 4 -} - -@test "checking zeyple: 'processing outgoing message' 4 times in logs" { - run docker exec mailserver_reverse /bin/sh -c "grep -i 'Processing outgoing message' /var/log/zeyple.log | wc -l" - assert_success - assert_output 4 -} - -@test "checking zeyple: zeyple.py exist (reverse configuration)" { - run docker exec mailserver_reverse [ -f /usr/local/bin/zeyple.py ] - assert_success -} - -@test "checking zeyple: zeyple.log exist (reverse configuration)" { - run docker exec mailserver_reverse [ -f /var/log/zeyple.log ] - assert_success -} - -@test "checking zeyple: zeyple.log doesn't exist (default configuration)" { - run docker exec mailserver_default [ -f /var/log/zeyple.log ] - assert_failure -} - -@test "checking zeyple: pubring.kbx exist (reverse configuration)" { - run docker exec mailserver_reverse [ -f /var/mail/zeyple/keys/pubring.kbx ] - assert_success -} - -@test "checking zeyple: pubring.kbx doesn't exist (default configuration)" { - run docker exec mailserver_default [ -f /var/mail/zeyple/keys/pubring.kbx ] - assert_failure -} - -@test "checking zeyple: trustdb.gpg exist (reverse configuration)" { - run docker exec mailserver_reverse [ -f /var/mail/zeyple/keys/trustdb.gpg ] - assert_success -} - -@test "checking zeyple: trustdb.gpg doesn't exist (default configuration)" { - run docker exec mailserver_default [ -f /var/mail/zeyple/keys/trustdb.gpg ] - assert_failure -} - -@test "checking zeyple: content_filter value (default configuration)" { - run docker exec mailserver_default /bin/sh -c "postconf -h content_filter" - assert_success - assert_output "" -} - -@test "checking zeyple: content_filter value (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "postconf -h content_filter" - assert_success - assert_output "zeyple" -} - -@test "checking zeyple: user zeyple doesn't exist (default configuration)" { - run docker exec mailserver_default /bin/sh -c "id -u zeyple" - assert_failure -} - -@test "checking zeyple: user zeyple exist (reverse configuration)" { - run docker exec mailserver_reverse /bin/sh -c "id -u zeyple" - assert_success -} - -@test "checking zeyple: retrieve john doe gpg key in public keyring" { - run docker exec mailserver_reverse /bin/sh -c "s6-setuidgid zeyple gpg --homedir /var/mail/zeyple/keys --with-colons --list-keys | grep 'John Doe (test key) ' | wc -l" - assert_success - assert_output 1 -} - -@test "checking zeyple: retrieve john doe gpg key in public keyring (using custom script)" { - run docker exec mailserver_reverse /bin/sh -c "encryption.sh --with-colons --list-keys | grep 'John Doe (test key) ' | wc -l" - assert_success - assert_output 1 -} - -@test "checking zeyple: 3 emails encrypted in john.doe folder" { - run docker exec mailserver_reverse /bin/sh -c "gzip -cd /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | grep -i 'multipart/encrypted' | wc -l" - assert_success - assert_output 3 - run docker exec mailserver_reverse /bin/sh -c "gzip -cd /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | grep -i 'BEGIN PGP MESSAGE' | wc -l" - assert_success - assert_output 3 - run docker exec mailserver_reverse /bin/sh -c "gzip -cd /var/mail/vhosts/domain.tld/john.doe/subdir/new/* | grep -i 'END PGP MESSAGE' | wc -l" - assert_success - assert_output 3 -} - -# -# unbound -# - -@test "checking unbound: /etc/resolv.conf (default configuration)" { - run docker exec mailserver_default cat /etc/resolv.conf - assert_success - assert_output "nameserver 127.0.0.1" -} - -@test "checking unbound: /etc/resolv.conf (reverse configuration)" { - run docker exec mailserver_reverse cat /etc/resolv.conf - assert_success - refute_output "nameserver 127.0.0.1" -} - -@test "checking unbound: /etc/resolv.conf (ldap configuration)" { - run docker exec mailserver_ldap cat /etc/resolv.conf - assert_success - assert_output "nameserver 127.0.0.1" -} - -@test "checking unbound: /var/mail/postfix/spool/etc/resolv.conf (default configuration)" { - run docker exec mailserver_default cat /var/mail/postfix/spool/etc/resolv.conf - assert_success - assert_output "nameserver 127.0.0.1" -} - -@test "checking unbound: /var/mail/postfix/spool/etc/resolv.conf (reverse configuration)" { - run docker exec mailserver_reverse cat /var/mail/postfix/spool/etc/resolv.conf - assert_success - refute_output "nameserver 127.0.0.1" -} - -@test "checking unbound: /var/mail/postfix/spool/etc/resolv.conf (ldap configuration)" { - run docker exec mailserver_ldap cat /var/mail/postfix/spool/etc/resolv.conf - assert_success - assert_output "nameserver 127.0.0.1" -} - -@test "checking unbound: root.hints exist (default configuration)" { - run docker exec mailserver_default [ -f /etc/unbound/root.hints ] - assert_success -} - -@test "checking unbound: root.hints doesn't exist (reverse configuration)" { - run docker exec mailserver_reverse [ ! -f /etc/unbound/root.hints ] - assert_success -} - -@test "checking unbound: root.hints exist (ldap configuration)" { - run docker exec mailserver_ldap [ -f /etc/unbound/root.hints ] - assert_success -} - -@test "checking unbound: root.key exist (default configuration)" { - run docker exec mailserver_default [ -f /etc/unbound/root.key ] - assert_success -} - -@test "checking unbound: root.key doesn't exist (reverse configuration)" { - run docker exec mailserver_reverse [ ! -f /etc/unbound/root.key ] - assert_success -} - -@test "checking unbound: root.key exist (ldap configuration)" { - run docker exec mailserver_ldap [ -f /etc/unbound/root.key ] - assert_success -} - -@test "checking unbound: unbound_control.key exist" { - run docker exec mailserver_default [ -f /etc/unbound/unbound_control.key ] - assert_success -} - -@test "checking unbound: unbound_control.pem exist" { - run docker exec mailserver_default [ -f /etc/unbound/unbound_control.pem ] - assert_success -} - -@test "checking unbound: unbound_server.key exist" { - run docker exec mailserver_default [ -f /etc/unbound/unbound_server.key ] - assert_success -} - -@test "checking unbound: unbound_server.pem exist" { - run docker exec mailserver_default [ -f /etc/unbound/unbound_server.pem ] - assert_success -} - -@test "checking unbound: server is running and unbound-control works" { - run docker exec -ti mailserver_default unbound-control status - assert_success - assert_output --partial 'is running' -} - -@test "checking unbound: get stats" { - run docker exec -ti mailserver_default unbound-control stats_noreset - assert_success -} - -@test "checking unbound: testing DNSSEC validation" { - run docker exec mailserver_default /bin/sh -c "dig com. SOA +nocmd +noall +dnssec +comments | grep 'flags: qr rd ra ad' | wc -l" - assert_success - assert_output 1 -} - -@test "checking unbound: debug mode enabled" { - run docker exec mailserver_traefik_acmev2 /bin/sh -c "unbound-control status | grep 'verbosity: 2'" - assert_success -} - -@test "checking unbound: debug mode disabled" { - run docker exec mailserver_default /bin/sh -c "unbound-control status | grep 'verbosity: 0'" - assert_success -} - -# -# ssl -# - -@test "checking ssl: ECDSA P-384 cert works correctly" { - run docker exec mailserver_ecdsa /bin/sh -c "timeout 1 openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp | grep 'Verify return code: 18 (self signed certificate)'" - assert_success -} - -@test "checking ssl: generated default cert works correctly" { - run docker exec mailserver_default /bin/sh -c "timeout 1 openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp | grep 'Verify return code: 18 (self signed certificate)'" - assert_success -} - -@test "checking ssl: let's encrypt cert works correctly" { - run docker exec mailserver_reverse /bin/sh -c "timeout 1 openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp | grep 'Verify return code: 10 (certificate has expired)'" - assert_success -} - -@test "checking ssl: traefik cert works correctly (acme v1)" { - run docker exec mailserver_traefik_acmev1 /bin/sh -c "timeout 1 openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp | grep 'Verify return code: 21 (unable to verify the first certificate)'" - assert_success -} - -@test "checking ssl: traefik cert works correctly (acme v2)" { - run docker exec mailserver_traefik_acmev2 /bin/sh -c "timeout 1 openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp | grep 'Verify return code: 21 (unable to verify the first certificate)'" - assert_success -} - -@test "checking ssl: default configuration is correct" { - run docker exec mailserver_default /bin/sh -c "grep '/ssl' /etc/postfix/main.cf | wc -l" - assert_success - assert_output 4 - run docker exec mailserver_default /bin/sh -c "grep '/ssl' /etc/dovecot/conf.d/10-ssl.conf | wc -l" - assert_success - assert_output 2 -} - -@test "checking ssl: let's encrypt configuration is correct" { - run docker exec mailserver_reverse /bin/sh -c "grep '/ssl' /etc/postfix/main.cf | wc -l" - assert_success - assert_output 4 - run docker exec mailserver_reverse /bin/sh -c "grep '/ssl' /etc/dovecot/conf.d/10-ssl.conf | wc -l" - assert_success - assert_output 2 -} - -# -# traefik acme v1 -# - -@test "checking traefik acme v1: acme.json exist" { - run docker exec mailserver_traefik_acmev1 [ -f /etc/letsencrypt/acme/acme.json ] - assert_success -} - -@test "checking traefik acme v1: dump.log doesn't exist" { - run docker exec mailserver_traefik_acmev1 [ -f /etc/letsencrypt/acme/dump.log ] - assert_failure -} - -@test "checking traefik acme v1: all certificates were generated" { - run docker exec mailserver_traefik_acmev1 [ -f /ssl/cert.pem ] - assert_success - run docker exec mailserver_traefik_acmev1 [ -f /ssl/chain.pem ] - assert_success - run docker exec mailserver_traefik_acmev1 [ -f /ssl/fullchain.pem ] - assert_success - run docker exec mailserver_traefik_acmev1 [ -f /ssl/privkey.pem ] - assert_success -} - -@test "checking traefik acme v1: check private key" { - run docker exec mailserver_traefik_acmev1 /bin/sh -c "openssl rsa -in /ssl/privkey.pem -check 2>/dev/null | head -n 1" - assert_success - assert_output "RSA key ok" -} - -@test "checking traefik acme v1: private key matches the certificate" { - run docker exec mailserver_traefik_acmev1 /bin/sh -c "(openssl x509 -noout -modulus -in /ssl/cert.pem | openssl md5 ; openssl rsa -noout -modulus -in /ssl/privkey.pem | openssl md5) | uniq | wc -l" - assert_success - assert_output 1 -} - -# -# traefik acme v2 -# - -@test "checking traefik acme v2: acme.json exist" { - run docker exec mailserver_traefik_acmev2 [ -f /etc/letsencrypt/acme/acme.json ] - assert_success -} - -@test "checking traefik acme v2: dump.log doesn't exist" { - run docker exec mailserver_traefik_acmev2 [ -f /etc/letsencrypt/acme/dump.log ] - assert_failure -} - -@test "checking traefik acme v2: all certificates were generated" { - run docker exec mailserver_traefik_acmev2 [ -f /ssl/cert.pem ] - assert_success - run docker exec mailserver_traefik_acmev2 [ -f /ssl/chain.pem ] - assert_success - run docker exec mailserver_traefik_acmev2 [ -f /ssl/fullchain.pem ] - assert_success - run docker exec mailserver_traefik_acmev2 [ -f /ssl/privkey.pem ] - assert_success -} - -@test "checking traefik acme v2: check private key" { - run docker exec mailserver_traefik_acmev2 /bin/sh -c "openssl rsa -in /ssl/privkey.pem -check 2>/dev/null | head -n 1" - assert_success - assert_output "RSA key ok" -} - -@test "checking traefik acme v2: private key matches the certificate" { - run docker exec mailserver_traefik_acmev2 /bin/sh -c "(openssl x509 -noout -modulus -in /ssl/cert.pem | openssl md5 ; openssl rsa -noout -modulus -in /ssl/privkey.pem | openssl md5) | uniq | wc -l" - assert_success - assert_output 1 -} - -# -# index files -# - -@test "checking hash tables: existing header_checks and virtual index files" { - run docker exec mailserver_default [ -f /etc/postfix/header_checks.db ] - assert_success - run docker exec mailserver_default [ -f /etc/postfix/virtual.db ] - assert_success -} - -# -# ldap -# - -@test "checking ldap maps exist in postfix main.cf in ldap configurations" { - run docker exec mailserver_ldap grep -i 'ldap:' /etc/postfix/main.cf - assert_success - run docker exec mailserver_ldap2 grep -i 'ldap:' /etc/postfix/main.cf - assert_success -} - -@test "checking no sql maps exist in postfix main.cf in ldap configurations" { - run docker exec mailserver_ldap grep -i 'sql' /etc/postfix/main.cf - assert_failure - run docker exec mailserver_ldap2 grep -i 'sql' /etc/postfix/main.cf - assert_failure -} - -@test "checking ldap alias, forward and group maps exist in postfix main.cf in ldap configurations" { - run docker exec mailserver_ldap grep -i 'ldap:/etc/postfix/ldap/virtual-alias-maps.cf' /etc/postfix/main.cf - assert_success - run docker exec mailserver_ldap grep -i 'ldap:/etc/postfix/ldap/virtual-forward-maps.cf' /etc/postfix/main.cf - assert_success - run docker exec mailserver_ldap grep -i 'ldap:/etc/postfix/ldap/virtual-group-maps.cf' /etc/postfix/main.cf - assert_success -} - -@test "checking ony ldap alias (not forward and group maps) exist in postfix main.cf in ldap2 configurations" { - run docker exec mailserver_ldap2 grep -i 'ldap:/etc/postfix/ldap/virtual-alias-maps.cf' /etc/postfix/main.cf - assert_success - run docker exec mailserver_ldap2 grep -i 'ldap:/etc/postfix/ldap/virtual-forward-maps.cf' /etc/postfix/main.cf - assert_failure - run docker exec mailserver_ldap2 grep -i 'ldap:/etc/postfix/ldap/virtual-group-maps.cf' /etc/postfix/main.cf - assert_failure -} - -@test "checking ldap master only exists in ldap2 configurations" { - run docker exec mailserver_ldap grep -i 'master' /etc/dovecot/conf.d/auth-ldap.conf.ext - assert_failure - run docker exec mailserver_ldap2 grep -i 'master' /etc/dovecot/conf.d/auth-ldap.conf.ext - assert_success -} - - -# -# logs -# - -@test "checking logs: /var/log/mail.log in mailserver_default is error free" { - run docker exec mailserver_default grep -i ': error:' /var/log/mail.log - assert_failure - run docker exec mailserver_default grep -i 'is not writable' /var/log/mail.log - assert_failure - run docker exec mailserver_default grep -i 'permission denied' /var/log/mail.log - assert_failure - run docker exec mailserver_default grep -i 'address already in use' /var/log/mail.log - assert_failure -} - -@test "checking logs: /var/log/mail.log in mailserver_reverse is error free " { - run docker exec mailserver_reverse grep -i ': error:' /var/log/mail.log - assert_failure - run docker exec mailserver_reverse grep -i 'is not writable' /var/log/mail.log - assert_failure - run docker exec mailserver_reverse grep -i 'permission denied' /var/log/mail.log - assert_failure - run docker exec mailserver_default grep -i 'address already in use' /var/log/mail.log - assert_failure -} - -@test "checking logs: /var/log/mail.log in mailserver_ldap is error free" { - run docker exec mailserver_ldap grep -i ': error:' /var/log/mail.log - assert_failure - run docker exec mailserver_ldap grep -i 'is not writable' /var/log/mail.log - assert_failure - run docker exec mailserver_ldap grep -i 'permission denied' /var/log/mail.log - assert_failure - run docker exec mailserver_ldap grep -i 'address already in use' /var/log/mail.log - assert_failure -} - -@test "checking logs: /var/log/mail.log in mailserver_ldap2 is error free" { - run docker exec mailserver_ldap2 grep -i ': error:' /var/log/mail.log - assert_failure - run docker exec mailserver_ldap2 grep -i 'is not writable' /var/log/mail.log - assert_failure - run docker exec mailserver_ldap2 grep -i 'permission denied' /var/log/mail.log - assert_failure - run docker exec mailserver_ldap2 grep -i 'address already in use' /var/log/mail.log - assert_failure -} - -@test "checking logs: /var/log/mail.err in mailserver_default does not exist" { - run docker exec mailserver_default cat /var/log/mail.err - assert_failure - assert_output --partial 'No such file or directory' -} - -@test "checking logs: /var/log/mail.err in mailserver_reverse does not exist" { - run docker exec mailserver_reverse cat /var/log/mail.err - assert_failure - assert_output --partial 'No such file or directory' -} - -@test "checking logs: /var/log/mail.err in mailserver_ldap does not exist" { - run docker exec mailserver_ldap cat /var/log/mail.err - assert_failure - assert_output --partial 'No such file or directory' -} - -@test "checking logs: /var/log/mail.err in mailserver_ldap2 does not exist" { - run docker exec mailserver_ldap2 cat /var/log/mail.err - assert_failure - assert_output --partial 'No such file or directory' -} - -@test "checking logs: /var/log/mail.err in mailserver_ecdsa does not exist" { - run docker exec mailserver_ecdsa cat /var/log/mail.err - assert_failure - assert_output --partial 'No such file or directory' -} - -@test "checking logs: /var/log/mail.err in mailserver_traefik_acmev1 does not exist" { - run docker exec mailserver_traefik_acmev1 cat /var/log/mail.err - assert_failure - assert_output --partial 'No such file or directory' -} - -@test "checking logs: /var/log/mail.err in mailserver_traefik_acmev2 does not exist" { - run docker exec mailserver_traefik_acmev2 cat /var/log/mail.err - assert_failure - assert_output --partial 'No such file or directory' -} diff --git a/test/traefik_acmev1.bats b/test/traefik_acmev1.bats new file mode 100644 index 00000000..c82e709d --- /dev/null +++ b/test/traefik_acmev1.bats @@ -0,0 +1,100 @@ +load 'test_helper/bats-support/load' +load 'test_helper/bats-assert/load' + +# rspamd + +@test "checking rspamd: debug mode disabled (traefik_acmev1 configuration)" { + run docker exec mailserver_traefik_acmev1 /bin/sh -c 'rspamadm configdump | grep -E "level = \"warning\";"' + assert_success +} + +# +# postfix +# + +@test "checking postfix: verbose mode enabled (traefik_acmev1 configuration)" { + run docker exec mailserver_traefik_acmev1 /bin/sh -c "grep 'smtpd -v' /etc/postfix/master.cf | wc -l" + assert_success + assert_output 3 +} + +# +# dovecot +# + +@test "checking dovecot: debug mode enabled (traefik_acmev1 configuration)" { + run docker exec mailserver_traefik_acmev1 /bin/sh -c "doveconf -h auth_verbose 2>/dev/null" + assert_success + assert_output "yes" + run docker exec mailserver_traefik_acmev1 /bin/sh -c "doveconf -h auth_verbose_passwords 2>/dev/null" + assert_success + assert_output "sha1" + run docker exec mailserver_traefik_acmev1 /bin/sh -c "doveconf -h auth_debug 2>/dev/null" + assert_success + assert_output "yes" + run docker exec mailserver_traefik_acmev1 /bin/sh -c "doveconf -h auth_debug_passwords 2>/dev/null" + assert_success + assert_output "yes" + run docker exec mailserver_traefik_acmev1 /bin/sh -c "doveconf -h mail_debug 2>/dev/null" + assert_success + assert_output "yes" + run docker exec mailserver_traefik_acmev1 /bin/sh -c "doveconf -h verbose_ssl 2>/dev/null" + assert_success + assert_output "yes" +} + +# +# ssl +# + +@test "checking ssl: traefik cert works correctly (acme v1)" { + run docker exec mailserver_traefik_acmev1 /bin/sh -c "timeout 1 openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp | grep 'Verify return code: 10 (certificate has expired)'" + assert_success +} + +# +# traefik acme v1 +# + +@test "checking traefik acme v1: acme.json exist" { + run docker exec mailserver_traefik_acmev1 [ -f /etc/letsencrypt/acme/acme.json ] + assert_success +} + +@test "checking traefik acme v1: dump.log doesn't exist" { + run docker exec mailserver_traefik_acmev1 [ -f /etc/letsencrypt/acme/dump.log ] + assert_failure +} + +@test "checking traefik acme v1: all certificates were generated" { + run docker exec mailserver_traefik_acmev1 [ -f /ssl/cert.pem ] + assert_success + run docker exec mailserver_traefik_acmev1 [ -f /ssl/chain.pem ] + assert_success + run docker exec mailserver_traefik_acmev1 [ -f /ssl/fullchain.pem ] + assert_success + run docker exec mailserver_traefik_acmev1 [ -f /ssl/privkey.pem ] + assert_success +} + +@test "checking traefik acme v1: check private key" { + run docker exec mailserver_traefik_acmev1 /bin/sh -c "openssl rsa -in /ssl/privkey.pem -check 2>/dev/null | head -n 1" + assert_success + assert_output "RSA key ok" +} + +@test "checking traefik acme v1: private key matches the certificate" { + run docker exec mailserver_traefik_acmev1 /bin/sh -c "(openssl x509 -noout -modulus -in /ssl/cert.pem | openssl md5 ; openssl rsa -noout -modulus -in /ssl/privkey.pem | openssl md5) | uniq | wc -l" + assert_success + assert_output 1 +} + +# +# logs +# + +@test "checking logs: /var/log/mail.err in mailserver_traefik_acmev1 does not exist" { + run docker exec mailserver_traefik_acmev1 cat /var/log/mail.err + assert_failure + assert_output --partial 'No such file or directory' +} diff --git a/test/traefik_acmev2.bats b/test/traefik_acmev2.bats new file mode 100644 index 00000000..28805876 --- /dev/null +++ b/test/traefik_acmev2.bats @@ -0,0 +1,109 @@ +load 'test_helper/bats-support/load' +load 'test_helper/bats-assert/load' + +# rspamd + +@test "checking rspamd: debug mode enabled (traefik_acmev2 configuration)" { + run docker exec mailserver_traefik_acmev2 /bin/sh -c 'rspamadm configdump | grep -E "level = \"info\";"' + assert_success +} + +# +# postfix +# + +@test "checking postfix: verbose mode enabled (traefik_acmev2 configuration)" { + run docker exec mailserver_traefik_acmev2 /bin/sh -c "grep 'smtpd -v' /etc/postfix/master.cf | wc -l" + assert_success + assert_output 3 +} + +# +# dovecot +# + +@test "checking dovecot: debug mode enabled (traefik_acmev2 configuration)" { + run docker exec mailserver_traefik_acmev2 /bin/sh -c "doveconf -h auth_verbose 2>/dev/null" + assert_success + assert_output "yes" + run docker exec mailserver_traefik_acmev2 /bin/sh -c "doveconf -h auth_verbose_passwords 2>/dev/null" + assert_success + assert_output "sha1" + run docker exec mailserver_traefik_acmev2 /bin/sh -c "doveconf -h auth_debug 2>/dev/null" + assert_success + assert_output "yes" + run docker exec mailserver_traefik_acmev2 /bin/sh -c "doveconf -h auth_debug_passwords 2>/dev/null" + assert_success + assert_output "yes" + run docker exec mailserver_traefik_acmev2 /bin/sh -c "doveconf -h mail_debug 2>/dev/null" + assert_success + assert_output "yes" + run docker exec mailserver_traefik_acmev2 /bin/sh -c "doveconf -h verbose_ssl 2>/dev/null" + assert_success + assert_output "yes" +} + +# +# unbound +# + +@test "checking unbound: debug mode enabled" { + run docker exec mailserver_traefik_acmev2 /bin/sh -c "unbound-control status | grep 'verbosity: 2'" + assert_success +} + +# +# ssl +# + +@test "checking ssl: traefik cert works correctly (acme v2)" { + run docker exec mailserver_traefik_acmev2 /bin/sh -c "timeout 1 openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp | grep 'Verify return code: 10 (certificate has expired)'" + assert_success +} + +# +# traefik acme v2 +# + +@test "checking traefik acme v2: acme.json exist" { + run docker exec mailserver_traefik_acmev2 [ -f /etc/letsencrypt/acme/acme.json ] + assert_success +} + +@test "checking traefik acme v2: dump.log doesn't exist" { + run docker exec mailserver_traefik_acmev2 [ -f /etc/letsencrypt/acme/dump.log ] + assert_failure +} + +@test "checking traefik acme v2: all certificates were generated" { + run docker exec mailserver_traefik_acmev2 [ -f /ssl/cert.pem ] + assert_success + run docker exec mailserver_traefik_acmev2 [ -f /ssl/chain.pem ] + assert_success + run docker exec mailserver_traefik_acmev2 [ -f /ssl/fullchain.pem ] + assert_success + run docker exec mailserver_traefik_acmev2 [ -f /ssl/privkey.pem ] + assert_success +} + +@test "checking traefik acme v2: check private key" { + run docker exec mailserver_traefik_acmev2 /bin/sh -c "openssl rsa -in /ssl/privkey.pem -check 2>/dev/null | head -n 1" + assert_success + assert_output "RSA key ok" +} + +@test "checking traefik acme v2: private key matches the certificate" { + run docker exec mailserver_traefik_acmev2 /bin/sh -c "(openssl x509 -noout -modulus -in /ssl/cert.pem | openssl md5 ; openssl rsa -noout -modulus -in /ssl/privkey.pem | openssl md5) | uniq | wc -l" + assert_success + assert_output 1 +} + +# +# logs +# + +@test "checking logs: /var/log/mail.err in mailserver_traefik_acmev2 does not exist" { + run docker exec mailserver_traefik_acmev2 cat /var/log/mail.err + assert_failure + assert_output --partial 'No such file or directory' +} From 1c9d2d67169d9632284409aef3f9323b2fcb54f8 Mon Sep 17 00:00:00 2001 From: Andrew Savinykh <658865+AndrewSav@users.noreply.github.com> Date: Sat, 25 Sep 2021 12:47:33 +1200 Subject: [PATCH 32/50] added workflow for the master branch push and adjusted the badges (#41) --- .github/workflows/master.yml | 19 +++++++++++++++++++ README.md | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/master.yml diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml new file mode 100644 index 00000000..7eadc88c --- /dev/null +++ b/.github/workflows/master.yml @@ -0,0 +1,19 @@ +name: all tests + +on: + push: + branches: master + +jobs: + # Run tests + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Run all tests + run: make diff --git a/README.md b/README.md index 43530ada..d1f47fe9 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,11 @@ Thank you very much. ### Build -[![Build Status](https://travis-ci.com/mailserver2/mailserver.svg?branch=master)](https://travis-ci.com/mailserver2/mailserver) +[![Build Status](https://github.com/mailserver2/mailserver/actions/workflows/master.yml/badge.svg)](https://github.com/mailserver2/mailserver/actions/workflows/master.yml) ### Docker image -[![](https://images.microbadger.com/badges/image/mailserver2/mailserver:latest.svg)](https://microbadger.com/images/mailserver2/mailserver:latest) [![](https://img.shields.io/docker/automated/mailserver2/mailserver.svg)](https://hub.docker.com/r/mailserver2/mailserver/builds/) [![](https://img.shields.io/docker/pulls/mailserver2/mailserver.svg)](https://hub.docker.com/r/mailserver2/mailserver/) [![](https://img.shields.io/docker/stars/mailserver2/mailserver.svg)](https://hub.docker.com/r/mailserver2/mailserver/) [![](https://img.shields.io/badge/bitcoin-donate-green.svg)](https://keybase.io/hardware) +[![](https://images.microbadger.com/badges/image/mailserver2/mailserver:latest.svg)](https://microbadger.com/images/mailserver2/mailserver:latest) [![](https://img.shields.io/docker/automated/mailserver2/mailserver.svg)](https://hub.docker.com/r/mailserver2/mailserver/builds/) [![](https://img.shields.io/docker/pulls/mailserver2/mailserver.svg)](https://hub.docker.com/r/mailserver2/mailserver/) [![](https://img.shields.io/docker/stars/mailserver2/mailserver.svg)](https://hub.docker.com/r/mailserver2/mailserver/) **mailserver2/mailserver** is a simple and full-featured mail server build as a set of multiple docker images, including: From 71688532934217c117d79e80b89b2bd36eeadefc Mon Sep 17 00:00:00 2001 From: Johan Smits Date: Sat, 2 Oct 2021 04:43:01 +0200 Subject: [PATCH 33/50] Microsoft Active Directory you can have groups in groups support. (#40) To support this you need to set the leaf group member option for it to follow the sub groups (if any). --- Makefile | 2 ++ README.md | 2 ++ rootfs/etc/postfix/ldap/virtual-group-maps.cf | 10 ++++++++++ rootfs/usr/local/bin/setup.sh | 4 ++++ 4 files changed, 18 insertions(+) diff --git a/Makefile b/Makefile index 6aa2e7be..8bf4e3c4 100644 --- a/Makefile +++ b/Makefile @@ -91,6 +91,8 @@ init_ldap: init_openldap init_redis -e LDAP_FORWARD_ATTRIBUTE="mail" \ -e LDAP_GROUP_FILTER="(&(mail=%s)(objectClass=mailGroup))" \ -e LDAP_GROUP_ATTRIBUTE="uid" \ + -e LDAP_GROUP_RESULT_ATTRIBUTE="mail" \ + -e LDAP_GROUP_RESULT_MEMBER="member" \ -e LDAP_SENDER_FILTER="(&(|(mail=%s)(mailalias=%s))(objectClass=mailAccount))" \ -e LDAP_SENDER_ATTRIBUTE="mail" \ -e LDAP_DOVECOT_USER_ATTRS="=home=/var/mail/vhosts/%d/%n/,=mail=maildir:/var/mail/vhosts/%d/%n/mail/,mailuserquota=quota_rule=*:bytes=%\$$" \ diff --git a/README.md b/README.md index d1f47fe9..d29c90d7 100644 --- a/README.md +++ b/README.md @@ -392,6 +392,8 @@ When using LDAP authentication the following additional variables become availab | **LDAP_GROUP_FILTER** | The search filter for group lookups | *optional* | | **LDAP_GROUP_ATTRIBUTE** | The attributes for group lookup | *optional* | | **LDAP_GROUP_FORMAT** | The format for group lookups | *optional* | +| **LDAP_GROUP_RESULT_ATTRIBUTE** | The leaf result attribute group | *optional* | +| **LDAP_GROUP_RESULT_MEMBER** | The special result attribute of the group | *optional* | | **LDAP_SENDER_SEARCH_BASE** | The search base for sender lookups | *optional* | ${LDAP_DEFAULT_SEARCH_BASE} | | **LDAP_SENDER_SEARCH_SCOPE** | The search scope for sender lookups | *optional* | ${LDAP_DEFAULT_SEARCH_SCOPE} | | **LDAP_SENDER_FILTER** | The search filter for sender lookups | **required** | diff --git a/rootfs/etc/postfix/ldap/virtual-group-maps.cf b/rootfs/etc/postfix/ldap/virtual-group-maps.cf index 9e9ce874..ea014aaa 100644 --- a/rootfs/etc/postfix/ldap/virtual-group-maps.cf +++ b/rootfs/etc/postfix/ldap/virtual-group-maps.cf @@ -28,9 +28,19 @@ search_base = {{ .LDAP_GROUP_SEARCH_BASE }} scope = {{ .LDAP_GROUP_SEARCH_SCOPE }} query_filter = {{ .LDAP_GROUP_FILTER }} + +{{ if ne .LDAP_GROUP_ATTRIBUTE "" }} result_attribute = {{ .LDAP_GROUP_ATTRIBUTE }} +{{ end }} {{ if ne .LDAP_GROUP_FORMAT "" }} result_format = {{ .LDAP_GROUP_FORMAT }} {{ end }} +{{ if ne .LDAP_GROUP_RESULT_ATTRIBUTE "" }} +leaf_result_attribute = {{ .LDAP_GROUP_RESULT_ATTRIBUTE }} +{{ end }} + +{{ if ne .LDAP_GROUP_RESULT_MEMBER "" }} +special_result_attribute = {{ .LDAP_GROUP_RESULT_MEMBER }} +{{ end }} diff --git a/rootfs/usr/local/bin/setup.sh b/rootfs/usr/local/bin/setup.sh index f1cbea3e..725555b6 100644 --- a/rootfs/usr/local/bin/setup.sh +++ b/rootfs/usr/local/bin/setup.sh @@ -126,6 +126,8 @@ if [ "$DBDRIVER" = "ldap" ]; then export LDAP_GROUP_FILTER export LDAP_GROUP_ATTRIBUTE export LDAP_GROUP_FORMAT + export LDAP_GROUP_RESULT_ATTRIBUTE + export LDAP_GROUP_RESULT_MEMBER export LDAP_SENDER_SEARCH_BASE export LDAP_SENDER_SEARCH_SCOPE @@ -182,6 +184,8 @@ if [ "$DBDRIVER" = "ldap" ]; then LDAP_GROUP_FILTER=${LDAP_GROUP_FILTER:-} LDAP_GROUP_ATTRIBUTE=${LDAP_GROUP_ATTRIBUTE:-} LDAP_GROUP_FORMAT=${LDAP_GROUP_FORMAT:-} + LDAP_GROUP_RESULT_ATTRIBUTE=${LDAP_GROUP_RESULT_ATTRIBUTE:-} + LDAP_GROUP_RESULT_MEMBER=${LDAP_GROUP_RESULT_MEMBER:-} LDAP_SENDER_SEARCH_BASE=${LDAP_SENDER_SEARCH_BASE:-"${LDAP_DEFAULT_SEARCH_BASE}"} LDAP_SENDER_SEARCH_SCOPE=${LDAP_SENDER_SEARCH_SCOPE:-"${LDAP_DEFAULT_SEARCH_SCOPE}"} From b9f2ad7dcc26a8c59587a2545ab71ad293416b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Sat, 2 Oct 2021 05:15:55 +0200 Subject: [PATCH 34/50] Update base image to mailserver2/debian-mail-overlay:1.0.6 (#42) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index a5396bb4..44076ae0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.5 +FROM mailserver2/debian-mail-overlay:1.0.6 LABEL description="Simple and full-featured mail server using Docker" From 359790ee5b123de9220f5bdb9e8bdb18574addd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Thu, 6 Jan 2022 21:53:57 +0100 Subject: [PATCH 35/50] Update base image to mailserver2/debian-mail-overlay:1.0.7 (#43) Simple update to the latest [debian-mail-overlay](https://github.com/mailserver2/debian-mail-overlay/releases/tag/v1.0.7) image. --- Dockerfile | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 44076ae0..c326781a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.6 +FROM mailserver2/debian-mail-overlay:1.0.7 LABEL description="Simple and full-featured mail server using Docker" diff --git a/Makefile b/Makefile index 8bf4e3c4..b5032ee3 100644 --- a/Makefile +++ b/Makefile @@ -63,7 +63,7 @@ init_postgres: -e POSTGRES_USER=postfix \ -e POSTGRES_PASSWORD=testpasswd \ -v "`pwd`/test/config/postgres":/docker-entrypoint-initdb.d \ - -t postgres:13-alpine + -t postgres:14-alpine init_ldap: init_openldap init_redis -docker rm -f \ From a4ef19d9e096632b62723038cdfed7f5f00074f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Tue, 5 Apr 2022 17:43:25 +0200 Subject: [PATCH 36/50] Update base image to mailserver2/debian-mail-overlay:1.0.8 (#45) Simple update to the latest [debian-mail-overlay](https://github.com/mailserver2/debian-mail-overlay/releases/tag/v1.0.8) image. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c326781a..155351cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.7 +FROM mailserver2/debian-mail-overlay:1.0.8 LABEL description="Simple and full-featured mail server using Docker" From 765a1cb232953618b34130e8b1dba48aee0aa56a Mon Sep 17 00:00:00 2001 From: Andrew Savinykh <658865+AndrewSav@users.noreply.github.com> Date: Thu, 15 Dec 2022 09:25:07 +1300 Subject: [PATCH 37/50] Update base image to mailserver2/debian-mail-overlay:1.0.9 (#48) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 155351cf..df2f9bfa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.8 +FROM mailserver2/debian-mail-overlay:1.0.9 LABEL description="Simple and full-featured mail server using Docker" From fd6357ce9a4df051193df606deb1880e57d399fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Mon, 27 Feb 2023 16:30:04 +0100 Subject: [PATCH 38/50] Update base image to mailserver2/debian-mail-overlay:1.0.10 (#51) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index df2f9bfa..98d51e0b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.9 +FROM mailserver2/debian-mail-overlay:1.0.10 LABEL description="Simple and full-featured mail server using Docker" From 6fdf2fb85ac62024722473fd96c550575ec84780 Mon Sep 17 00:00:00 2001 From: diroots <30463065+diroots@users.noreply.github.com> Date: Tue, 16 May 2023 00:38:21 +0200 Subject: [PATCH 39/50] config changes for latest clamav-unofficial-sigs 7.2.5 (#46) * config changes for latest clamav-unofficial-sigs 7.2.5 * update to new /etc/clamav-unofficial-sigs path in README * update makefile for pathes, and os.conf for restart option removal * change path in test/share as per makefile changes * changes in pathes --------- Co-authored-by: diroots --- Makefile | 8 +- README.md | 12 +- .../master.conf | 436 +- rootfs/etc/clamav-unofficial-sigs/os.conf | 39 + rootfs/etc/clamav/unofficial-sigs/os.conf | 9 - .../usr/local/bin/clamav-unofficial-sigs.sh | 4337 +++++++++++------ rootfs/usr/local/bin/setup.sh | 4 +- .../user.conf | 0 8 files changed, 3209 insertions(+), 1636 deletions(-) rename rootfs/etc/{clamav/unofficial-sigs => clamav-unofficial-sigs}/master.conf (64%) create mode 100644 rootfs/etc/clamav-unofficial-sigs/os.conf delete mode 100644 rootfs/etc/clamav/unofficial-sigs/os.conf mode change 100755 => 100644 rootfs/usr/local/bin/clamav-unofficial-sigs.sh rename test/share/{clamav/unofficial-sigs => clamav-unofficial-sigs}/user.conf (100%) diff --git a/Makefile b/Makefile index b5032ee3..1cd73bb2 100644 --- a/Makefile +++ b/Makefile @@ -113,7 +113,7 @@ init_ldap: init_openldap init_redis -v "`pwd`/test/share/postfix/custom.conf":/var/mail/postfix/custom.conf \ -v "`pwd`/test/share/postfix/sender_access":/var/mail/postfix/sender_access \ -v "`pwd`/test/share/dovecot/conf.d":/var/mail/dovecot/conf.d \ - -v "`pwd`/test/share/clamav/unofficial-sigs/user.conf":/var/mail/clamav-unofficial-sigs/user.conf \ + -v "`pwd`/test/share/clamav-unofficial-sigs/user.conf":/var/mail/clamav-unofficial-sigs/user.conf \ -h mail.domain.tld \ -t $(NAME) @@ -194,7 +194,7 @@ init_ldap2: init_openldap init_redis -v "`pwd`/test/share/postfix/custom.conf":/var/mail/postfix/custom.conf \ -v "`pwd`/test/share/postfix/sender_access":/var/mail/postfix/sender_access \ -v "`pwd`/test/share/dovecot/conf.d":/var/mail/dovecot/conf.d \ - -v "`pwd`/test/share/clamav/unofficial-sigs/user.conf":/var/mail/clamav-unofficial-sigs/user.conf \ + -v "`pwd`/test/share/clamav-unofficial-sigs/user.conf":/var/mail/clamav-unofficial-sigs/user.conf \ -h mail.domain.tld \ -t $(NAME) fixtures_ldap2: @@ -236,7 +236,7 @@ init_default: init_redis init_mariadb -v "`pwd`/test/share/postfix/custom.conf":/var/mail/postfix/custom.conf \ -v "`pwd`/test/share/postfix/sender_access":/var/mail/postfix/sender_access \ -v "`pwd`/test/share/dovecot/conf.d":/var/mail/dovecot/conf.d \ - -v "`pwd`/test/share/clamav/unofficial-sigs/user.conf":/var/mail/clamav-unofficial-sigs/user.conf \ + -v "`pwd`/test/share/clamav-unofficial-sigs/user.conf":/var/mail/clamav-unofficial-sigs/user.conf \ -h mail.domain.tld \ -t $(NAME) @@ -425,7 +425,7 @@ init_sieve: init_redis init_mariadb -v "`pwd`/test/share/postfix/custom.conf":/var/mail/postfix/custom.conf \ -v "`pwd`/test/share/postfix/sender_access":/var/mail/postfix/sender_access \ -v "`pwd`/test/share/dovecot/conf.d":/var/mail/dovecot/conf.d \ - -v "`pwd`/test/share/clamav/unofficial-sigs/user.conf":/var/mail/clamav-unofficial-sigs/user.conf \ + -v "`pwd`/test/share/clamav-unofficial-sigs/user.conf":/var/mail/clamav-unofficial-sigs/user.conf \ -h mail.domain.tld \ -t $(NAME) diff --git a/README.md b/README.md index d29c90d7..c76f064b 100644 --- a/README.md +++ b/README.md @@ -675,8 +675,7 @@ Readme: https://github.com/extremeshok/clamav-unofficial-sigs #### Enable clamav-unofficial-sigs -Create your `user.conf` file under `/mnt/docker/mail/clamav-unofficial-sigs` directory to configure clamav-unofficial-sigs updater. This file override the default configuration specified in [os.conf](https://github.com/mailserver2/mailserver/blob/master/rootfs/etc/clamav/unofficial-sigs/os.conf) and [master.conf](https://github.com/mailserver2/mailserver/blob/master/rootfs/etc/clamav/unofficial-sigs/master.conf). Don't forget, once you have completed the configuration of this file, set the value of `user_configuration_complete` to `yes` otherwise the script will not be able to execute. -As [Yara rules are broken with Clamav = 0.100](https://github.com/extremeshok/clamav-unofficial-sigs/issues/203), we disable Yara rules for now. +Create your `user.conf` file under `/mnt/docker/mail/clamav-unofficial-sigs` directory to configure clamav-unofficial-sigs updater. This file override the default configuration specified in [os.conf](https://github.com/mailserver2/mailserver/blob/master/rootfs/etc/clamav-unofficial-sigs/os.conf) and [master.conf](https://github.com/mailserver2/mailserver/blob/master/rootfs/etc/clamav-unofficial-sigs/master.conf). Don't forget, once you have completed the configuration of this file, set the value of `user_configuration_complete` to `yes` otherwise the script will not be able to execute. ```ini # /mnt/docker/mail/clamav-unofficial-sigs/user.conf @@ -711,9 +710,12 @@ As [Yara rules are broken with Clamav = 0.100](https://github.com/extremeshok/cl # - 6. Enter the authorization signature into the config securiteinfo_authorisation_signature: replacing YOUR-SIGNATURE-NUMBER with your authorization signature from the link # securiteinfo_authorisation_signature="YOUR-SIGNATURE-NUMBER" -# We disable Yara rules for now because they are broken with Clamav releases > 0.100 -yararulesproject_enabled="no" -enable_yararules="no" +# We enable Yara rules as they are now working with Clamav releases > 0.100 +yararulesproject_enabled="yes" +enable_yararules="yes" + +# We enable interserver sigs +interserver_enabled="yes" # After you have completed the configuration of this file, set the value to "yes" user_configuration_complete="yes" diff --git a/rootfs/etc/clamav/unofficial-sigs/master.conf b/rootfs/etc/clamav-unofficial-sigs/master.conf similarity index 64% rename from rootfs/etc/clamav/unofficial-sigs/master.conf rename to rootfs/etc/clamav-unofficial-sigs/master.conf index 6884f0dc..66f1c97b 100644 --- a/rootfs/etc/clamav/unofficial-sigs/master.conf +++ b/rootfs/etc/clamav-unofficial-sigs/master.conf @@ -1,22 +1,18 @@ # This file contains master configuration settings for clamav-unofficial-sigs.sh -################### +################################################################################ # This is property of eXtremeSHOK.com # You are free to use, modify and distribute, however you may not remove this notice. # Copyright (c) Adrian Jon Kriel :: admin@extremeshok.com # License: BSD (Berkeley Software Distribution) -################## -# -# Script updates can be found at: https://github.com/extremeshok/clamav-unofficial-sigs -# -################## +################################################################################ # -# NOT COMPATIBLE WITH VERSION 3.XX / 4.XX CONFIG +# DO NOT EDIT THIS FILE !! DO NOT EDIT THIS FILE !! DO NOT EDIT THIS FILE !! # ################################################################################ # -# IT IS BETTER TO SET YOUR OPTIONS IN THE user.conf AS THIS MAKES UPDATES EASIER +# SET YOUR CUSTOM OPTIONS AND SETTINGS IN THE user.conf # -# os.conf AND user.conf OVERRIDES THE OPTIONS IN THIS FILE +# os.conf (os.***.conf) AND user.conf OVERRIDES THE OPTIONS IN THIS FILE # ################################################################################ @@ -66,13 +62,15 @@ work_dir="/var/lib/clamav-unofficial-sigs" #Top level working directory logging_enabled="yes" log_file_path="/var/log/clamav-unofficial-sigs" log_file_name="clamav-unofficial-sigs.log" +## Use a program to log messages +#log_pipe_cmd="/usr/bin/logger -it 'clamav-unofficial-sigs'" # ========================= # MalwarePatrol : https://www.malwarepatrol.net # MalwarePatrol 2016 (free) clamav signatures # -# 1. Sign up for an account : https://www.malwarepatrol.net/signup-free.shtml +# 1. Sign up for an account : https://www.malwarepatrol.net/free-guard-upgrade-option/ # 2. You will recieve an email containing your password/receipt number # 3. Login to your account at malwarePatrol # 4. In My Accountpage, choose the ClamAV list you will download. Free subscribers only get ClamAV Basic, commercial subscribers have access to ClamAV Extended. Do not use the agressive lists. @@ -81,8 +79,17 @@ log_file_name="clamav-unofficial-sigs.log" malwarepatrol_receipt_code="YOUR-RECEIPT-NUMBER" malwarepatrol_product_code="8" malwarepatrol_list="clamav_basic" # clamav_basic or clamav_ext -# Set to no to enable the commercial subscription url. +# if the malwarepatrol_product_code is not 8, +# the malwarepatrol_free is set to no (non-free) +# set to no to enable the commercial subscription url, malwarepatrol_free="yes" +malwarepatrol_db="malwarepatrol.db" + + +# ========================= +# Malware Expert : https://www.Malware Expert +# Malware Expert 2020 (non-free) clamav signatures +malwareexpert_serial_key="YOUR-SERIAL-KEY" # ========================= # SecuriteInfo : https://www.SecuriteInfo.com @@ -100,32 +107,42 @@ malwarepatrol_free="yes" # - 6. Enter the authorisation signature into the config securiteinfo_authorisation_signature: replacing YOUR-SIGNATURE-NUMBER with your authorisation signature from the link securiteinfo_authorisation_signature="YOUR-SIGNATURE-NUMBER" +# Enable if you have a commercial/premium/non-free subscription +securiteinfo_premium="no" + # ======================== # Database provider update time # ======================== # Since the database files are dynamically created, non default values can cause banning, change with caution - -sanesecurity_update_hours="2" # Default is 2 hours (12 downloads daily). -securiteinfo_update_hours="4" # Default is 4 hours (6 downloads daily). +additional_update_hours="4" # Default is 4 hours (6 downloads daily). +interserver_update_hours="1" # Default is 2 hours (12 downloads daily). linuxmalwaredetect_update_hours="6" # Default is 6 hours (4 downloads daily). +malwareexpert_update_hours="2" # Default is 2 hours (12 downloads daily). malwarepatrol_update_hours="24" # Default is 24 hours (1 downloads daily). +sanesecurity_update_hours="2" # Default is 2 hours (12 downloads daily). +securiteinfo_premium_update_hours="1" # Default is 1 hours (24 downloads daily). +securiteinfo_update_hours="4" # Default is 4 hours (6 downloads daily). +urlhaus_update_hours="1" # Default is 1 hours (24 downloads daily). yararulesproject_update_hours="24" # Default is 24 hours (1 downloads daily). -additional_update_hours="4" # Default is 4 hours (6 downloads daily). # ======================== # Enabled Databases # ======================== # Set to no to disable an entire database, if the database is empty it will also be disabled. -sanesecurity_enabled="yes" # Sanesecurity -securiteinfo_enabled="yes" # SecuriteInfo +additional_enabled="yes" # Additional Databases +interserver_enabled="yes" # interServer linuxmalwaredetect_enabled="yes" # Linux Malware Detect +malwareexpert_enabled="yes" # Malware Expert malwarepatrol_enabled="yes" # Malware Patrol -yararulesproject_enabled="no" # Yara-Rule Project, automatically disabled if clamav is older than 0.99 -additional_enabled="yes" # Additional Databases +sanesecurity_enabled="yes" # Sanesecurity +securiteinfo_enabled="yes" # SecuriteInfo +urlhaus_enabled="yes" # urlhaus +yararulesproject_enabled="yes" # Yara-Rule Project, automatically disabled if clamav is older than 0.100 and enable_yararules is disabled -## Disabling this will also cause the yararulesproject to be disabled. -enable_yararules="no" #Enables yararules in the various databases, automatically disabled if clamav is older than 0.99 +# Disabled by default +## Enabling this will also cause the yararulesproject to be enabled if they are det to enabled. +enable_yararules="yes" #Enables yararules in the various databases, automatically disabled if clamav is older than 0.100 # ======================== # eXtremeSHOK Database format @@ -146,7 +163,7 @@ enable_yararules="no" #Enables yararules in the various databases, automatical # LOWONLY : used only when the rating is low # MEDIUMONLY : used only when the rating is medium # LOWMEDIUMONLY : used only when the rating is medium or low -# DISABLED : never used, or you can also comment the line out if you want +# DISABLED : never used, will automatically remove the present file # # Old Format is still supported, requiring you to comment out files to disable them # old_example_dbs=" @@ -154,15 +171,16 @@ enable_yararules="no" #Enables yararules in the various databases, automatical # " # Default dbs rating -# valid rating: LOW, MEDIUM, HIGH -default_dbs_rating="LOW" +# valid rating: LOW, MEDIUM, HIGH, DISABLE +default_dbs_rating="MEDIUM" # Per Database # These ratings will override the global rating for the specific database -# valid rating: LOW, MEDIUM, HIGH, DISABLED +# valid ratings: LOW | MEDIUM | HIGH | DISABLE +#linuxmalwaredetect_dbs_rating="" #sanesecurity_dbs_rating="" #securiteinfo_dbs_rating="" -#linuxmalwaredetect_dbs_rating="" +#urlhaus_dbs_rating="" #yararulesproject_dbs_rating="" # ======================== @@ -187,36 +205,28 @@ declare -a sanesecurity_dbs=( # BEGIN SANESECURITY DATABASE sanesecurity.ftm|REQUIRED # Message file types, for best performance sigwhitelist.ign2|REQUIRED # Fast update file to whitelist any problem signatures # LOW +blurl.ndb|LOW # Blacklisted full urls over the last 7 days, covering malware/spam/phishing. URLs added only when main signatures have failed to detect but are known to be "bad" junk.ndb|LOW # General high hitting junk, containing spam/phishing/lottery/jobs/419s etc jurlbl.ndb|LOW # Junk Url based +malwarehash.hsb|LOW # Malware hashes without known Size phish.ndb|LOW # Phishing and Malware rogue.hdb|LOW # Malware, Rogue anti-virus software and Fake codecs etc. Updated hourly to cover the latest malware threats scam.ndb|LOW # Spam/scams -spamimg.hdb|LOW # Spam images spamattach.hdb|LOW # Spam Spammed attachments such as pdf/doc/rtf/zips -blurl.ndb|LOW # Blacklisted full urls over the last 7 days, covering malware/spam/phishing. URLs added only when main signatures have failed to detect but are known to be "bad" -malwarehash.hsb|LOW # Malware hashes without known Size +spamimg.hdb|LOW # Spam images # MEDIUM +badmacro.ndb|MEDIUM # Blocks dangerous macros embedded in Word/Excel/Xml/RTF/JS documents jurlbla.ndb|MEDIUM # Junk Url based autogenerated from various feeds lott.ndb|MEDIUM # Lottery +shelter.ldb|MEDIUM # Phishing and Malware spam.ldb|MEDIUM # Spam detected using the new Logical Signature type spear.ndb|MEDIUM # Spear phishing email addresses (autogenerated from data here) spearl.ndb|MEDIUM # Spear phishing urls (autogenerated from data here) -badmacro.ndb|MEDIUM # Blocks dangerous macros embedded in Word/Excel/Xml/RTF/JS documents -shelter.ldb|MEDIUM # Phishing and Malware - -### MALWARE.EXPERT https://malware.expert/ -# LOW -malware.expert.hdb|MEDIUM # statics MD5 pattern for files -# MEDIUM -malware.expert.fp|MEDIUM # found to be false positive malware -malware.expert.ldb|MEDIUM # which use multi-words search for malware in files -malware.expert.ndb|MEDIUM # Generic Hex pattern PHP malware, which can cause false positive alarms ### FOXHOLE http://sanesecurity.com/foxhole-databases/ # LOW -foxhole_generic.cdb|LOW # See Foxhole page for more details foxhole_filename.cdb|LOW # See Foxhole page for more details +foxhole_generic.cdb|LOW # See Foxhole page for more details # MEDIUM foxhole_js.cdb|MEDIUM # See Foxhole page for more details foxhole_js.ndb|MEDIUM # See Foxhole page for more details @@ -228,21 +238,21 @@ foxhole_mail.cdb|HIGH # block any mail that contains a possible dangerous attach ### OITC http://www.oitc.com/winnow/clamsigs/index.html ### Note: the two databases winnow_phish_complete.ndb and winnow_phish_complete_url.ndb should NOT be used together. # LOW -winnow_malware.hdb|LOW # Current virus, trojan and other malware not yet detected by ClamAV. -winnow_malware_links.ndb|LOW # Links to malware -winnow_extended_malware.hdb|LOW # contain hand generated signatures for malware -winnow.attachments.hdb|LOW # Spammed attachments such as pdf/doc/rtf/zip as well as malware crypted configs winnow_bad_cw.hdb|LOW # md5 hashes of malware attachments acquired directly from a group of botnets +winnow_extended_malware.hdb|LOW # contain hand generated signatures for malware +winnow_malware_links.ndb|LOW # Links to malware +winnow_malware.hdb|LOW # Current virus, trojan and other malware not yet detected by ClamAV. winnow_phish_complete_url.ndb|LOWMEDIUMONLY # Similar to winnow_phish_complete.ndb except that entire urls are used +winnow.attachments.hdb|LOW # Spammed attachments such as pdf/doc/rtf/zip as well as malware crypted configs # MEDIUM +winnow_extended_malware_links.ndb|MEDIUM # contain hand generated signatures for malware links winnow_spam_complete.ndb|MEDIUM # Signatures to detect fraud and other malicious spam winnow.complex.patterns.ldb|MEDIUM # contain hand generated signatures for malware and some egregious fraud -winnow_extended_malware_links.ndb|MEDIUM # contain hand generated signatures for malware links # HIGH winnow_phish_complete.ndb|HIGH # Phishing and other malicious urls and compromised hosts **DO NOT USE WITH winnow_phish_complete_url** ### OITC YARA Format rules -### Note: Yara signatures require ClamAV 0.99 or newer to work -winnow_malware.yara|LOW # detect spam +### Note: Yara signatures require ClamAV 0.100 or newer to work +winnow_malware.yara|DISABLED # Duplicated in EMAIL_Cryptowall.yar and no longer maintaned ### MiscreantPunch http://malwarefor.me/about/ ## MEDIUM @@ -252,14 +262,14 @@ MiscreantPunch099-INFO-Low.ldb|HIGH # ruleset provides context to various files. ### SCAMNAILER http://www.scamnailer.info/ # MEDIUM -scamnailer.ndb|MEDIUM # Spear phishing and other phishing emails +scamnailer.ndb|DISABLED # Spear phishing and other phishing emails, service has been discontinued https://github.com/extremeshok/clamav-unofficial-sigs/issues/365 ### BOFHLAND http://clamav.bofhland.org/ # LOW bofhland_cracked_URL.ndb|LOW # Spam URLs +bofhland_malware_attach.hdb|LOW # Malware Hashes bofhland_malware_URL.ndb|LOW # Malware URLs bofhland_phishing_URL.ndb|LOW # Phishing URLs -bofhland_malware_attach.hdb|LOW # Malware Hashes ### RockSecurity http://rooksecurity.com/ # LOW @@ -267,12 +277,12 @@ hackingteam.hsb|LOW # Hacking Team hashes based on work by rooksecurity.com ### Porcupine # LOW -porcupine.ndb|LOW # Brazilian e-mail phishing and malware signatures phishtank.ndb|LOW # Online and valid phishing urls from phishtank.com data feed porcupine.hsb|LOW # Sha256 Hashes of VBS and JSE malware, kept for 7 days +porcupine.ndb|LOW # Brazilian e-mail phishing and malware signatures ### Sanesecurity YARA Format rules -### Note: Yara signatures require ClamAV 0.99 or newer to work +### Note: Yara signatures require ClamAV 0.100 or newer to work Sanesecurity_sigtest.yara|LOW # Sanesecurity test signatures Sanesecurity_spam.yara|LOW # Detects Spam emails @@ -290,29 +300,82 @@ declare -a securiteinfo_dbs=( #START SECURITEINFO DATABASES ## REQUIRED, Do NOT disable securiteinfo.ign2|REQUIRED # Signature Whitelist # LOW -securiteinfo.hdb|LOW # Malwares in the Wild javascript.ndb|LOW # Malwares Javascript -securiteinfohtml.hdb|LOW # Malwares HTML +securiteinfo.hdb|LOW # Malwares younger than 3 years. +securiteinfoandroid.hdb|LOW # Malwares Java/Android Dalvik securiteinfoascii.hdb|LOW # Text file malwares (Perl or shell scripts, bat files, exploits, ...) +securiteinfohtml.hdb|LOW # Malwares HTML +securiteinfoold.hdb|LOW # Malwares older than 3 years. securiteinfopdf.hdb|LOW # Malwares PDF -securiteinfoandroid.hdb|LOW # Malwares Java/Android Dalvik # HIGH spam_marketing.ndb|HIGH # Spam Marketing / spammer blacklist ) #END SECURITEINFO DATABASES +# SECURITEINFO PREMIUM (NON-FREE) DATABASES +declare -a securiteinfo_premium_dbs=( #START SECURITEINFO DATABASES +securiteinfo.mdb|LOW # 0-day Malwares +securiteinfo0hour.hdb|LOW # 0-Hour Malwares +) #END NON-FREE SECURITEINFO DATABASES + # ======================== -# Linux Malware Detect Database(s) +# LinuxMalwareDetect Database(s) # ======================== # Add or remove database file names between quote marks as needed. To -# disable any SecuriteInfo database downloads, remove the appropriate +# disable any LinuxMalwareDetect database downloads, remove the appropriate # lines below. declare -a linuxmalwaredetect_dbs=( ### Linux Malware Detect https://www.rfxn.com/projects/linux-malware-detect/ # LOW rfxn.ndb|LOW # HEX Malware detection signatures -rfxn.hdb|LOW # MD5 malware detection signatures +rfxn.hdb|LOW # MD5 Malware detection signatures +rfxn.yara|LOW # Yara Malware detection signatures ) #END LINUXMALWAREDETECT DATABASES +# ======================== +# interServer Database(s) +# ======================== +# Add or remove database file names between quote marks as needed. To +# disable any Malware Expert database downloads, remove the appropriate +# lines below. +declare -a interserver_dbs=( +## REQUIRED, Do NOT disable +whitelist.fp|REQUIRED # found to be false positive malware +# LOW +interserver256.hdb|LOW # 100% known malware sha256 format +# MEDIUM +interservertopline.db|MEDIUM # inserts into files, manual cleaning HEX +# HIGH +shell.ldb|HIGH # 99.9% known malware using logical signatures +) #END Malware Expert DATABASES + +# ======================== +# Malware Expert Database(s) +# ======================== +# Add or remove database file names between quote marks as needed. To +# disable any Malware Expert database downloads, remove the appropriate +# lines below. +declare -a malwareexpert_dbs=( +## REQUIRED, Do NOT disable +malware.expert.fp|REQUIRED # found to be false positive malware +# LOW +malware.expert.hdb|LOW # statics MD5 pattern for files +# MEDIUM +malware.expert.ldb|MEDIUM # which use multi-words search for malware in files +malware.expert.ndb|MEDIUM # Generic Hex pattern PHP malware, which can cause false positive alarms +) #END Malware Expert DATABASES + +# ======================== +# urlhaus Database(s) +# ======================== +# Add or remove database file names between quote marks as needed. To +# disable any urlhaus database downloads, remove the appropriate +# lines below. +declare -a urlhaus_dbs=( +### urlhaus https://urlhaus.abuse.ch/browse/ +# LOW +urlhaus.ndb|LOW # malicious URLs that are being used for malware distribution +) #END URLHAUS DATABASES + # ======================== # Yara Rules Project Database(s) # ======================== @@ -325,33 +388,108 @@ declare -a yararulesproject_dbs=( # Some rules are now in sub-directories. To reference a file in a sub-directory # use subdir/file # LOW -Antidebug_AntiVM/antidebug_antivm.yar|LOW # anti debug and anti virtualization techniques used by malware -Exploit-Kits/EK_Angler.yar|LOW # Angler Exploit Kit Redirector -Exploit-Kits/EK_Blackhole.yar|LOW # BlackHole2 Exploit Kit Detection -Exploit-Kits/EK_BleedingLife.yar|LOW # BleedingLife2 Exploit Kit Detection -Exploit-Kits/EK_Crimepack.yar|LOW # CrimePack Exploit Kit Detection -Exploit-Kits/EK_Eleonore.yar|LOW # Eleonore Exploit Kit Detection -Exploit-Kits/EK_Fragus.yar|LOW # Fragus Exploit Kit Detection -Exploit-Kits/EK_Phoenix.yar|LOW # Phoenix Exploit Kit Detection -Exploit-Kits/EK_Sakura.yar|LOW # Sakura Exploit Kit Detection -Exploit-Kits/EK_ZeroAcces.yar|LOW # ZeroAccess Exploit Kit Detection -Exploit-Kits/EK_Zerox88.yar|LOW # 0x88 Exploit Kit Detection -Exploit-Kits/EK_Zeus.yar|LOW # Zeus Exploit Kit Detection +# Anti debug and anti virtualization techniques used by malware +antidebug_antivm/antidebug_antivm.yar|DISABLED # (core dumped) +# Aimed toward the detection and existence of Exploit Kits. +exploit_kits/EK_Angler.yar|DISABLED # duplicated in rxfn.yara +exploit_kits/EK_Blackhole.yar|DISABLED # duplicated in rxfn.yara +exploit_kits/EK_BleedingLife.yar|LOW # duplicated in rxfn.yara +exploit_kits/EK_Crimepack.yar|DISABLED # duplicated in rxfn.yara +exploit_kits/EK_Eleonore.yar|DISABLED # duplicated in rxfn.yara +exploit_kits/EK_Fragus.yar|DISABLED # duplicated in rxfn.yara +exploit_kits/EK_Phoenix.yar|DISABLED # duplicated in rxfn.yara +exploit_kits/EK_Sakura.yar|DISABLED # duplicated in rxfn.yara +exploit_kits/EK_ZeroAcces.yar|DISABLED # duplicated in rxfn.yara +exploit_kits/EK_Zerox88.yar|DISABLED # duplicated in rxfn.yara +exploit_kits/EK_Zeus.yar|DISABLED # duplicated in rxfn.yara +#Identification of well-known webshells +webshells/WShell_APT_Laudanum.yar|DISABLED # duplicated in rxfn.yara +webshells/WShell_ASPXSpy.yar|LOW +webshells/WShell_Drupalgeddon2_icos.yar|LOW +webshells/WShell_PHP_Anuna.yar|DISABLED # duplicated in rxfn.yara +webshells/WShell_PHP_in_images.yar|DISABLED # duplicated in rxfn.yara +webshells/WShell_THOR_Webshells.yar|DISABLED # duplicated in rxfn.yara +webshells/Wshell_ChineseSpam.yar|DISABLED # duplicated in rxfn.yara +webshells/Wshell_fire2013.yar|DISABLED # duplicated in rxfn.yara # MEDIUM -Malicious_Documents/maldoc_somerules.yar|MEDIUM # documents with malicious code -Malicious_Documents/Maldoc_Hidden_PE_file.yar|MEDIUM # Detect a hidden PE file inside a sequence of numbers (comma separated) -Packers/packer.yar|MEDIUM # well-known sofware packers -CVE_Rules/CVE-2010-0805.yar|MEDIUM # CVE 2010 0805 -CVE_Rules/CVE-2010-0887.yar|MEDIUM # CVE 2010 0887 -CVE_Rules/CVE-2010-1297.yar|MEDIUM # CVE 2010 1297 -CVE_Rules/CVE-2013-0074.yar|MEDIUM # CVE 2013 0074 -CVE_Rules/CVE-2013-0422.yar|MEDIUM # CVE 2013 0422 -CVE_Rules/CVE-2015-5119.yar|MEDIUM # CVE 2015 5119 +# Identification of specific Common Vulnerabilities and Exposures (CVEs) +cve_rules/CVE-2010-0805.yar|MEDIUM +cve_rules/CVE-2010-0887.yar|MEDIUM +cve_rules/CVE-2010-1297.yar|MEDIUM +cve_rules/CVE-2012-0158.yar|MEDIUM +cve_rules/CVE-2013-0074.yar|MEDIUM +cve_rules/CVE-2013-0422.yar|MEDIUM +cve_rules/CVE-2015-1701.yar|MEDIUM +cve_rules/CVE-2015-2426.yar|MEDIUM +cve_rules/CVE-2015-2545.yar|MEDIUM +cve_rules/CVE-2015-5119.yar|MEDIUM +cve_rules/CVE-2016-5195.yar|MEDIUM +cve_rules/CVE-2017-11882.yar|MEDIUM +cve_rules/CVE-2018-20250.yar|MEDIUM +cve_rules/CVE-2018-4878.yar|MEDIUM +# Identification of malicious e-mails. +email/bank_rule.yar|MEDIUM +email/EMAIL_Cryptowall.yar|MEDIUM +email/Email_fake_it_maintenance_bulletin.yar|MEDIUM +email/Email_quota_limit_warning.yar|MEDIUM +email/email_Ukraine_BE_powerattack.yar|MEDIUM +email/scam.yar|MEDIUM +# Detect well-known software packers, that can be used by malware to hide itself. +packers/JJencode.yar|DISABLED # Causes high CPU load with email attachments (images) https://github.com/extremeshok/clamav-unofficial-sigs/issues/362 # HIGH -Packers/Javascript_exploit_and_obfuscation.yar|HIGH # JavaScript Obfuscation Detection -Crypto/crypto.yar|HIGH # detect the existence of cryptographic algoritms +# Used with documents to find if they have been crafted to leverage malicious code. +email/Email_generic_phishing.yar|HIGH +maldocs/Maldoc_APT_OLE_JSRat.yar|HIGH +maldocs/Maldoc_APT10_MenuPass.yar|HIGH +maldocs/Maldoc_APT19_CVE-2017-0199.yar|HIGH +maldocs/Maldoc_Contains_VBE_File.yar|HIGH +maldocs/Maldoc_CVE_2017_11882.yar|HIGH +maldocs/Maldoc_CVE_2017_8759.yar|HIGH +maldocs/Maldoc_CVE-2017-0199.yar|HIGH +maldocs/Maldoc_DDE.yar|HIGH +maldocs/Maldoc_Dridex.yar|HIGH +maldocs/Maldoc_hancitor_dropper.yar|HIGH +maldocs/Maldoc_Hidden_PE_file.yar|HIGH +maldocs/Maldoc_malrtf_ole2link.yar|HIGH +maldocs/Maldoc_MIME_ActiveMime_b64.yar|HIGH +maldocs/Maldoc_PDF.yar|HIGH +maldocs/Maldoc_PowerPointMouse.yar|HIGH +maldocs/maldoc_somerules.yar|HIGH +maldocs/Maldoc_Suspicious_OLE_target.yar|HIGH +maldocs/Maldoc_UserForm.yar|HIGH +maldocs/Maldoc_VBA_macro_code.yar|HIGH +maldocs/Maldoc_Word_2007_XML_Flat_OPC.yar|HIGH +# Yara Rules aimed to detect well-known software packers, that can be used by malware to hide itself. +packers/Javascript_exploit_and_obfuscation.yar|HIGH +# DISABLED +# NOT SUPPORTED OR CRASHING CLAMAV +email/attachment.yar|DISABLED # detects all emails with attachments +email/image.yar|DISABLED # detects all emails with images +email/urls.yar|DISABLED # detects all emails with urls +crypto/crypto_signatures.yar|DISABLED # detects all files which are encrypted +# These files use module includes not supported by ClamAV +packers/packer_compiler_signatures.yar|DISABLED +packers/packer.yar|DISABLED +packers/peid.yar|DISABLED +antidebug_antivm|DISABLED ) #END yararulesproject DATABASES +declare -a yararulesproject_dbs_catagories=( +#LOW +cve_rules|LOW +exploit_kits|LOW +malware|LOW +webshells|LOW +#MEDIUM +email|MEDIUM +maldocs|MEDIUM +# HIGH +capabilities|HIGH +crypto|HIGH +packers|HIGH +) + + # ========================= # Additional signature databases # ========================= @@ -373,6 +511,29 @@ Crypto/crypto.yar|HIGH # detect the existence of cryptographic algoritms # http://www.example.org/sigs.ldb #) #END ADDITIONAL DATABASES +# ================================================== +# ================================================== +# D E B U G O P T I O N S +# ================================================== +# ================================================== + +# Enable debugging, will cause all options below to enable +debug="no" + +# Causes the xshok_file_download function to be verbose, used for debugging +downloader_debug="no" + +# Causes clamscan signature test errors to be vebose +clamscan_debug="no" + +# Causes curl errors to be vebose +curl_debug="no" + +# Causes wget errors to be vebose +wget_debug="no" + +# Causes rsync errors to be vebose +rsync_debug="no" # ================================================== # ================================================== @@ -380,6 +541,21 @@ Crypto/crypto.yar|HIGH # detect the existence of cryptographic algoritms # ================================================== # ================================================== +# Branch for update checking, default: master +git_branch="master" + +# Enable support for script and master.conf upgrades +# enbles the --upgrade command line option +# packagers, if required please disable or set this option to no in the os.conf +allow_upgrades="yes" + +# Enable support for script and master.conf update checks +# packagers, if required please disable or set this option to no in the os.conf +allow_update_checks="yes" + +# How often the script should check for updates +update_check_hours="12"# Default is 12 hours (2 checks daily). + # Enable or disable download time randomization. This allows the script to # be executed via cron, but the actual database file checking will pause # for a random number of seconds between the "min" and "max" time settings @@ -393,19 +569,28 @@ enable_locking="yes" # If download time randomization is enabled above (enable_random="yes"), # then set the min and max radomization time intervals (in seconds). -min_sleep_time="60" # Default minimum is 60 seconds (1 minute). max_sleep_time="600" # Default maximum is 600 seconds (10 minutes). +min_sleep_time="60" # Default minimum is 60 seconds (1 minute). # Command to do a full clamd service stop/start #clamd_restart_opt="service clamd restart" # Custom Command Paths, these are detected with the which command when not set -#uname_bin="/usr/bin/uname" #clamscan_bin="/usr/bin/clamscan" -#rsync_bin="/usr/bin/rsync" -#wget_bin="/usr/bin/wget" #curl_bin="/usr/bin/curl" #gpg_bin="/usr/bin/gpg" +#rsync_bin="/usr/bin/rsync" +#tar_bin="/usr/bin/tar" +#uname_bin="/usr/bin/uname" +#wget_bin="/usr/bin/wget" +#dig_bin="usr/bin/dig" +#host_bin="/usr/bin/host" + +# force wget, by default curl is used when curl and wget is present. +force_wget="no" + +# force host, by default dig is used when dig and host is present. +force_host="no" # GnuPG / Signature verification # To disable usage of gpg, set the following variable to "no". @@ -428,31 +613,35 @@ enable_gpg="yes" rsync_connect_timeout="60" rsync_max_time="180" -# Ignore ssl errors and warnings, ie. operate in insecure mode. -downloader_ignore_ssl="yes" # Default is "yes" ignore ssl errors and warnings +# HTTPS validation +# Uncomment to allow and ignore SSL errors leading to insecure transfers +# downloader_ignore_ssl_errors="yes" # Default is "no" # Set downloader connection, data transfer timeout limits in seconds. # The defaults settings here are reasonable, only change if you are # experiencing timeout issues. downloader_connect_timeout="60" -downloader_max_time="180" +downloader_max_time="1800" # Set downloader retry count for failed transfers -downloader_tries="3" +downloader_tries="5" # Set working directory paths (edit to meet your own needs). If these # directories do not exist, the script will attempt to create them. # Always located inside the work_dir, do not add / # Sub-directory names: -sanesecurity_dir="dbs-ss" # Sanesecurity sub-directory -securiteinfo_dir="dbs-si" # SecuriteInfo sub-directory +add_dir="dbs-add" # User defined databases sub-directory +gpg_dir="gpg-key" # Sanesecurity GPG Key sub-directory +interserver_dir="dbs-is" # interServer sub-directory linuxmalwaredetect_dir="dbs-lmd" # Linux Malware Detect sub-directory +malwareexpert_dir="dbs-me" # Malware Expert sub-directory malwarepatrol_dir="dbs-mbl" # MalwarePatrol sub-directory -yararulesproject_dir="dbs-yara" # Yara-Rules sub-directory -work_dir_configs="configs" # Script configs sub-directory -gpg_dir="gpg-key" # Sanesecurity GPG Key sub-directory pid_dir="pid" # User defined pid sub-directory -add_dir="dbs-add" # User defined databases sub-directory +sanesecurity_dir="dbs-ss" # Sanesecurity sub-directory +securiteinfo_dir="dbs-si" # SecuriteInfo sub-directory +urlhaus_dir="dbs-uh" # urlhaus sub-directory +work_dir_configs="configs" # Script configs sub-directory +yararulesproject_dir="dbs-yara" # Yara-Rules sub-directory # If you would like to make a backup copy of the current running database # file before updating, leave the following variable set to "yes" and a @@ -464,7 +653,7 @@ keep_db_backup="no" remove_bad_database="yes" # When a database is disabled we will remove the associated database files. -remove_disabled_databases="no" # Default is "no" since we are not a database managament tool by default. +remove_disabled_databases="yes" # Default is "yes" # Enable SELinux fixes, ie. running restorecon on the database files. # **Run the following command as root to enable clamav selinux support** @@ -472,32 +661,33 @@ remove_disabled_databases="no" # Default is "no" since we are not a database man # selinux_fixes="no" # Default is "no" ignore ssl errors and warnings -# If necessary to proxy database downloads, define the rsync and/or wget -# proxy settings here. For rsync, the proxy must support connections to -# port 873. Both wget and rsync proxy setting need to be defined in the -# format of "hostname:port". For wget, also note the https and http -#rsync_proxy="" -#curl_proxy="" -#wget_proxy_http="-e http_proxy=http://username:password@proxy_host:proxy_port" -#wget_proxy_https="-e https_proxy=https://username:password@proxy_host:proxy_port" - +# Proxy Support +# If necessary to proxy database downloads, define the rsync, curl, wget, dig, hosr proxy settings here. +#rsync_proxy="username:password@proxy_host:proxy_port" +# Define rsync to use netcat for socks tunnel +#rsync_connect_prog="nc -X 5 -x socksproxy_host:socksproxy_port %H 873" +#curl_proxy="--proxy http://username:password@proxy_host:proxy_port" +#wget_proxy="-e http_proxy=http://username:password@proxy_host:proxy_port -e https_proxy=https://username:password@proxy_host:proxy_port" +#dig_proxy="@proxy_host -p proxy_host:proxy_port" +#host_proxy="@proxy_host" #does not support port # Custom Cron install settings, these are detected and only used if you want to override # the automatic detection and generation of the values when not set, this is mainly to aid package maintainers +#cron_bash="" #default: detected with the which command #cron_dir="" #default: /etc/cron.d #cron_filename="" #default: clamav-unofficial-sigs #cron_minute="" #default: random value between 0-59 -#cron_user="" #default: uses the clam_user -#cron_bash="" #default: detected with the which command #cron_script_full_path="" #default: detected to the fullpath of the script +#cron_sudo="no" #default no, yes will append sudo -u before the username +#cron_user="" #default: uses the clam_user # Custom logrotate install settings, these are detected and only used if you want to override # the automatic detection and generation of the values when not set, this is mainly to aid package maintainers #logrotate_dir="" #default: /etc/logrotate.d #logrotate_filename="" #default: clamav-unofficial-sigs -#logrotate_user="" #default: uses the clam_user #logrotate_group="" #default: uses the clam_group #logrotate_log_file_full_path="" #default: detected to the $log_file_path/$log_file_name +#logrotate_user="" #default: uses the clam_user # Custom man install settings, these are detected and only used if you want to override # the automatic detection and generation of the values when not set, this is mainly to aid package maintainers @@ -513,15 +703,18 @@ selinux_fixes="no" # Default is "no" ignore ssl errors and warnings # Custom full working directory paths, these are detected and only used if you want to override # the automatic detection and generation of the values when not set, this is mainly to aid package maintainers -#work_dir_sanesecurity="" #default: uses work_dir/sanesecurity_dir -#work_dir_securiteinfo="" #default: uses work_dir/securiteinfo_dir -#work_dir_linuxmalwaredetect="" #default: uses work_dir/linuxmalwaredetect_dir -#work_dir_malwarepatrol="" #default: uses work_dir/malwarepatrol_dir -#work_dir_yararulesproject="" #default: uses work_dir/yararulesproject_dir #work_dir_add="" #default: uses work_dir/add_dir -#work_dir_work_configs="" #default: uses work_dir/work_dir_configs #work_dir_gpg="" #default: uses work_dir/gpg_dir +#work_dir_interserver="" #default: uses work_dir/interserver_dir +#work_dir_linuxmalwaredetect="" #default: uses work_dir/linuxmalwaredetect_dir +#work_dir_malwareexpert="" #default: uses work_dir/malwareexpert_dir +#work_dir_malwarepatrol="" #default: uses work_dir/malwarepatrol_dir #work_dir_pid="" #default: uses work_dir/pid_dir +#work_dir_sanesecurity="" #default: uses work_dir/sanesecurity_dir +#work_dir_securiteinfo="" #default: uses work_dir/securiteinfo_dir +#work_dir_urlhaus="" #default: uses work_dir/urlhaus_dir +#work_dir_work_configs="" #default: uses work_dir/work_dir_configs +#work_dir_yararulesproject="" #default: uses work_dir/yararulesproject_dir # ======================== # After you have completed the configuration of this file, set the value to "yes" @@ -530,15 +723,24 @@ user_configuration_complete="no" # ======================== # DO NOT EDIT ! # Database provider URLs +interserver_url="https://sigs.interserver.net" +linuxmalwaredetect_sigpack_url="https://cdn.rfxn.com/downloads/maldet-sigpack.tgz" +linuxmalwaredetect_version_url="https://cdn.rfxn.com/downloads/maldet.sigs.ver" +malwareexpert_url="https://signatures.malware.expert" +malwarepatrol_url="https://lists.malwarepatrol.net/cgi/getfile" +sanesecurity_gpg_url="https://www.sanesecurity.com/publickey.gpg" sanesecurity_url="rsync.sanesecurity.net" -sanesecurity_gpg_url="http://www.sanesecurity.net/publickey.gpg" securiteinfo_url="https://www.securiteinfo.com/get/signatures" -linuxmalwaredetect_url="http://cdn.rfxn.com/downloads" -malwarepatrol_url="https://lists.malwarepatrol.net/cgi/getfile" +urlhaus_url="https://urlhaus.abuse.ch/downloads" yararulesproject_url="https://raw.githubusercontent.com/Yara-Rules/rules/master" # ======================== # DO NOT EDIT ! -config_version="73" +config_version="97" +################################################################################ +# +# DO NOT EDIT THIS FILE !! DO NOT EDIT THIS FILE !! DO NOT EDIT THIS FILE !! +# +################################################################################ # https://eXtremeSHOK.com ###################################################### diff --git a/rootfs/etc/clamav-unofficial-sigs/os.conf b/rootfs/etc/clamav-unofficial-sigs/os.conf new file mode 100644 index 00000000..4fc70c90 --- /dev/null +++ b/rootfs/etc/clamav-unofficial-sigs/os.conf @@ -0,0 +1,39 @@ +# This file contains os configuration settings for clamav-unofficial-sigs.sh +################### +# This is property of eXtremeSHOK.com +# You are free to use, modify and distribute, however you may not remove this notice. +# Copyright (c) Adrian Jon Kriel :: admin@extremeshok.com +# License: BSD (Berkeley Software Distribution) +################## +# +# Script updates can be found at: https://github.com/extremeshok/clamav-unofficial-sigs +# +################## +# +# NOT COMPATIBLE WITH VERSION 3.XX / 4.XX CONFIG +# +################################################################################ +# SEE MASTER.CONF FOR CONFIG EXPLANATIONS +################################################################################ +# Rename to os.conf to enable this file +################################################################################ + +# Debian 9+ (stretch, buster) + +clam_user="clamav" +clam_group="clamav" + +logrotate_group="adm" + +clam_dbs="/var/lib/clamav" + +clamd_pid="/run/clamav/clamd.pid" + +clamd_reload_opt="s6-svc -2 /services/clamd" +# this is in case the needed/expected keyword is now restart and not reload +clamd_restart_opt="s6-svc -2 /services/clamd" + +# this is enabled here https://github.com/mailserver2/mailserver/blob/master/rootfs/etc/clamav/unofficial-sigs/os.conf#L6 +clamd_socket="/run/clamav/clamd.ctl" + +# https://eXtremeSHOK.com ###################################################### diff --git a/rootfs/etc/clamav/unofficial-sigs/os.conf b/rootfs/etc/clamav/unofficial-sigs/os.conf deleted file mode 100644 index 68457e22..00000000 --- a/rootfs/etc/clamav/unofficial-sigs/os.conf +++ /dev/null @@ -1,9 +0,0 @@ -clam_user="clamav" -clam_group="clamav" -logrotate_group="adm" -clam_dbs="/var/lib/clamav" -clamd_pid="/run/clamav/clamd.pid" -clamd_socket="/run/clamav/clamd.ctl" -clamd_reload_opt="s6-svc -2 /services/clamd" -min_sleep_time="30" -max_sleep_time="60" diff --git a/rootfs/usr/local/bin/clamav-unofficial-sigs.sh b/rootfs/usr/local/bin/clamav-unofficial-sigs.sh old mode 100755 new mode 100644 index 3eecf06b..36b7cfb2 --- a/rootfs/usr/local/bin/clamav-unofficial-sigs.sh +++ b/rootfs/usr/local/bin/clamav-unofficial-sigs.sh @@ -1,6 +1,8 @@ -#!/bin/bash -# shellcheck disable=SC2154 +#!/usr/bin/env bash +# shellcheck disable=SC2119 +# shellcheck disable=SC2120 # shellcheck disable=SC2128 +# shellcheck disable=SC2154 ################################################################################ # This is property of eXtremeSHOK.com # You are free to use, modify and distribute, however you may not remove this notice. @@ -29,70 +31,67 @@ ################################################################################ - # Detect to make sure the entire script is avilable, fail if the script is missing contents -if [ "$(tail -n 1 "$0" | head -n 1 | cut -c 1-7)" != "exit \$?" ] ; then - echo "FATAL ERROR: Script is incomplete, please redownload" - exit 1 +if [ "$(tail -n 1 "${0}" | head -n 1 | cut -c 1-7)" != "exit \$?" ] ; then + echo "FATAL ERROR: Script is incomplete, please redownload" + exit 1 fi # Trap the keyboard interrupt (Ctrl + C) trap xshok_control_c SIGINT - ################################################################################ # HELPER FUNCTIONS ################################################################################ # Support user config settings for applying file and directory access permissions. -function perms () { - if [ -n "$clam_user" ] && [ -n "$clam_group" ] ; then +function perms() { + if [ -n "${clam_user}" ] && [ -n "${clam_group}" ] ; then "${@:-}" fi } # Prompt a user if they should complete an action with Y or N # Usage: xshok_prompt_confirm -# if xshok_prompt_confirm; then +# if xshok_prompt_confirm ; then # xshok_prompt_confirm && echo "accepted" # xshok_prompt_confirm && echo "yes" || echo "no" # shellcheck disable=SC2120 -function xshok_prompt_confirm () { # optional_message +function xshok_prompt_confirm() { # optional_message message="${1:-Are you sure?}" while true; do - read -r -p "$message [y/N]" response < /dev/tty - case "$response" in + read -r -p "${message} [y/N]" response < /dev/tty + case "${response}" in [yY]) return 0 ;; [nN]) return 1 ;; - *) printf " \033[31m %s \n\033[0m" "invalid input" + *) printf " \\033[31m %s \\n\\033[0m" "invalid input" esac done } # Create a pid file -function xshok_create_pid_file () { # pid.file - if [ "$1" ] ; then - pidfile="$1" - echo $$ > "$pidfile" - if [ $? -ne 0 ] ; then - xshok_pretty_echo_and_log "ERROR: Could not create PID file: $pidfile" +function xshok_create_pid_file() { # pid.file + if [ "${1}" ] ; then + pidfile="${1}" + if ! echo $$ > "${pidfile}" ; then + xshok_pretty_echo_and_log "ERROR: Could not create PID file: ${pidfile}" exit 1 fi else - xshok_pretty_echo_and_log "ERROR: Missing value for option" "=" + xshok_pretty_echo_and_log "ERROR: Missing value for option" exit 1 fi } # Intercept ctrl+c and calls the cleanup function -function xshok_control_c () { +function xshok_control_c() { echo - xshok_pretty_echo_and_log "--------------| Exiting ... Please wait |--------------" "-" + xshok_pretty_echo_and_log "---------------| Exiting ... Please wait |---------------" "-" xshok_cleanup exit $? } # Cleanup function -function xshok_cleanup () { +function xshok_cleanup() { # Wait for all processes to end wait xshok_pretty_echo_and_log " Powered By https://eXtremeSHOK.com " "#" @@ -100,11 +99,11 @@ function xshok_cleanup () { } # Check if the current running user is the root user, otherwise return false -function xshok_is_root () { +function xshok_is_root() { if [ "$(uname -s)" == "SunOS" ] ; then id_bin="/usr/xpg4/bin/id" else - id_bin="$(which id 2> /dev/null)" + id_bin="$(command -v id 2> /dev/null)" fi if [ "$($id_bin -u)" == 0 ] ; then return 0 @@ -114,8 +113,8 @@ function xshok_is_root () { } # Check if its a file, otherwise return false -function xshok_is_file () { # filepath - filepath="$1" +function xshok_is_file() { # filepath + filepath="${1}" if [ -f "${filepath}" ] ; then return 0 ; else @@ -127,7 +126,7 @@ function xshok_is_file () { # filepath # Usage: xshok_is_subdir "filepath" # xshok_is_subdir "/root/" - false # xshok_is_subdir "/usr/local/etc" && echo "yes" - yes -function xshok_is_subdir () { # filepath +function xshok_is_subdir() { # filepath shopt -s extglob; filepath="${filepath%%+(/)}" if [ -d "$filepath" ] ; then res="${filepath//[^\/]}" @@ -142,16 +141,15 @@ function xshok_is_subdir () { # filepath } # Create a dir and set the ownership -function xshok_mkdir_ownership () { # path - if [ "$1" ] ; then - mkdir -p "$1" 2>/dev/null - if [ $? -ne 0 ] ; then - xshok_pretty_echo_and_log "ERROR: Could not create directory: $1" +function xshok_mkdir_ownership() { # path + if [ "${1}" ] ; then + if ! mkdir -p "${1}" 2>/dev/null ; then + xshok_pretty_echo_and_log "ERROR: Could not create directory: ${1}" exit 1 fi - perms chown -f "$clam_user:$clam_group" "$1" > /dev/null 2>&1 + perms chown -f "${clam_user}:${clam_group}" "${1}" > /dev/null 2>&1 else - xshok_pretty_echo_and_log "ERROR: Missing value for option" "=" + xshok_pretty_echo_and_log "ERROR: Missing value for option" exit 1 fi } @@ -160,30 +158,29 @@ function xshok_mkdir_ownership () { # path # Usage: # xshok_is_subdir "username" && echo "user found" || echo "no" # xshok_is_subdir "username" "groupname" && echo "user and group found" || echo "no" -function xshok_user_group_exists () { # username groupname +function xshok_user_group_exists() { # username groupname if [ "$(uname -s)" == "SunOS" ] ; then id_bin="/usr/xpg4/bin/id" else - id_bin="$(which id 2> /dev/null)" + id_bin="$(command -v id 2> /dev/null)" fi - if [ "$2" ] ; then + if [ "${2}" ] ; then if [ "$(uname -s)" == "Darwin" ] ; then #use ruby, as this is the best way. Ruby is always avilable as brew uses ruby ruby -e 'require "etc"; puts Etc::getgrnam("_clamav").gid' > /dev/null 2>&1 ret="$?" else - getent_bin="$(which getent 2> /dev/null)" - $getent_bin group "$2" >/dev/null 2>&1 + getent_bin="$(command -v getent 2> /dev/null)" + $getent_bin group "${2}" >/dev/null 2>&1 ret="$?" fi fi - if [ "$1" ] ; then - $id_bin -u "$1" > /dev/null 2>&1 - if [ $? -eq 0 ]; then - if [ "$2" ] ; then - if [ "$ret" -eq 0 ]; then + if [ "${1}" ] ; then + if $id_bin -u "${1}" > /dev/null 2>&1 ; then + if [ "${2}" ] ; then + if [ "$ret" -eq 0 ] ; then return 0 ; # User and group exists else return 1 ; # Group does NOT exist @@ -195,7 +192,7 @@ function xshok_user_group_exists () { # username groupname return 1 ; # User does NOT exist fi else - xshok_pretty_echo_and_log "ERROR: Missing value for option" "=" + xshok_pretty_echo_and_log "ERROR: Missing value for option" exit 1 fi } @@ -214,146 +211,192 @@ function xshok_user_group_exists () { # username groupname # ======== # pretty_echo_and_log "" "/\" "7" # /\/\/\/\/\/\ -# type: e = error, w= warning "" -function xshok_pretty_echo_and_log () { # "string" "repeating" "count" "type" - # Handle comments - if [ "$comment_silence" == "no" ] ; then - if [ "${#@}" -eq 1 ] ; then - echo "$1" +# type: e = error, w= warning, a = alert, n = notice +# will auto detect using the first word "error,warning,alert,notice" +# type e will make a == border +# type w will make a -- border +# type a will make a ** border +# type n will make a ++ border +function xshok_pretty_echo_and_log() { # "string" "repeating" "count" "type" + #detect if running under cron and silence + mystring="$1" + myrepeating="$2" + mycount="$3" + mytype="$4" + if [ "$comment_silence" != "yes" ] && [ "$force_verbose" != "yes" ]; then + if [ ! -t 1 ] ; then + comment_silence="yes" + fi + fi + # always show errors and alerts + if [ -z "$mytype" ] ; then + shopt -s nocasematch + if [[ "$mystring" =~ "ERROR:" ]] || [[ "$mystring" =~ "ERROR " ]] ; then + mytype="e" + elif [[ "$mystring" =~ "WARNING:" ]] || [[ "$mystring" =~ "WARNING " ]] ; then + mytype="w" + elif [[ "$mystring" =~ "ALERT:" ]] || [[ "$mystring" =~ "ALERT " ]] ; then + mytype="a" + elif [[ "$mystring" =~ "NOTICE:" ]] || [[ "$mystring" =~ "NOTICE " ]] ; then + mytype="n" + fi + fi + if [ "$mytype" == "e" ] || [ "$mytype" == "a" ] ; then + comment_silence="no" + fi + # Handle comments is not silenced or type + if [ "$comment_silence" != "yes" ] ; then + if [ -z "$myrepeating" ] ; then + if [ "$mytype" == "e" ] ; then + myrepeating="=" + elif [ "$mytype" == "w" ] ; then + myrepeating="-" + elif [ "$mytype" == "a" ] ; then + myrepeating="*" + elif [ "$mytype" == "n" ] ; then + myrepeating="+" + fi + fi + if [ -z "$myrepeating" ] ; then + echo "${mystring}" else myvar="" - if [ -n "$3" ] ; then - mycount="$3" - else - mycount="${#1}" + if [ -z "$mycount" ] ; then + mycount="${#mystring}" fi for (( n = 0; n < mycount; n++ )) ; do - myvar="$myvar$2" + myvar="${myvar}${myrepeating}" done - if [ -n "$1" ] ; then - echo -e "$myvar\n$1\n$myvar" + if [ -n "${mystring}" ] ; then + echo -e "${myvar}\\n${1}\\n${myvar}" else - echo -e "$myvar" + echo -e "${myvar}" fi fi fi - # Handle logging if [ "$enable_log" == "yes" ] ; then - if [ ! -e "$log_file_path/$log_file_name" ] ; then - # xshok_mkdir_ownership "$log_file_path" - mkdir -p "$log_file_path" - touch "$log_file_path/$log_file_name" 2>/dev/null - perms chown -f "$clam_user:$clam_group" "$log_file_path/$log_file_name" - fi - if [ ! -w "$log_file_path/$log_file_name" ] ; then - echo "Warning: Logging Disabled, as file not writable: $log_file_path/$log_file_name" - enable_log="no" - else - echo "$(date "+%b %d %T")" "$1" >> "$log_file_path/$log_file_name" - fi + + #filter ===, --- + mystring=${1//===} + mystring=${mystring//---} + + if [ -n "$mystring" ] ; then + if [ -n "$log_pipe_cmd" ] ; then + echo "${mystring}" | $log_pipe_cmd + else + if [ ! -e "${log_file_path}/${log_file_name}" ] ; then + # xshok_mkdir_ownership "$log_file_path" + mkdir -p "$log_file_path" + touch "${log_file_path}/${log_file_name}" 2>/dev/null + perms chown -f "${clam_user}:${clam_group}" "${log_file_path}/${log_file_name}" + fi + if [ ! -w "${log_file_path}/${log_file_name}" ] ; then + echo "WARNING: Logging Disabled, as file not writable: ${log_file_path}/${log_file_name}" + enable_log="no" + else + echo "$(date "+%b %d %T")" "${mystring}" >> "${log_file_path}/${log_file_name}" + fi + fi + fi fi } # Check if the $2 value is not null and does not start with - -function xshok_check_s2 () { # value1 value2 - if [ "$1" ] ; then - if [[ "$1" =~ ^-.* ]] ; then - xshok_pretty_echo_and_log "ERROR: Missing value for option or value begins with -" "=" +function xshok_check_s2() { # value1 value2 + if [ "${1}" ] ; then + if [[ "${1}" =~ ^-.* ]] ; then + xshok_pretty_echo_and_log "ERROR: Missing value for option or value begins with -" exit 1 fi else - xshok_pretty_echo_and_log "ERROR: Missing value for option" "=" + xshok_pretty_echo_and_log "ERROR: Missing value for option" exit 1 fi } # Time remaining information function -function xshok_draw_time_remaining () { #time_remaining #update_hours #name - if [ "$1" ] && [ "$2" ]; then - time_remaining="$1" +function xshok_draw_time_remaining() { #time_remaining #update_hours #name + if [ "${1}" ] && [ "${2}" ] ; then + time_remaining="${1}" hours_left="$((time_remaining / 3600))" minutes_left="$((time_remaining % 3600 / 60))" - xshok_pretty_echo_and_log "$2 hours have not yet elapsed since the last $3 update check" + xshok_pretty_echo_and_log "${2} hours have not yet elapsed since the last ${3} update check" xshok_pretty_echo_and_log "No update check was performed at this time" "-" - xshok_pretty_echo_and_log "Next check will be performed in approximately $hours_left hour(s), $minutes_left minute(s)" + xshok_pretty_echo_and_log "Next check will be performed in approximately ${hours_left} hour(s), ${minutes_left} minute(s)" fi } # Download function -function xshok_file_download () { #outputfile #url - if [ "$1" ] && [ "$2" ]; then - if [ -n "$wget_bin" ] ; then - # shellcheck disable=SC2086 - $wget_bin $wget_proxy_https $wget_proxy_http $wget_insecure $wget_output_level --connect-timeout="$downloader_connect_timeout" --random-wait --tries="$downloader_tries" --timeout="$downloader_max_time" --output-document="$1" "$2" - result=$? - else - # shellcheck disable=SC2086 - $curl_bin $curl_proxy $curl_insecure $curl_output_level --connect-timeout "$downloader_connect_timeout" --remote-time --location --retry "$downloader_tries" --max-time "$downloader_max_time" --output "$1" "$2" - result=$? - fi - return $result - fi -} - -# Auto update -function xshok_auto_update () { # version - xshok_pretty_echo_and_log "Performing automatic update..." - - # Download new version - echo -n "Downloading latest version..." - - xshok_file_download "$0.tmp" "$UPDATE_BASE/$SELF" - result=$? - - if [ "$result" -ne 0 ]; then - echo "Failed: Error while trying to get new version!" - echo "File requested: $UPDATE_BASE/$SELF" - exit 1 - fi - echo "Done." - - # Copy over modes from old version - OCTAL_MODE="$(stat -c "%a" "$SELF")" - if ! chmod "$OCTAL_MODE" "${0}.tmp" ; then - echo "Failed: Error while trying to set mode on ${0}.tmp." - exit 1 +function xshok_file_download() { #outputfile #url #notimestamp + if [ "$downloader_debug" == "yes" ] ; then + xshok_pretty_echo_and_log "url: ${2} >> outputfile: ${1} | ${3}" + fi + if [ "${1}" ] && [ "${2}" ] ; then + if [ -n "$curl_bin" ] ; then + if [ -f "${1}" ] ; then + # shellcheck disable=SC2086 + $curl_bin --fail --compressed $curl_proxy $curl_insecure $curl_output_level --connect-timeout "${downloader_connect_timeout}" --remote-time --location --retry "${downloader_tries}" --max-time "${downloader_max_time}" --time-cond "${1}" --output "${1}" "${2}" 2>&11 + result=$? + else + # shellcheck disable=SC2086 + $curl_bin --fail --compressed $curl_proxy $curl_insecure $curl_output_level --connect-timeout "${downloader_connect_timeout}" --remote-time --location --retry "${downloader_tries}" --max-time "${downloader_max_time}" --output "${1}" "${2}" 2>&11 + result=$? + fi + else + if [ ! "${3}" ] ; then + # the following is required because wget, cannot do --timestamping and --output-document together + this_dir="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + output_file="$1" + url="$2" + output_dir="${output_file%/*}" + output_file="${output_file##*/}" + url_file="${url##*/}" + wget_output_link="" + + cd "${output_dir}" || exit + if [ "$output_file" != "$url_file" ] ; then + if [ ! -f "$url_file" ] ; then + if [ ! -f "$output_file" ] ; then + touch "$output_file" + fi + ln -s "$output_file" "$url_file" + wget_output_link="$url_file" + fi + fi + # shellcheck disable=SC2086 + $wget_bin $wget_compression $wget_proxy $wget_insecure $wget_output_level --connect-timeout="${downloader_connect_timeout}" --random-wait --tries="${downloader_tries}" --timeout="${downloader_max_time}" --timestamping "${2}" 2>&12 + result=$? + if [ -z "$wget_output_link" ] ; then + if [ -L "$wget_output_link" ] ; then + rm -f "$wget_output_link" + fi + fi + cd "$this_dir" || exit + else + # shellcheck disable=SC2086 + $wget_bin $wget_compression $wget_proxy $wget_insecure $wget_output_level --connect-timeout="${downloader_connect_timeout}" --random-wait --tries="${downloader_tries}" --timeout="${downloader_max_time}" --output-document="${1}" "${2}" 2>&12 + result=$? + fi + fi + return $result fi - - # Generate the update script - cat > xshok_update_script.sh << EOF -#!/bin/bash -# Overwrite old file with new -if mv "$0.tmp" "$0"; then - echo "Done. Update complete." - rm \$0 -else - echo "Failed! The update was not completed." -fi -EOF - - - echo -n "Inserting update process..." - - # Replaced with $0, so code will update and then call itself with the same parameters it had - #exec /bin/bash xshok_update_script.sh - exec "$0" "$@" } # Handle list of database files -function clamav_files () { - echo "$clam_dbs/$db" >> "$current_tmp" +function clamav_files() { + echo "${clam_dbs}/${db}" >> "${current_tmp}" if [ "$keep_db_backup" == "yes" ] ; then - echo "$clam_dbs/$db-bak" >> "$current_tmp" + echo "${clam_dbs}/${db}-bak" >> "${current_tmp}" fi } # Manage the databases and allow multi-dimensions as well as global overrides # Since the datbases are basically a multi-dimentional associative arrays in bash -# ratings: LOW| MEDIUM| HIGH| REQUIRED| LOWONLY| MEDIUMONLY| LOWMEDIUMONLY | MEDIUMHIGHONLY | HIGHONLY| DISABLED -function xshok_database () { # rating database_array +# ratings: LOW | MEDIUM | HIGH | REQUIRED | LOWONLY | MEDIUMONLY | LOWMEDIUMONLY | DISABLED +function xshok_database() { # rating database_array # Assign - current_rating="$1" + current_rating="${1}" declare -a current_dbs=( "${@:2}" ) # Zero declare -a new_dbs=( ) @@ -366,12 +409,12 @@ function xshok_database () { # rating database_array continue # Skip to the next value fi fi - if [ -z "$current_rating" ] ; then # YARA rules are disabled + if [ -z "$current_rating" ] ; then new_dbs+=( "$db_name" ) else - if [[ ! "$db_name" = *"|"* ]] ; then # This old format - new_dbs+=( "$db_name" ) - else + if [[ ! "$db_name" = *"|"* ]] ; then # This old format + new_dbs+=( "$db_name" ) + else db_name_rating="${db_name#*|}" db_name="${db_name%|*}" @@ -381,72 +424,119 @@ function xshok_database () { # rating database_array elif [ "$db_name_rating" == "REQUIRED" ] ; then new_dbs+=( "$db_name" ) elif [ "$current_rating" == "LOW" ] ; then - if [ "$db_name_rating" == "LOWONLY" ] || [ "$db_name_rating" == "LOW" ] || [ "$db_name_rating" == "LOWMEDIUM" ] ; then + if [ "$db_name_rating" == "LOWONLY" ] || [ "$db_name_rating" == "LOW" ] || [ "$db_name_rating" == "LOWMEDIUMONLY" ] ; then new_dbs+=( "$db_name" ) fi elif [ "$current_rating" == "MEDIUM" ] ; then - if [ "$db_name_rating" == "MEDIUMONLY" ] || [ "$db_name_rating" == "MEDIUM" ] || [ "$db_name_rating" == "LOW" ] || [ "$db_name_rating" == "LOWMEDIUM" ] ; then + if [ "$db_name_rating" == "MEDIUMONLY" ] || [ "$db_name_rating" == "MEDIUM" ] || [ "$db_name_rating" == "LOW" ] || [ "$db_name_rating" == "LOWMEDIUMONLY" ] ; then new_dbs+=( "$db_name" ) fi elif [ "$current_rating" == "HIGH" ] ; then - if [ "$db_name_rating" == "HIGH" ] || [ "$db_name_rating" == "MEDIUM" ] || [ "$db_name_rating" == "LOW" ] ; then + if [ "$db_name_rating" == "HIGH" ] || [ "$db_name_rating" == "MEDIUM" ] || [ "$db_name_rating" == "LOW" ]; then new_dbs+=( "$db_name" ) fi - fi fi - fi fi + fi + fi done fi fi echo "${new_dbs[@]}" | xargs # Remove extra whitespace } +# Manage the databases to be removed and allow multi-dimensions as well as global overrides +# Since the datbases are basically a multi-dimentional associative arrays in bash +# ratings: LOW | MEDIUM | HIGH | REQUIRED | LOWONLY | MEDIUMONLY | LOWMEDIUMONLY | DISABLED +function xshok_remove_database() { # rating database_array + # Assign + current_rating="${1}" + declare -a current_dbs=( "${@:2}" ) + # Zero + declare -a new_dbs=( ) + + if [ ${#current_dbs} -ge 1 ] ; then + for db_name in "${current_dbs[@]}" ; do + db_name_rating="${db_name#*|}" + db_name="${db_name%|*}" + removed="no" + # Checks + if [ "$current_rating" == "DISABLED" ] ; then + new_dbs+=( "$db_name" ) + removed="yes" + elif [ "$current_rating" == "HIGH" ] ; then + if [ "$db_name_rating" == "LOWONLY" ] || [ "$db_name_rating" == "LOWMEDIUMONLY" ] ||[ "$db_name_rating" == "MEDIUMONLY" ] ; then + new_dbs+=( "$db_name" ) + removed="yes" + fi + elif [ "$current_rating" == "MEDIUM" ] ; then + if [ "$db_name_rating" == "HIGH" ] || [ "$db_name_rating" == "LOWONLY" ] ; then + new_dbs+=( "$db_name" ) + removed="yes" + fi + elif [ "$current_rating" == "LOW" ] ; then + if [ "$db_name_rating" == "MEDIUMONLY" ] || [ "$db_name_rating" == "MEDIUM" ] || [ "$db_name_rating" == "HIGH" ]; then + new_dbs+=( "$db_name" ) + removed="yes" + fi + fi + if [ "$removed" == "no" ] ; then # not already removed, process futher + if [ "$enable_yararules" == "no" ] && [[ "$db_name" == *".yar"* ]] ; then # YARA rules are disabled AND it's the value you want to delete + new_dbs+=( "$db_name" ) + fi + fi + done + fi + echo "${new_dbs[@]}" | xargs # Remove extra whitespace +} + + + ################################################################################ # ADDITIONAL PROGRAM FUNCTIONS ################################################################################ # Generates a man config and installs it -function install_man () { +function install_man() { if [ -n "$pkg_mgr" ] || [ -n "$pkg_rm" ] ; then - echo "This script (clamav-unofficial-sigs) was installed on the system via '$pkg_mgr'" + xshok_pretty_echo_and_log "This script (clamav-unofficial-sigs) was installed on the system via ${pkg_mgr}" exit 1 fi - echo "" - echo "Generating man file for install...." + xshok_pretty_echo_and_log "" + xshok_pretty_echo_and_log "Generating man file for install...." # Use defined varibles or attempt to use default varibles - if [ ! -e "$man_dir/$man_filename" ] ; then + if [ ! -e "${man_dir}/${man_filename}" ] ; then mkdir -p "$man_dir" - touch "$man_dir/$man_filename" 2>/dev/null + touch "${man_dir}/${man_filename}" 2>/dev/null fi - if [ ! -w "$man_dir/$man_filename" ] ; then - echo "ERROR: man install aborted, as file not writable: $man_dir/$man_filename" + if [ ! -w "${man_dir}/${man_filename}" ] ; then + xshok_pretty_echo_and_log "ERROR: man install aborted, as file not writable: ${man_dir}/${man_filename}" else - BOLD="\fB" + BOLD="\\fB" #REV="" - NORM="\fR" + NORM="\\fR" manresult="$(help_and_usage "man")" # Our template.. - cat << EOF > "$man_dir/$man_filename" + cat << EOF > "${man_dir}/${man_filename}" -.\" Manual page for eXtremeSHOK.com ClamAV Unofficial Signature Updater -.TH clamav-unofficial-sigs 8 "$script_version_date" "Version: $script_version" "SCRIPT COMMANDS" +.\\" Manual page for eXtremeSHOK.com ClamAV Unofficial Signature Updater +.TH clamav-unofficial-sigs 8 "${script_version_date}" "Version: ${script_version}" "SCRIPT COMMANDS" .SH NAME -clamav-unofficial-sigs \- Download, test, and install third-party ClamAV signature databases. +clamav-unofficial-sigs \\- Download, test, and install third-party ClamAV signature databases. .SH SYNOPSIS .B clamav-unofficial-sigs .RI [ options ] .SH DESCRIPTION -\fBclamav-unofficial-sigs\fP provides a simple way to download, test, and update third-party signature databases provided by Sanesecurity, FOXHOLE, OITC, Scamnailer, BOFHLAND, CRDF, Porcupine, Securiteinfo, MalwarePatrol, Yara-Rules Project, etc. It will also generate and install cron, logrotate, and man files. +\\fBclamav-unofficial-sigs\\fP provides a simple way to download, test, and update third-party signature databases provided by Sanesecurity, FOXHOLE, OITC, BOFHLAND, CRDF, Porcupine, Securiteinfo, MalwarePatrol, Yara-Rules Project, etc. It will also generate and install cron, logrotate, and man files. .SH UPDATES -Script updates can be found at: \fBhttps://github.com/extremeshok/clamav-unofficial-sigs\fP +Script updates can be found at: \\fBhttps://github.com/extremeshok/clamav-unofficial-sigs\\fP .SH OPTIONS This script follows the standard GNU command line syntax. .LP @@ -461,7 +551,7 @@ You are free to use, modify and distribute, however you may not remove this noti .SH LICENSE BSD (Berkeley Software Distribution) .SH BUGS -Report bugs to \fBhttps://github.com/extremeshok/clamav-unofficial-sigs\fP +Report bugs to \\fBhttps://github.com/extremeshok/clamav-unofficial-sigs\\fP .SH AUTHOR Adrian Jon Kriel :: admin@extremeshok.com Originially based on Script provide by Bill Landry @@ -470,43 +560,43 @@ Originially based on Script provide by Bill Landry EOF fi - echo "Completed: man installed, as file: $man_dir/$man_filename" + xshok_pretty_echo_and_log "Completed: man installed, as file: ${man_dir}/${man_filename}" } # Generate a logrotate config and install it -function install_logrotate () { +function install_logrotate() { if [ -n "$pkg_mgr" ] || [ -n "$pkg_rm" ] ; then - echo "This script (clamav-unofficial-sigs) was installed on the system via '$pkg_mgr'" + xshok_pretty_echo_and_log "This script (clamav-unofficial-sigs) was installed on the system via ${pkg_mgr}" exit 1 fi - echo "" - echo "Generating logrotate file for install...." + xshok_pretty_echo_and_log "" + xshok_pretty_echo_and_log "Generating logrotate file for install...." # Use defined varibles or attempt to use default varibles if [ -z "$logrotate_user" ] ; then - logrotate_user="$clam_user"; + logrotate_user="${clam_user}"; fi if [ -z "$logrotate_group" ] ; then - logrotate_group="$clam_group"; + logrotate_group="${clam_group}"; fi if [ -z "$logrotate_log_file_full_path" ] ; then - logrotate_log_file_full_path="$log_file_path/$log_file_name" + logrotate_log_file_full_path="${log_file_path}/${log_file_name}" fi - if [ ! -e "$logrotate_dir/$logrotate_filename" ] ; then + if [ ! -e "${logrotate_dir}/${logrotate_filename}" ] ; then mkdir -p "$logrotate_dir" - touch "$logrotate_dir/$logrotate_filename" 2>/dev/null + touch "${logrotate_dir}/${logrotate_filename}" 2>/dev/null fi - if [ ! -w "$logrotate_dir/$logrotate_filename" ] ; then - echo "ERROR: logrotate install aborted, as file not writable: $logrotate_dir/$logrotate_filename" + if [ ! -w "${logrotate_dir}/${logrotate_filename}" ] ; then + xshok_pretty_echo_and_log "ERROR: logrotate install aborted, as file not writable: ${logrotate_dir}/${logrotate_filename}" else # Our template.. - cat << EOF > "$logrotate_dir/$logrotate_filename" + cat << EOF > "${logrotate_dir}/${logrotate_filename}" # https://eXtremeSHOK.com ###################################################### # This file contains the logrotate settings for clamav-unofficial-sigs.sh ################### @@ -537,49 +627,51 @@ $logrotate_log_file_full_path { missingok notifempty compress - create 0640 $logrotate_user $logrotate_group + create 0640 ${logrotate_user} ${logrotate_group} } EOF fi - echo "Completed: logrotate installed, as file: $logrotate_dir/$logrotate_filename" + xshok_pretty_echo_and_log "Completed: logrotate installed, as file: ${logrotate_dir}/${logrotate_filename}" } # Generate a cron config and install it -function install_cron () { +function install_cron() { if [ -n "$pkg_mgr" ] || [ -n "$pkg_rm" ] ; then - echo "This script (clamav-unofficial-sigs) was installed on the system via '$pkg_mgr'" + xshok_pretty_echo_and_log "This script (clamav-unofficial-sigs) was installed on the system via {$pkg_mgr}" exit 1 fi - echo "" - echo "Generating cron file for install...." + xshok_pretty_echo_and_log "" + xshok_pretty_echo_and_log "Generating cron file for install...." # Use defined varibles or attempt to use default varibles if [ -z "$cron_minute" ] ; then cron_minute="$(( ( RANDOM % 59 ) + 1 ))" fi if [ -z "$cron_user" ] ; then - cron_user="$clam_user"; + cron_user="${clam_user}"; fi if [ -z "$cron_bash" ] ; then - cron_bash="$(which bash 2> /dev/null)" + cron_bash="$(command -v bash 2> /dev/null)" fi if [ -z "$cron_script_full_path" ] ; then cron_script_full_path="$this_script_full_path" fi - - if [ ! -e "$cron_dir/$cron_filename" ] ; then + if [ "$cron_sudo" == "yes" ] ; then + cron_sudo="sudo -u" + fi + if [ ! -e "${cron_dir}/${cron_filename}" ] ; then mkdir -p "$cron_dir" - touch "$cron_dir/$cron_filename" 2>/dev/null + touch "${cron_dir}/${cron_filename}" 2>/dev/null fi - if [ ! -w "$cron_dir/$cron_filename" ] ; then - echo "ERROR: cron install aborted, as file not writable: $cron_dir/$cron_filename" + if [ ! -w "${cron_dir}/${cron_filename}" ] ; then + xshok_pretty_echo_and_log "ERROR: cron install aborted, as file not writable: ${cron_dir}/${cron_filename}" else # Our template.. - cat << EOF > "$cron_dir/$cron_filename" + cat << EOF > "${cron_dir}/${cron_filename}" # https://eXtremeSHOK.com ###################################################### # This file contains the cron settings for clamav-unofficial-sigs.sh ################### @@ -607,81 +699,216 @@ function install_cron () { # script itself is set to randomize the actual execution time between # 60 - 600 seconds. To Adjust the cron values, edit your configs and run # bash clamav-unofficial-sigs.sh --install-cron to generate a new file. - -$cron_minute * * * * $cron_user [ -x $cron_script_full_path ] && $cron_bash $cron_script_full_path > /dev/null +# Uncomment to enable emails to the root user +#MAILTO=root +$cron_minute * * * * ${cron_sudo} ${cron_user} [ -x ${cron_script_full_path} ] && ${cron_bash} ${cron_script_full_path} # https://eXtremeSHOK.com ###################################################### EOF fi - echo "Completed: cron installed, as file: $cron_dir/$cron_filename" + xshok_pretty_echo_and_log "Completed: cron installed, as file: ${cron_dir}/${cron_filename}" +} + +# Auto upgrade the master.conf and the +function xshok_upgrade() { + + if [ "$allow_upgrades" == "no" ] ; then + xshok_pretty_echo_and_log "ERROR: --upgrade has been disabled, allow_upgrades=no" + exit 1 + fi + if ! xshok_is_root ; then + xshok_pretty_echo_and_log "ERROR: Only root can run the upgrade" + exit 1 + fi + + xshok_pretty_echo_and_log "Checking for updates ..." + + found_upgrade="no" + if [ -n "$curl_bin" ] ; then + # shellcheck disable=SC2086 + latest_version="$($curl_bin --compressed $curl_proxy $curl_insecure $curl_output_level --connect-timeout "${downloader_connect_timeout}" --remote-time --location --retry "${downloader_tries}" --max-time "${downloader_max_time}" "https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/${git_branch}/clamav-unofficial-sigs.sh" 2>&11 | $grep_bin "^script_version=" | head -n1 | cut -d '"' -f 2)" + # shellcheck disable=SC2086 + latest_config_version="$($curl_bin --compressed $curl_proxy $curl_insecure $curl_output_level --connect-timeout "${downloader_connect_timeout}" --remote-time --location --retry "${downloader_tries}" --max-time "${downloader_max_time}" "https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/${git_branch}/config/master.conf" 2>&11 | $grep_bin "^config_version=" | head -n1 | cut -d '"' -f 2)" + else + # shellcheck disable=SC2086 + latest_version="$($wget_bin $wget_compression $wget_proxy $wget_insecure $wget_output_level --connect-timeout="${downloader_connect_timeout}" --random-wait --tries="${downloader_tries}" --timeout="${downloader_max_time}" "https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/${git_branch}/clamav-unofficial-sigs.sh" -O - 2>&12 | $grep_bin "^script_version=" | head -n1 | cut -d '"' -f 2)" + # shellcheck disable=SC2086 + latest_config_version="$($wget_bin $wget_compression $wget_proxy $wget_insecure $wget_output_level --connect-timeout="${downloader_connect_timeout}" --random-wait --tries="${downloader_tries}" --timeout="${downloader_max_time}" "https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/${git_branch}/config/master.conf" -O - 2>&12 | $grep_bin "^config_version=" | head -n1 | cut -d '"' -f 2)" + fi + + # config_dir/master.conf + if [ "$latest_config_version" ] ; then + # shellcheck disable=SC2183,SC2086 + if [ "$(printf "%02d%02d%02d%02d" ${latest_config_version//./ })" -gt "$(printf "%02d%02d%02d%02d" ${config_version//./ })" ] ; then + found_upgrade="yes" + xshok_pretty_echo_and_log "ALERT: Upgrading config from v${config_version} to v${latest_config_version}" + if [ -w "${config_dir}/master.conf" ] && [ -f "${config_dir}/master.conf" ] ; then + echo "Downloading https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/${git_branch}/config/master.conf" + xshok_file_download "${work_dir}/master.conf.tmp" "https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/${git_branch}/config/master.conf" "notimestamp" + ret="$?" + if [ "$ret" -ne 0 ] ; then + xshok_pretty_echo_and_log "ERROR: Could not download https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/${git_branch}/config/master.conf" + exit 1 + fi + if ! $grep_bin -m 1 "config_version" "${work_dir}/master.conf.tmp" > /dev/null 2>&1 ; then + echo "ERROR: Downloaded master.conf is incomplete, please re-run" + exit 1 + fi + # Copy over permissions from old version + OCTAL_MODE="$(stat -c "%a" "${config_dir}/master.conf" 2> /dev/null)" + if [ -z "$OCTAL_MODE" ]; then + OCTAL_MODE="$(stat -f '%p' "${config_dir}/master.conf")" + fi + + xshok_pretty_echo_and_log "Running update process" + if ! mv -f "${work_dir}/master.conf.tmp" "${config_dir}/master.conf" ; then + xshok_pretty_echo_and_log "ERROR: failed moving ${work_dir}/master.conf.tmp to ${config_dir}/master.conf" + exit 1 + fi + if ! chmod "$OCTAL_MODE" "${config_dir}/master.conf" ; then + xshok_pretty_echo_and_log "ERROR: unable to set permissions on ${config_dir}/master.conf" + exit 1 + fi + xshok_pretty_echo_and_log "Completed" + else + xshok_pretty_echo_and_log "ERROR: ${config_dir}/master.conf is not a file or is not writable" + exit 1 + fi + fi + fi + + if [ "$latest_version" ] ; then + # shellcheck disable=SC2183,SC2086 + if [ "$(printf "%02d%02d%02d%02d" ${latest_version//./ })" -gt "$(printf "%02d%02d%02d%02d" ${script_version//./ })" ] ; then + found_upgrade="yes" + xshok_pretty_echo_and_log "ALERT: Upgrading script from v${script_version} to v${latest_version}" + if [ -w "${config_dir}/master.conf" ] && [ -f "${config_dir}/master.conf" ] ; then + echo "Downloading https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/${git_branch}/clamav-unofficial-sigs.sh" + xshok_file_download "${work_dir}/clamav-unofficial-sigs.sh.tmp" "https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/${git_branch}/clamav-unofficial-sigs.sh" "notimestamp" + ret=$? + if [ "$ret" -ne 0 ] ; then + xshok_pretty_echo_and_log "ERROR: Could not download https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/${git_branch}/clamav-unofficial-sigs.sh" + exit 1 + fi + # Detect to make sure the entire script is avilable, fail if the script is missing contents + if [ "$(tail -n 1 "${work_dir}/clamav-unofficial-sigs.sh.tmp" | head -n 1 | cut -c 1-7)" != "exit \$?" ] ; then + echo "ERROR: Downloaded clamav-unofficial-sigs.sh is incomplete, please re-run" + exit 1 + fi + # Copy over permissions from old version + OCTAL_MODE="$(stat -c "%a" "${this_script_full_path}" 2> /dev/null)" + if [ -z "$OCTAL_MODE" ]; then + OCTAL_MODE="$(stat -f '%p' "${this_script_full_path}")" + fi + xshok_pretty_echo_and_log "Inserting update process..." + # Generate the update script + cat > "${work_dir}/xshok_update_script.sh" << EOF +#!/usr/bin/env bash +echo "Running update process" +# Overwrite old file with new +if ! mv -f "${work_dir}/clamav-unofficial-sigs.sh.tmp" "${this_script_full_path}" ; then + echo "ERROR: failed moving ${work_dir}/clamav-unofficial-sigs.sh.tmp to ${this_script_full_path}" + rm -f \$0 + exit 1 +fi +if ! chmod "$OCTAL_MODE" "${this_script_full_path}" ; then + echo "ERROR: unable to set permissions on ${this_script_full_path}" + rm -f \$0 + exit 1 +fi + echo "Completed" + # echo "---------------------" + # echo "Optional, run as root: " + # echo "clamav-unofficial-sigs.sh --install-all" + echo "---------------------" + echo "Run once as root: " + echo "clamav-unofficial-sigs.sh --force" + + #remove the tmp script before exit + rm -f \$0 +EOF + # Replaced with $0, so code will update and then call itself with the same parameters it had + #exec "${0}" "$@" + bash_bin="$(command -v bash 2> /dev/null)" + exec "$bash_bin" "${work_dir}/xshok_update_script.sh" + echo "Running once as root" + else + xshok_pretty_echo_and_log "ERROR: ${config_dir}/master.conf is not a file or is not writable" + exit 1 + fi + fi +fi + +if [ "$found_upgrade" == "no" ] ; then + xshok_pretty_echo_and_log "No updates available" +fi } # Decode a third-party signature either by signature name -function decode_third_party_signature_by_signature_name () { - echo "" - echo "Input a third-party signature name to decode (e.g: Sanesecurity.Junk.15248) or" - echo "a hexadecimal encoded data string and press enter (do not include '.UNOFFICIAL'" - echo "in the signature name nor add quote marks to any input string):" +function decode_third_party_signature_by_signature_name() { + xshok_pretty_echo_and_log "" + xshok_pretty_echo_and_log "Input a third-party signature name to decode (e.g: Sanesecurity.Junk.15248) or" + xshok_pretty_echo_and_log "a hexadecimal encoded data string and press enter:" read -r input - input="$(echo "$input" | tr -d "'" | tr -d '"')" - if echo "$input" | $grep_bin "\." > /dev/null ; then + # Remove quotes and .UNOFFICIAL from the whitelist input string + input="$(echo "${input}" | tr -d "'" | tr -d '"' | tr -d '`')" + input=${input/\.UNOFFICIAL/} + if echo "${input}" | $grep_bin "\\." > /dev/null ; then cd "$clam_dbs" || exit - sig="$($grep_bin "$input:" ./*.ndb)" + sig="$($grep_bin "${input}:" ./*.ndb)" if [ -n "$sig" ] ; then db_file="${sig%:*}" - echo "$input found in: $db_file" - echo "$input signature decodes to:" - echo "$sig" | cut -d ":" -f 5 | perl -pe 's/([a-fA-F0-9]{2})|(\{[^}]*\}|\([^)]*\))/defined $2 ? $2 : chr(hex $1)/eg' + xshok_pretty_echo_and_log "${input} found in: ${db_file}" + xshok_pretty_echo_and_log "${input} signature decodes to:" + xshok_pretty_echo_and_log "$sig" | cut -d ":" -f 5 | perl -pe 's/([a-fA-F0-9]{2})|(\{[^}]*\}|\([^)]*\))/defined $2 ? $2 : chr(hex $1)/eg' else - echo "Signature '$input' could not be found." - echo "This script will only decode ClamAV 'UNOFFICIAL' third-Party," - echo "non-image based, signatures as found in the *.ndb databases." + xshok_pretty_echo_and_log "Signature ${input} could not be found." + xshok_pretty_echo_and_log "This script will only decode ClamAV 'UNOFFICIAL' third-Party," + xshok_pretty_echo_and_log "non-image based, signatures as found in the *.ndb databases." fi else - echo "Here is the decoded hexadecimal input string:" - echo "$input" | perl -pe 's/([a-fA-F0-9]{2})|(\{[^}]*\}|\([^)]*\))/defined $2 ? $2 : chr(hex $1)/eg' + xshok_pretty_echo_and_log "Here is the decoded hexadecimal input string:" + echo "${input}" | perl -pe 's/([a-fA-F0-9]{2})|(\{[^}]*\}|\([^)]*\))/defined $2 ? $2 : chr(hex $1)/eg' fi } # Hexadecimal encode an entire input string -function hexadecimal_encode_entire_input_string () { - echo "" - echo "Input the data string that you want to hexadecimal encode and then press enter. Do not include" - echo "any quotes around the string unless you want them included in the hexadecimal encoded output:" +function hexadecimal_encode_entire_input_string() { + xshok_pretty_echo_and_log "" + xshok_pretty_echo_and_log "Input the data string that you want to hexadecimal encode and then press enter. Do not include" + xshok_pretty_echo_and_log "any quotes around the string unless you want them included in the hexadecimal encoded output:" read -r input - echo "Here is the hexadecimal encoded input string:" - echo "$input" | perl -pe 's/(.)/sprintf("%02lx", ord $1)/eg' + xshok_pretty_echo_and_log "Here is the hexadecimal encoded input string:" + echo "${input}" | perl -pe 's/(.)/sprintf("%02lx", ord $1)/eg' } # Hexadecimal encode a formatted input string -function hexadecimal_encode_formatted_input_string () { - echo "" - echo "Input a formated data string containing spacing fields '{}, (), *' that you want to hexadecimal" - echo "encode, without encoding the spacing fields, and then press enter. Do not include any quotes" - echo "around the string unless you want them included in the hexadecimal encoded output:" +function hexadecimal_encode_formatted_input_string() { + xshok_pretty_echo_and_log "" + xshok_pretty_echo_and_log "Input a formated data string containing spacing fields '{}, (), *' that you want to hexadecimal" + xshok_pretty_echo_and_log "encode, without encoding the spacing fields, and then press enter. Do not include any quotes" + xshok_pretty_echo_and_log "around the string unless you want them included in the hexadecimal encoded output:" read -r input - echo "Here is the hexadecimal encoded input string:" - echo "$input" | perl -pe 's/(\{[^}]*\}|\([^)]*\)|\*)|(.)/defined $1 ? $1 : sprintf("%02lx", ord $2)/eg' + xshok_pretty_echo_and_log "Here is the hexadecimal encoded input string:" + echo "${input}" | perl -pe 's/(\{[^}]*\}|\([^)]*\)|\*)|(.)/defined $1 ? $1 : sprintf("%02lx", ord $2)/eg' } # GPG verify a specific Sanesecurity database file -function gpg_verify_specific_sanesecurity_database_file () { # databasefile - echo "" +function gpg_verify_specific_sanesecurity_database_file() { # databasefile + xshok_pretty_echo_and_log "" if [ "$enable_gpg" == "no" ] ; then - xshok_pretty_echo_and_log "Notice: GnuPG / signature verification disabled" "-" + xshok_pretty_echo_and_log "GnuPG / signature verification disabled" "-" else - if [ "$1" ] ; then - db_file="$(echo "$1" | awk -F "/" '{print $NF}')" - if [ -r "$work_dir_sanesecurity/$db_file" ] ; then - echo "GPG signature testing database file: $work_dir_sanesecurity/$db_file" - if [ -r "$work_dir_sanesecurity/$db_file".sig ] ; then - "$gpg_bin" -q --trust-model always --no-default-keyring --homedir "$work_dir_gpg" --keyring "$work_dir_gpg"/ss-keyring.gpg --verify "$work_dir_sanesecurity"/"$db_file".sig "$work_dir_sanesecurity"/"$db_file" - if [ $? -ne 0 ]; then - "$gpg_bin" -q --always-trust --no-default-keyring --homedir "$work_dir_gpg" --keyring "$work_dir_gpg"/ss-keyring.gpg --verify "$work_dir_sanesecurity"/"$db_file".sig "$work_dir_sanesecurity"/"$db_file" - if [ $? -eq 0 ]; then + if [ "${1}" ] ; then + db_file="$(echo "${1}" | awk -F "/" '{print $NF}')" + if [ -r "${work_dir_sanesecurity}/${db_file}" ] ; then + xshok_pretty_echo_and_log "GPG signature testing database file: ${work_dir_sanesecurity}/${db_file}" + if [ -r "${work_dir_sanesecurity}/${db_file}.sig" ] ; then + if ! "$gpg_bin" -q --trust-model always --no-default-keyring --homedir "${work_dir_gpg}" --keyring "${work_dir_gpg}/ss-keyring.gpg" --verify "${work_dir_sanesecurity}/${db_file}.sig" "${work_dir_sanesecurity}/${db_file}" ; then + if "$gpg_bin" -q --always-trust --no-default-keyring --homedir "${work_dir_gpg}" --keyring "${work_dir_gpg}/ss-keyring.gpg" --verify "${work_dir_sanesecurity}/${db_file}.sig" "${work_dir_sanesecurity}/${db_file}" ; then exit 0 else exit 1 @@ -690,15 +917,15 @@ function gpg_verify_specific_sanesecurity_database_file () { # databasefile exit 0 fi else - echo "Signature '${db_file}.sig' cannot be found." + xshok_pretty_echo_and_log "Signature ${db_file}.sig cannot be found." fi else - echo "File '$db_file' cannot be found or is not a Sanesecurity database file." - echo "Only the following Sanesecurity and OITC databases can be GPG signature tested:" - ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" "$work_dir_sanesecurity" + xshok_pretty_echo_and_log "File ${db_file} cannot be found or is not a Sanesecurity database file." + xshok_pretty_echo_and_log "Only the following Sanesecurity and OITC databases can be GPG signature tested:" + ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" --ignore "*.fp" "${work_dir_sanesecurity}" fi else - xshok_pretty_echo_and_log "ERROR: Missing value for option" "=" + xshok_pretty_echo_and_log "ERROR: Missing value for option" exit 1 fi exit 1 @@ -706,52 +933,53 @@ function gpg_verify_specific_sanesecurity_database_file () { # databasefile } # Output system and configuration information -function output_system_configuration_information () { - echo "" - echo "*** SCRIPT VERSION ***" - echo "$this_script_name $script_version ($script_version_date)" - echo "*** SYSTEM INFORMATION ***" +function output_system_configuration_information() { + xshok_pretty_echo_and_log "" + xshok_pretty_echo_and_log "*** SCRIPT INFORMATION ***" + xshok_pretty_echo_and_log "${this_script_name} ${script_version} (${script_version_date})" + xshok_pretty_echo_and_log "Master.conf Version: ${config_version}" + xshok_pretty_echo_and_log "Minimum required config: ${minimum_required_config_version}" + xshok_pretty_echo_and_log "*** SYSTEM INFORMATION ***" $uname_bin -a - echo "*** CLAMSCAN LOCATION & VERSION ***" - echo "$clamscan_bin" + xshok_pretty_echo_and_log "*** CLAMSCAN LOCATION & VERSION ***" + xshok_pretty_echo_and_log "${clamscan_bin}" $clamscan_bin --version | head -1 - echo "*** RSYNC LOCATION & VERSION ***" - echo "$rsync_bin" + xshok_pretty_echo_and_log "*** RSYNC LOCATION & VERSION ***" + xshok_pretty_echo_and_log "${rsync_bin}" $rsync_bin --version | head -1 - if [ -n "$wget_bin" ] ; then - echo "*** WGET LOCATION & VERSION ***" - echo "$wget_bin" - $wget_bin --version | head -1 + if [ -n "$curl_bin" ] ; then + xshok_pretty_echo_and_log "*** CURL LOCATION & VERSION ***" + xshok_pretty_echo_and_log "${curl_bin}" + $curl_bin --version | head -1 else - echo "*** CURL LOCATION & VERSION ***" - echo "$curl_bin" - $curl_bin --version | head -1 + xshok_pretty_echo_and_log "*** WGET LOCATION & VERSION ***" + xshok_pretty_echo_and_log "${wget_bin}" + $wget_bin --version | head -1 fi if [ "$enable_gpg" == "yes" ] ; then - echo "*** GPG LOCATION & VERSION ***" - echo "$gpg_bin" + xshok_pretty_echo_and_log "*** GPG LOCATION & VERSION ***" + xshok_pretty_echo_and_log "${gpg_bin}" $gpg_bin --version | head -1 fi - echo "*** SCRIPT WORKING DIRECTORY INFORMATION ***" - echo "$work_dir" - echo "*** CLAMAV DIRECTORY INFORMATION ***" - echo "$clam_dbs" - echo "*** SCRIPT CONFIGURATION SETTINGS ***" + xshok_pretty_echo_and_log "*** DIRECTORY INFORMATION ***" + xshok_pretty_echo_and_log "Working Directory: ${work_dir}" + xshok_pretty_echo_and_log "Clam Database Directory: ${clam_dbs}" if [ "$custom_config" != "no" ] ; then if [ -d "$custom_config" ] ; then # Assign the custom config dir and remove trailing / (removes / and //) - echo "Custom Configuration Directory: $config_dir" + xshok_pretty_echo_and_log "Custom Configuration Directory: ${custom_config}" else - echo "Custom Configuration File: $custom_config" + xshok_pretty_echo_and_log "Custom Configuration File: ${custom_config}" fi else - echo "Configuration Directory: $config_dir" + xshok_pretty_echo_and_log "Configuration Directory: ${config_dir}" fi + xshok_pretty_echo_and_log "" } # Make a signature database from an ascii file -function make_signature_database_from_ascii_file () { - echo "" +function make_signature_database_from_ascii_file() { + xshok_pretty_echo_and_log "" echo " The '-m' script flag provides a way to create a ClamAV hexadecimal signature database (*.ndb) file from a list of data strings stored in a clear-text ascii file, with one data string entry per line. @@ -790,7 +1018,7 @@ function make_signature_database_from_ascii_file () { target type is used and full file scanning is enabled (see ClamAV signatures.pdf for details). - Line numbering will be done automatically by the script. - " | command sed 's/^ //g' + " | command "$sed_bin" 's/^ //g' echo -n "Do you wish to continue? " if xshok_prompt_confirm ; then @@ -799,12 +1027,12 @@ function make_signature_database_from_ascii_file () { if [ -r "$source" ] ; then source_file="$(basename "$source")" - echo "What signature prefix would you like to use? For example: 'Phish.Domains'" - echo "will create signatures that looks like: 'Phish.Domains.1:4:*:HexSigHere'" + xshok_pretty_echo_and_log "What signature prefix would you like to use? For example: 'Phish.Domains'" + xshok_pretty_echo_and_log "will create signatures that looks like: 'Phish.Domains.1:4:*:HexSigHere'" echo -n "Enter signature prefix: " read -r prefix - path_file="$(echo "$source" | cut -d "." -f -1 | command sed 's/$/.ndb/')" + path_file="$(echo "$source" | cut -d "." -f -1 | command "$sed_bin" 's/$/.ndb/')" db_file="$(basename "$path_file")" rm -f "$path_file" total="$(wc -l "$source" | cut -d " " -f 1)" @@ -813,240 +1041,275 @@ function make_signature_database_from_ascii_file () { while read -r line ; do line_prefix="$(echo "$line" | awk -F ":" '{print $1}')" if [ "$line_prefix" == "-" ] ; then - echo "$line" | cut -d ":" -f 2- | perl -pe 's/(.)/sprintf("%02lx", ord $1)/eg' | command sed "s/^/$prefix\.$line_num:4:\*:/" >> "$path_file" + echo "$line" | cut -d ":" -f 2- | perl -pe 's/(.)/sprintf("%02lx", ord $1)/eg' | command "$sed_bin" "s/^/$prefix\\.$line_num:4:\\*:/" >> "$path_file" elif [ "$line_prefix" == "=" ] ; then - echo "$line" | cut -d ":" -f 2- | perl -pe 's/(\{[^}]*\}|\([^)]*\)|\*)|(.)/defined $1 ? $1 : sprintf("%02lx", ord $2)/eg' | command sed "s/^/$prefix\.$line_num:4:\*:/" >> "$path_file" + echo "$line" | cut -d ":" -f 2- | perl -pe 's/(\{[^}]*\}|\([^)]*\)|\*)|(.)/defined $1 ? $1 : sprintf("%02lx", ord $2)/eg' | command "$sed_bin" "s/^/$prefix\\.$line_num:4:\\*:/" >> "$path_file" else - echo "$line" | perl -pe 's/(.)/sprintf("%02lx", ord $1)/eg' | command sed "s/^/$prefix\.$line_num:4:\*:/" >> "$path_file" + echo "$line" | perl -pe 's/(.)/sprintf("%02lx", ord $1)/eg' | command "$sed_bin" "s/^/$prefix\\.$line_num:4:\\*:/" >> "$path_file" fi - echo "Hexadecimal encoding $source_file line: $line_num of $total" + xshok_pretty_echo_and_log "Hexadecimal encoding ${source_file} line: ${line_num} of ${total}" line_num="$((line_num + 1))" done < "$source" else - echo "Source file not found, exiting..." + xshok_pretty_echo_and_log "Source file not found, exiting..." exit fi - echo "Signature database file created at: $path_file" - if $clamscan_bin --quiet -d "$path_file" "$work_dir_work_configs/scan-test.txt" 2>/dev/null ; then + xshok_pretty_echo_and_log "Signature database file created at: ${path_file}" + if $clamscan_bin --quiet -d "$path_file" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then - echo "Clamscan reports database integrity tested good." + xshok_pretty_echo_and_log "Clamscan reports database integrity tested good." - echo -n "Would you like to move '$db_file' into '$clam_dbs' and reload databases?" + echo -n "Would you like to move '${db_file}' into '${clam_dbs}' and reload databases?" if xshok_prompt_confirm ; then - if ! cmp -s "$path_file" "$clam_dbs/$db_file" ; then + if ! cmp -s "$path_file" "${clam_dbs}/${db_file}" ; then if $rsync_bin -pcqt "$path_file" "$clam_dbs" ; then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/$db_file" + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" perms chmod -f 0644 "$clam_dbs"/"$db_file" if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/$db_file" + restorecon "${clam_dbs}/${db_file}" fi $clamd_restart_opt - echo "Signature database '$db_file' was successfully implemented and ClamD databases reloaded." + xshok_pretty_echo_and_log "Signature database '${db_file}' was successfully implemented and ClamD databases reloading." else - echo "Failed to add/update '$db_file', ClamD database not reloaded." + xshok_pretty_echo_and_log "Failed to add/update '${db_file}', ClamD database not reloading." fi else - echo "Database '$db_file' has not changed - skipping" + xshok_pretty_echo_and_log "Database '${db_file}' has not changed - skipping" fi else - echo "No action taken." + xshok_pretty_echo_and_log "No action taken." fi else - echo "Clamscan reports that '$db_file' signature database integrity tested bad." + xshok_pretty_echo_and_log "Clamscan reports that '${db_file}' signature database integrity tested bad." fi fi } # Remove the clamav-unofficial-sigs script -function remove_script () { - echo "" +function remove_script() { + xshok_pretty_echo_and_log "" if [ -n "$pkg_mgr" ] || [ -n "$pkg_rm" ] ; then - echo "This script (clamav-unofficial-sigs) was installed on the system via '$pkg_mgr'" - echo "use '$pkg_rm' to remove the script and all of its associated files and databases from the system." + xshok_pretty_echo_and_log "This script (clamav-unofficial-sigs) was installed on the system via '${pkg_mgr}'" + xshok_pretty_echo_and_log "use '${pkg_rm}' to remove the script and all of its associated files and databases from the system." else - cron_file_full_path="$cron_dir/$cron_filename" - logrotate_file_full_path="$logrotate_dir/$logrotate_filename" - man_file_full_path="$man_dir/$man_filename" + cron_file_full_path="${cron_dir}/${cron_filename}" + logrotate_file_full_path="${logrotate_dir}/${logrotate_filename}" + man_file_full_path="${man_dir}/${man_filename}" - echo "This will remove the workdir ($work_dir), logrotate file ($logrotate_file_full_path), cron file ($cron_file_full_path), man file ($man_file_full_path)" - echo "Are you sure you want to remove the clamav-unofficial-sigs script and all of its associated files, third-party databases, and work directory from the system?" + xshok_pretty_echo_and_log "This will remove the workdir (${work_dir}), logrotate file (${logrotate_file_full_path}), cron file (${cron_file_full_path}), man file (${man_file_full_path})" + xshok_pretty_echo_and_log "Are you sure you want to remove the clamav-unofficial-sigs script and all of its associated files, third-party databases, and work directory from the system?" if xshok_prompt_confirm ; then - echo "This can not be undone are you sure ?" + xshok_pretty_echo_and_log "This can not be undone are you sure ?" if xshok_prompt_confirm ; then - if [ -r "$work_dir_work_configs/purge.txt" ] ; then + if [ -r "${work_dir_work_configs}/purge.txt" ] ; then while read -r file ; do xshok_is_file "$file" && rm -f -- "$file" - echo " Removed file: $file" - done < "$work_dir_work_configs"/purge.txt + xshok_pretty_echo_and_log " Removed file: ${file}" + done < "${work_dir_work_configs}/purge.txt" if [ -r "$cron_file_full_path" ] ; then xshok_is_file "$cron_file_full_path" && rm -f "$cron_file_full_path" - echo " Removed file: $cron_file_full_path" + xshok_pretty_echo_and_log " Removed file: ${cron_file_full_path}" fi if [ -r "$logrotate_file_full_path" ] ; then xshok_is_file "$logrotate_file_full_path" && rm -f "$logrotate_file_full_path" - echo " Removed file: $logrotate_file_full_path" + xshok_pretty_echo_and_log " Removed file: ${logrotate_file_full_path}" fi if [ -r "$man_file_full_path" ] ; then xshok_is_file "$man_file_full_path" && rm -f "$man_file_full_path" - echo " Removed file: $man_file_full_path" + xshok_pretty_echo_and_log " Removed file: ${man_file_full_path}" fi # Rather keep the configs #rm -f -- "$default_config" && echo " Removed file: $default_config" - #rm -f -- "$0" && echo " Removed file: $0" - xshok_is_subdir "$work_dir" && rm -rf -- "${work_dir:?}" && echo " Removed script working directories: $work_dir" + #rm -f -- "${0}" && echo " Removed file: $0" + xshok_is_subdir "$work_dir" && rm -rf -- "${work_dir:?}" && echo " Removed script working directories: ${work_dir}" - echo " The clamav-unofficial-sigs script and all of its associated files, third-party" - echo " databases, and work directories have been successfully removed from the system." + xshok_pretty_echo_and_log " The clamav-unofficial-sigs script and all of its associated files, third-party" + xshok_pretty_echo_and_log " databases, and work directories have been successfully removed from the system." else - echo " Cannot locate 'purge.txt' file in $work_dir_work_configs." - echo " Files and signature database will need to be removed manually." + xshok_pretty_echo_and_log " Cannot locate 'purge.txt' file in ${work_dir_work_configs}." + xshok_pretty_echo_and_log " Files and signature database will need to be removed manually." fi else - echo "Aborted" + xshok_pretty_echo_and_log "Aborted" fi else - echo "Aborted" + xshok_pretty_echo_and_log "Aborted" fi fi } # Clamscan integrity test a specific database file -function clamscan_integrity_test_specific_database_file () { # databasefile - echo "" - if [ "$1" ] ; then - input="$(echo "$1" | awk -F "/" '{print $NF}')" +function clamscan_integrity_test_specific_database_file() { # databasefile + xshok_pretty_echo_and_log "" + if [ "${1}" ] ; then + input="$(echo "${1}" | awk -F "/" '{print $NF}')" db_file="$(find "$work_dir" -name "$input")" if [ -r "$db_file" ] ; then - echo "Clamscan integrity testing: $db_file" - - $clamscan_bin --quiet -d "$db_file" "$work_dir_work_configs/scan-test.txt" - if [ $? -eq 0 ]; then - echo "Clamscan reports that '$input' database integrity tested GOOD" + xshok_pretty_echo_and_log "Clamscan integrity testing: ${db_file}" + if $clamscan_bin --quiet -d "$db_file" "${work_dir_work_configs}/scan-test.txt" ; then + xshok_pretty_echo_and_log "Clamscan reports that '${input}' database integrity tested GOOD" exit 0 else - echo "Clamscan reports that '$input' database integrity tested BAD" + xshok_pretty_echo_and_log "Clamscan reports that '${input}' database integrity tested BAD" exit 1 fi else - echo "File '$input' cannot be found." - echo "Here is a list of third-party databases that can be clamscan integrity tested:" + xshok_pretty_echo_and_log "File '${input}' cannot be found." + xshok_pretty_echo_and_log "Here is a list of third-party databases that can be clamscan integrity tested:" + + xshok_pretty_echo_and_log "=== Sanesecurity ===" + ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" --ignore "*.fp" "$work_dir_sanesecurity" - echo "=== Sanesecurity ===" - ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" "$work_dir_sanesecurity" + xshok_pretty_echo_and_log "=== SecuriteInfo ===" + ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" --ignore "*.fp" "$work_dir_securiteinfo" - echo "=== SecuriteInfo ===" - ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" "$work_dir_securiteinfo" + xshok_pretty_echo_and_log "=== MalwarePatrol ===" + ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" --ignore "*.fp" "$work_dir_malwarepatrol" - echo "=== MalwarePatrol ===" - ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" "$work_dir_malwarepatrol" + xshok_pretty_echo_and_log "=== Linux Malware Detect ===" + ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" --ignore "*.fp" "$work_dir_linuxmalwaredetect" - echo "=== Linux Malware Detect ===" - ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" "$work_dir_linuxmalwaredetect" + xshok_pretty_echo_and_log "=== interServer Detect ===" + ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" --ignore "*.fp" "$work_dir_interserver" - echo "=== Linux Malware Detect ===" - ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" "$work_dir_yararulesproject" + xshok_pretty_echo_and_log "=== Malware Expert Detect ===" + ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" --ignore "*.fp" "$work_dir_malwareexpert" - echo "=== User Defined Databases ===" - ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" "$work_dir_add" + xshok_pretty_echo_and_log "=== Linux Malware Detect ===" + ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" --ignore "*.fp" "$work_dir_yararulesproject" - echo "Check the file name and try again..." + xshok_pretty_echo_and_log "=== User Defined Databases ===" + ls --ignore "*.sig" --ignore "*.md5" --ignore "*.ign2" --ignore "*.fp" "$work_dir_add" + + xshok_pretty_echo_and_log "Check the file name and try again..." fi else - xshok_pretty_echo_and_log "ERROR: Missing value for option" "=" + xshok_pretty_echo_and_log "ERROR: Missing value for option" exit 1 fi } # Output names of any third-party signatures that triggered during the HAM directory scan -function output_signatures_triggered_during_ham_directory_scan () { - echo "" +function output_signatures_triggered_during_ham_directory_scan() { + xshok_pretty_echo_and_log "" if [ -n "$ham_dir" ] ; then - if [ -r "$work_dir_work_configs/whitelist.hex" ] ; then - echo "The following third-party signatures triggered hits during the HAM Directory scan:" + if [ -r "${work_dir_work_configs}/whitelist.hex" ] ; then + xshok_pretty_echo_and_log "The following third-party signatures triggered hits during the HAM Directory scan:" - $grep_bin -h -f "$work_dir_work_configs/whitelist.hex" "$work_dir"/*/*.ndb | cut -d ":" -f 1 + $grep_bin -h -f "${work_dir_work_configs}/whitelist.hex" "$work_dir"/*/*.ndb | cut -d ":" -f 1 + $grep_bin -h -f "${work_dir_work_configs}/whitelist.hex" "$work_dir"/*/*.db | cut -d "=" -f 1 else - echo "No third-party signatures have triggered hits during the HAM Directory scan." + xshok_pretty_echo_and_log "No third-party signatures have triggered hits during the HAM Directory scan." fi else - echo "Ham directory scanning is not currently enabled in the script's configuration file." + xshok_pretty_echo_and_log "Ham directory scanning is not currently enabled in the script's configuration file." fi } # Adds a signature whitelist entry in the newer ClamAV IGN2 format -function add_signature_whitelist_entry () { - echo "" - echo "Input a third-party signature name that you wish to whitelist due to false-positives" - echo "and press enter (do not include '.UNOFFICIAL' in the signature name nor add quote" - echo "marks to the input string):" - - read -r input +function add_signature_whitelist_entry() { #signature + xshok_pretty_echo_and_log "Signature Whitelist" "=" + if [ -n "$1" ] ; then + input="$1" + else + xshok_pretty_echo_and_log "Input a third-party signature name that you wish to whitelist and press enter" + read -r input + fi if [ -n "$input" ] ; then + xshok_pretty_echo_and_log "Processing: ${input}" cd "$clam_dbs" || exit - input="$(echo "$input" | tr -d "'" | tr -d '"')" - sig_full="$($grep_bin -H "$input" ./*.*db)" - sig_name="$(echo "$sig_full" | cut -d ":" -f 2)" + # Remove quotes and .UNOFFICIAL from the string + input="$(echo "${input}" | tr -d "'" | tr -d '"' | tr -d '`"')" + input=${input/\.UNOFFICIAL/} + + yaratest="$(echo "$input" | cut -d "." -f 1)" + shopt -s nocasematch + if [ "$yaratest" == "YARA" ] ; then + echo "YARA signature detected" + sig_full="$input" + sig_extension="" + sig_name="$input" + else + sig_full="$($grep_bin -H -m 1 "$input" ./*.*db)" + sig_extension=${sig_full%%\:*} + sig_extension=${sig_extension##*\.} + shopt -s nocasematch + if [ "$sig_extension" == "hdb" ] || [ "$sig_extension" == "hsb" ] || [ "$sig_extension" == "hdu " ] || [ "$sig_extension" == "hsu" ] || [ "$sig_extension" == "mdb" ] || [ "$sig_extension" == "msb" ] || [ "$sig_extension" == "mdu" ] || [ "$sig_extension" == "msu" ] ; then + # Hash-based Signature Database + position="4" + else + position="2" + fi + sig_name="$(echo "$sig_full" | cut -d ":" -f $position | cut -d "=" -f 1)" + fi + if [ -n "$sig_name" ] ; then - if ! $grep_bin "$sig_name" my-whitelist.ign2 > /dev/null 2>&1 ; then - cp -f my-whitelist.ign2 "$work_dir_work_configs" 2>/dev/null - echo "$sig_name" >> "$work_dir_work_configs/my-whitelist.ign2" - echo "$sig_full" >> "$work_dir_work_configs/tracker.txt" - if $clamscan_bin --quiet -d "$work_dir_work_configs/my-whitelist.ign2" "$work_dir_work_configs/scan-test.txt" ; then - if $rsync_bin -pcqt "$work_dir_work_configs/my-whitelist.ign2" "$clam_dbs" ; then - perms chown -f "$clam_user:$clam_group" my-whitelist.ign2 - - if [ ! -s "$work_dir_work_configs/monitor-ign.txt" ] ; then + if ! $grep_bin -m 1 "$sig_name" my-whitelist.ign2 > /dev/null 2>&1 ; then + cp -f -p my-whitelist.ign2 "$work_dir_work_configs" 2>/dev/null + echo "$sig_name" >> "${work_dir_work_configs}/my-whitelist.ign2" + shopt -s nocasematch + if [ "$yaratest" != "YARA" ] ; then + echo "$sig_full" >> "${work_dir_work_configs}/tracker.txt" + fi + + if $clamscan_bin --quiet -d "${work_dir_work_configs}/my-whitelist.ign2" "${work_dir_work_configs}/scan-test.txt" ; then + if $rsync_bin -pcqt "${work_dir_work_configs}/my-whitelist.ign2" "$clam_dbs" ; then + perms chown -f "${clam_user}:${clam_group}" my-whitelist.ign2 + + if [ ! -s "${work_dir_work_configs}/monitor-ign.txt" ] ; then # Create "monitor-ign.txt" file for clamscan database integrity testing. - echo "This is the monitor ignore file..." > "$work_dir_work_configs/monitor-ign.txt" + echo "This is the monitor ignore file..." > "${work_dir_work_configs}/monitor-ign.txt" fi - perms chmod -f 0644 my-whitelist.ign2 "$work_dir_work_configs/monitor-ign.txt" + perms chmod -f 0644 my-whitelist.ign2 "${work_dir_work_configs}/monitor-ign.txt" if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/local.ign" + restorecon "${clam_dbs}/local.ign" fi + do_clamd_reload="4" clamscan_reload_dbs - echo "Signature '$input' has been added to my-whitelist.ign2 and" - echo "all databases have been reloaded. The script will track any changes" - echo "to the offending signature and will automatically remove it if the" - echo "signature is modified or removed from the third-party database." + xshok_pretty_echo_and_log "Signature '${input}' has been added to my-whitelist.ign2 and all databases have been reloaded." + if [ "$yaratest" != "YARA" ] ; then + xshok_pretty_echo_and_log "The script will track any changes to the offending signature and will automatically remove it, " + xshok_pretty_echo_and_log "if the signature is modified or removed from the third-party database." + fi else - echo "Failed to successfully update my-whitelist.ign2 file - SKIPPING." + xshok_pretty_echo_and_log "Failed to successfully update my-whitelist.ign2 file - SKIPPING." fi else - echo "Clamscan reports my-whitelist.ign2 database integrity is bad - SKIPPING." + xshok_pretty_echo_and_log "Clamscan reports my-whitelist.ign2 database integrity is bad - SKIPPING." fi else - echo "Signature '$input' already exists in my-whitelist.ign2 - no action taken." + xshok_pretty_echo_and_log "Signature '${input}' already exists in my-whitelist.ign2 - no action taken." fi else - echo "Signature '$input' could not be found." + xshok_pretty_echo_and_log "Signature '${input}' could not be found." - echo "This script will only create a whitelise entry in my-whitelist.ign2 for ClamAV" - echo "'UNOFFICIAL' third-Party signatures as found in the *.ndb *.hdb *.db databases." + xshok_pretty_echo_and_log "This script will only create a whitelise entry in my-whitelist.ign2 for ClamAV" + xshok_pretty_echo_and_log "'UNOFFICIAL' third-Party signatures as found in the *.ndb *.hdb *.db databases." fi else - echo "No input detected - no action taken." + xshok_pretty_echo_and_log "No input detected - no action taken." fi } # Clamscan reload database -function clamscan_reload_dbs () { +function clamscan_reload_dbs() { # Reload all clamd databases if updates detected and $reload_dbs" is set to "yes" if [ "$reload_dbs" == "yes" ] ; then if [ "$do_clamd_reload" != "0" ] ; then @@ -1063,19 +1326,18 @@ function clamscan_reload_dbs () { fi if [[ "$($clamd_reload_opt 2>&1)" = *"ERROR"* ]] ; then - xshok_pretty_echo_and_log "ERROR: Failed to reload, trying again" "-" + xshok_pretty_echo_and_log "ERROR: Failed to reload, trying again" if [ -r "$clamd_pid" ] ; then mypid="$(cat "$clamd_pid")" - kill -USR2 "$mypid" - if [ $? -eq 0 ] ; then - xshok_pretty_echo_and_log "ClamAV databases Reloaded" "=" + + if kill -USR2 "$mypid" ; then + xshok_pretty_echo_and_log "ClamAV databases reloading" "=" else - xshok_pretty_echo_and_log "ERROR: Failed to reload, forcing clamd to restart" "-" + xshok_pretty_echo_and_log "ERROR: Failed to reload, forcing clamd to restart" if [ -z "$clamd_restart_opt" ] ; then - xshok_pretty_echo_and_log "WARNING: Check the script's configuration file, 'reload_dbs' enabled but no 'clamd_restart_opt'" "*" + xshok_pretty_echo_and_log "WARNING: Check the script's configuration file, 'reload_dbs' enabled but no 'clamd_restart_opt'" else - $clamd_restart_opt > /dev/null - if [ $? -eq 0 ] ; then + if $clamd_restart_opt > /dev/null ; then xshok_pretty_echo_and_log "ClamAV Restarted" "=" else xshok_pretty_echo_and_log "ClamAV NOT Restarted" "-" @@ -1083,12 +1345,11 @@ function clamscan_reload_dbs () { fi fi else - xshok_pretty_echo_and_log "ERROR: Failed to reload, forcing clamd to restart" "-" + xshok_pretty_echo_and_log "ERROR: Failed to reload, forcing clamd to restart" if [ -z "$clamd_restart_opt" ] ; then - xshok_pretty_echo_and_log "WARNING: Check the script's configuration file, 'reload_dbs' enabled but no 'clamd_restart_opt'" "*" + xshok_pretty_echo_and_log "WARNING: Check the script's configuration file, 'reload_dbs' enabled but no 'clamd_restart_opt'" else - $clamd_restart_opt > /dev/null - if [ $? -eq 0 ] ; then + if $clamd_restart_opt > /dev/null ; then xshok_pretty_echo_and_log "ClamAV Restarted" "=" else xshok_pretty_echo_and_log "ClamAV NOT Restarted" "-" @@ -1096,7 +1357,7 @@ function clamscan_reload_dbs () { fi fi else - xshok_pretty_echo_and_log "ClamAV databases Reloaded" "=" + xshok_pretty_echo_and_log "ClamAV databases reloading" "=" fi else xshok_pretty_echo_and_log "No updates detected, ClamAV databases were not reloaded" "=" @@ -1110,7 +1371,7 @@ function clamscan_reload_dbs () { # If ClamD status check is enabled ("clamd_socket" variable is uncommented # and the socket path is correctly specified in "User Edit" section above), # then test to see if clamd is running or not. -function check_clamav () { +function check_clamav() { if [ -n "$clamd_socket" ] ; then if [ -S "$clamd_socket" ] ; then if [ "$(perl -e 'use IO::Socket::UNIX; print $IO::Socket::UNIX::VERSION,"\n"' 2>/dev/null)" ] ; then @@ -1120,7 +1381,7 @@ function check_clamav () { xshok_pretty_echo_and_log "ClamD is running" "=" fi else - socat="$(which socat 2>/dev/null)" + socat="$(command -v socat 2>/dev/null)" if [ -n "$socat" ] && [ -x "$socat" ] ; then socket_cat1="1" if [ "$( (echo "PING"; sleep 1;) | socat - "$clamd_socket" 2>/dev/null)" == "PONG" ] ; then @@ -1130,11 +1391,11 @@ function check_clamav () { fi fi if [ -z "$io_socket1" ] && [ -z "$socket_cat1" ] ; then - xshok_pretty_echo_and_log "WARNING: socat or perl module 'IO::Socket::UNIX' not found, cannot test if ClamD is running" "*" + xshok_pretty_echo_and_log "WARNING: socat or perl module 'IO::Socket::UNIX' not found, cannot test if ClamD is running" else if [ -z "$io_socket2" ] && [ -z "$socket_cat2" ] ; then - xshok_pretty_echo_and_log "ALERT: CLAMD IS NOT RUNNING!" "=" + xshok_pretty_echo_and_log "ALERT: CLAMD IS NOT RUNNING!" if [ -n "$clamd_restart_opt" ] ; then xshok_pretty_echo_and_log "Attempting to start ClamD..." "-" if [ -n "$io_socket1" ] ; then @@ -1142,7 +1403,7 @@ function check_clamav () { if [ "$(perl -MIO::Socket::UNIX -we '$s = IO::Socket::UNIX->new(shift); $s->print("PING"); print $s->getline; $s->close' "$clamd_socket" 2>/dev/null)" = "PONG" ] ; then xshok_pretty_echo_and_log "ClamD was successfully started" "=" else - xshok_pretty_echo_and_log "ERROR: CLAMD FAILED TO START" "=" + xshok_pretty_echo_and_log "ERROR: CLAMD FAILED TO START" exit 1 fi else @@ -1151,7 +1412,7 @@ function check_clamav () { if [ "$( (echo "PING"; sleep 1;) | socat - "$clamd_socket" 2>/dev/null)" == "PONG" ] ; then xshok_pretty_echo_and_log "ClamD was successfully started" "=" else - xshok_pretty_echo_and_log "ERROR: CLAMD FAILED TO START" "=" + xshok_pretty_echo_and_log "ERROR: CLAMD FAILED TO START" exit 1 fi fi @@ -1160,56 +1421,60 @@ function check_clamav () { fi fi else - xshok_pretty_echo_and_log "WARNING: $clamd_socket is not a usable socket" "*" + xshok_pretty_echo_and_log "WARNING: ${clamd_socket} is not a usable socket" fi else - xshok_pretty_echo_and_log "WARNING: clamd_socket is not defined in the configuration file" "*" + xshok_pretty_echo_and_log "WARNING: clamd_socket is not defined in the configuration file" fi } # Check for a new version -function check_new_version () { - if [ -n "$wget_bin" ] ; then - # shellcheck disable=SC2086 - latest_version="$($wget_bin $wget_proxy_https $wget_proxy_http $wget_insecure $wget_output_level --connect-timeout="$downloader_connect_timeout" --random-wait --tries="$downloader_tries" --timeout="$downloader_max_time" https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/master/clamav-unofficial-sigs.sh -O - 2> /dev/null | $grep_bin "script""_version=" | cut -d '"' -f 2)" - else - # shellcheck disable=SC2086 - latest_version="$($curl_bin $curl_proxy $curl_insecure $curl_output_level --connect-timeout "$downloader_connect_timeout" --remote-time --location --retry "$downloader_tries" --max-time "$downloader_max_time" https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/master/clamav-unofficial-sigs.sh 2> /dev/null | $grep_bin "script""_version=" | cut -d '"' -f 2)" - fi +function check_new_version() { + found_upgrade="no" + if [ -n "$curl_bin" ] ; then + # shellcheck disable=SC2086 + latest_version="$($curl_bin --compressed $curl_proxy $curl_insecure $curl_output_level --connect-timeout "${downloader_connect_timeout}" --remote-time --location --retry "${downloader_tries}" --max-time "${downloader_max_time}" "https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/${git_branch}/clamav-unofficial-sigs.sh" 2>&11 | $grep_bin "^script_version=" | head -n1 | cut -d '"' -f 2)" + # shellcheck disable=SC2086 + latest_config_version="$($curl_bin --compressed $curl_proxy $curl_insecure $curl_output_level --connect-timeout "${downloader_connect_timeout}" --remote-time --location --retry "${downloader_tries}" --max-time "${downloader_max_time}" "https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/${git_branch}/config/master.conf" 2>&11 | $grep_bin "^config_version=" | head -n1 | cut -d '"' -f 2)" + else + # shellcheck disable=SC2086 + latest_version="$($wget_bin $wget_compression $wget_proxy $wget_insecure $wget_output_level --connect-timeout="${downloader_connect_timeout}" --random-wait --tries="${downloader_tries}" --timeout="${downloader_max_time}" "https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/${git_branch}/clamav-unofficial-sigs.sh" -O - 2>&12 | $grep_bin "^script_version=" | head -n1 | cut -d '"' -f 2)" + # shellcheck disable=SC2086 + latest_config_version="$($wget_bin $wget_compression $wget_proxy $wget_insecure $wget_output_level --connect-timeout="${downloader_connect_timeout}" --random-wait --tries="${downloader_tries}" --timeout="${downloader_max_time}" "https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/${git_branch}/config/master.conf" -O - 2>&12 | $grep_bin "^config_version=" | head -n1 | cut -d '"' -f 2)" + fi if [ "$latest_version" ] ; then - if [ "$latest_version" != "$script_version" ] ; then - xshok_pretty_echo_and_log "New version : v$latest_version @ https://github.com/extremeshok/clamav-unofficial-sigs" "-" + # shellcheck disable=SC2183,SC2086 + if [ "$(printf "%02d%02d%02d%02d" ${latest_version//./ })" -gt "$(printf "%02d%02d%02d%02d" ${script_version//./ })" ] ; then + xshok_pretty_echo_and_log "ALERT: New version : v${latest_version} @ https://github.com/extremeshok/clamav-unofficial-sigs" + found_upgrade="yes" fi fi -} - -# Check for a new version -function check_new_config_version () { - if [ -n "$wget_bin" ] ; then - # shellcheck disable=SC2086 - latest_config_version="$($wget_bin $wget_proxy_https $wget_proxy_http $wget_insecure $wget_output_level --connect-timeout="$downloader_connect_timeout" --random-wait --tries="$downloader_tries" --timeout="$downloader_max_time" https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/master/config/master.conf -O - 2> /dev/null | $grep_bin "config_version=" | cut -d '"' -f 2)" - else - # shellcheck disable=SC2086 - latest_config_version="$($curl_bin $curl_proxy $curl_insecure $curl_output_level --connect-timeout "$downloader_connect_timeout" --remote-time --location --retry "$downloader_tries" --max-time "$downloader_max_time" https://raw.githubusercontent.com/extremeshok/clamav-unofficial-sigs/master/config/master.conf 2> /dev/null | $grep_bin "config_version=" | cut -d '"' -f 2)" - fi if [ "$latest_config_version" ] ; then - if [ "$latest_config_version" != "$config_version" ] ; then - xshok_pretty_echo_and_log "New configversion : v${latest_config_version} @ https://github.com/extremeshok/clamav-unofficial-sigs" "-" + # shellcheck disable=SC2183,SC2086 + if [ "$(printf "%02d%02d%02d%02d" ${latest_config_version//./ })" -gt "$(printf "%02d%02d%02d%02d" ${config_version//./ })" ] ; then + xshok_pretty_echo_and_log "ALERT: New config version : v${latest_config_version} @ https://github.com/extremeshok/clamav-unofficial-sigs" + found_upgrade="yes" fi fi + +if [ "$found_upgrade" == "yes" ] && [ "$allow_upgrades" == "yes" ] ; then + xshok_pretty_echo_and_log "Quickly upgrade, run the following command as root:" + xshok_pretty_echo_and_log "${this_script_name} --upgrade" +fi + } # Display help and usage # Usage: # help_and_usage "1" - enables the man output formatting # help_and_usage - normal help output formatting -function help_and_usage () { +function help_and_usage() { - if [ "$1" ] ; then + if [ "${1}" ] ; then # option_format_start - ofs="\fB" + ofs="\\fB" # option_format_end - ofe="\fR" + ofe="\\fR" # option_format_blankline ofb=".TP" # option_format_tab_line @@ -1218,136 +1483,213 @@ function help_and_usage () { # option_format_start ofs="${BOLD}" # option_format_end - ofe="${NORM}\t" + ofe="${NORM}\\t" # option_format_blankline - ofb="\n" + ofb="\\n" # option_format_tab_line - oft="\n\t" + oft="\\n\\t" fi helpcontents="$(cat << EOF -$ofs Usage: $(basename "$0") $ofe [OPTION] [PATH|FILE] -$ofb -$ofs -c, --config $ofe Use a specific configuration file or directory $oft eg: '-c /your/dir' or ' -c /your/file.name' $oft Note: If a directory is specified the directory must contain atleast: $oft master.conf, os.conf or user.conf $oft Default Directory: $config_dir -$ofb -$ofs -F, --force $ofe Force all databases to be downloaded, could cause ip to be blocked -$ofb -$ofs -h, --help $ofe Display this script's help and usage information -$ofb -$ofs -V, --version $ofe Output script version and date information -$ofb -$ofs -v, --verbose $ofe Be verbose, enabled when not run under cron -$ofb -$ofs -s, --silence $ofe Only output error messages, enabled when run under cron -$ofb -$ofs -d, --decode-sig $ofe Decode a third-party signature either by signature name $oft (eg: Sanesecurity.Junk.15248) or hexadecimal string. $oft This flag will 'NOT' decode image signatures -$ofb -$ofs -e, --encode-string $ofe Hexadecimal encode an entire input string that can $oft be used in any '*.ndb' signature database file -$ofb -$ofs -f, --encode-formatted $ofe Hexadecimal encode a formatted input string containing $oft signature spacing fields '{}, (), *', without encoding $oft the spacing fields, so that the encoded signature $oft can be used in any '*.ndb' signature database file -$ofb -$ofs -g, --gpg-verify $ofe GPG verify a specific Sanesecurity database file $oft eg: '-g filename.ext' (do not include file path) -$ofb -$ofs -i, --information $ofe Output system and configuration information for $oft viewing or possible debugging purposes -$ofb -$ofs -m, --make-database $ofe Make a signature database from an ascii file containing $oft data strings, with one data string per line. Additional $oft information is provided when using this flag -$ofb -$ofs -t, --test-database $ofe Clamscan integrity test a specific database file $oft eg: '-t filename.ext' (do not include file path) -$ofb -$ofs -o, --output-triggered $ofe If HAM directory scanning is enabled in the script's $oft configuration file, then output names of any third-party $oft signatures that triggered during the HAM directory scan -$ofb -$ofs -w, --whitelist $ofe Adds a signature whitelist entry in the newer ClamAV IGN2 $oft format to 'my-whitelist.ign2' in order to temporarily resolve $oft a false-positive issue with a specific third-party signature. $oft Script added whitelist entries will automatically be removed $oft if the original signature is either modified or removed from $oft the third-party signature database -$ofb -$ofs --check-clamav $ofe If ClamD status check is enabled and the socket path is correctly $oft specifiedthen test to see if clamd is running or not -$ofb -$ofs --install-all $ofe Install and generate the cron, logroate and man files, autodetects the values $oft based on your config files -$ofb -$ofs --install-cron $ofe Install and generate the cron file, autodetects the values $oft based on your config files -$ofb -$ofs --install-logrotate $ofe Install and generate the logrotate file, autodetects the $oft values based on your config files -$ofb -$ofs --install-man $ofe Install and generate the man file, autodetects the $oft values based on your config files -$ofb -$ofs --remove-script $ofe Remove the clamav-unofficial-sigs script and all of $oft its associated files and databases from the system -$ofb +${ofs} Usage: $(basename "$0") ${ofe} [OPTION] [PATH|FILE] +${ofb} +${ofs} -c, --config ${ofe} Use a specific configuration file or directory ${oft} eg: '-c /your/dir' or ' -c /your/file.name' ${oft} Note: If a directory is specified the directory must contain atleast: ${oft} master.conf, os.conf or user.conf ${oft} Default Directory: ${config_dir} +${ofb} +${ofs} -F, --force ${ofe} Force all databases to be downloaded, could cause ip to be blocked +${ofb} +${ofs} -h, --help ${ofe} Display this script's help and usage information +${ofb} +${ofs} -V, --version ${ofe} Output script version and date information +${ofb} +${ofs} -v, --verbose ${ofe} Be verbose, enabled when not run under cron +${ofb} +${ofs} -s, --silence ${ofe} Only output error messages, enabled when run under cron +${ofb} +${ofs} -d, --decode-sig ${ofe} Decode a third-party signature either by signature name ${oft} (eg: Sanesecurity.Junk.15248) or hexadecimal string. ${oft} This flag will 'NOT' decode image signatures +${ofb} +${ofs} -e, --encode-string ${ofe} Hexadecimal encode an entire input string that can ${oft} be used in any '*.ndb' signature database file +${ofb} +${ofs} -f, --encode-formatted ${ofe} Hexadecimal encode a formatted input string containing ${oft} signature spacing fields '{}, (), *', without encoding ${oft} the spacing fields, so that the encoded signature ${oft} can be used in any '*.ndb' signature database file +${ofb} +${ofs} -g, --gpg-verify ${ofe} GPG verify a specific Sanesecurity database file ${oft} eg: '-g filename.ext' (do not include file path) +${ofb} +${ofs} -i, --information ${ofe} Output system and configuration information for ${oft} viewing or possible debugging purposes +${ofb} +${ofs} -m, --make-database ${ofe} Make a signature database from an ascii file containing ${oft} data strings, with one data string per line. Additional ${oft} information is provided when using this flag +${ofb} +${ofs} -t, --test-database ${ofe} Clamscan integrity test a specific database file ${oft} eg: '-t filename.ext' (do not include file path) +${ofb} +${ofs} -o, --output-triggered ${ofe} If HAM directory scanning is enabled in the script's ${oft} configuration file, then output names of any third-party ${oft} signatures that triggered during the HAM directory scan +${ofb} +${ofs} -w, --whitelist ${ofe} Adds a signature whitelist entry in the newer ClamAV IGN2 ${oft} format to 'my-whitelist.ign2' in order to temporarily resolve ${oft} a false-positive issue with a specific third-party signature. ${oft} Script added whitelist entries will automatically be removed ${oft} if the original signature is either modified or removed from ${oft} the third-party signature database +${ofb} +${ofs} --check-clamav ${ofe} If ClamD status check is enabled and the socket path is correctly ${oft} specifiedthen test to see if clamd is running or not +${ofb} +${ofs} --upgrade ${ofe} Upgrades this script and master.conf to the latest available version +${ofb} +${ofs} --install-all ${ofe} Install and generate the cron, logroate and man files, autodetects the values ${oft} based on your config files +${ofb} +${ofs} --install-cron ${ofe} Install and generate the cron file, autodetects the values ${oft} based on your config files +${ofb} +${ofs} --install-logrotate ${ofe} Install and generate the logrotate file, autodetects the ${oft} values based on your config files +${ofb} +${ofs} --install-man ${ofe} Install and generate the man file, autodetects the ${oft} values based on your config files +${ofb} +${ofs} --remove-script ${ofe} Remove the clamav-unofficial-sigs script and all of ${oft} its associated files and databases from the system +${ofb} EOF )" # This is very important -if [ "$1" ] ; then - echo "${helpcontents//-/\\-}" -else + if [ "${1}" ] ; then + echo "${helpcontents//-/\\-}" + else echo -e "$helpcontents" fi } - ################################################################################ # MAIN PROGRAM ################################################################################ # Script Info -script_version="5.6.2" -script_version_date="2017-03-19" -minimum_required_config_version="72" -minimum_yara_clamav_version="0.99" +script_version="7.2.5" +script_version_date="2021-03-20" +minimum_required_config_version="96" +minimum_yara_clamav_version="0.100" + +# Discover script: name, full_path and path +this_script_full_path="${BASH_SOURCE[0]}" +# follow the symlinks +while [ -h "$this_script_full_path" ]; do + this_script_path="$( cd -P "$( dirname "$this_script_full_path" )" >/dev/null 2>&1 && pwd )" + this_script_full_path="$(readlink "$this_script_full_path")" + # if relative symlink, then resolve the path + if [[ $this_script_full_path != /* ]] ; then + this_script_full_path="$this_script_path/$this_script_full_path" + fi +done +this_script_path="$( cd -P "$( dirname "$this_script_full_path" )" >/dev/null 2>&1 && pwd )" +this_script_name="$(basename "$this_script_full_path")" -# Default config files -config_dir="/etc/clamav/unofficial-sigs" -config_files=( "$config_dir/master.conf" "$config_dir/os.conf" "$config_dir/user.conf" ) +if [ -z "$this_script_full_path" ] || [ -z "$this_script_path" ] || [ -z "$this_script_name" ] ; then + echo "ERROR: could not determin script name and fullpath" + exit 1 +fi + +#allow for other negatives besides no. +#disabled_values_array=("0 no No NO false False FALSE off Off OFF disable Disable DISABLE disabled Disabled DISABLED") +# if [[ " ${disabled_values_array[@]} " =~ " ${value} " ]]; then +# # whatever you want to do when arr contains value +# fi +# +# if [[ ! " ${disabled_values_array[@]} " =~ " ${value} " ]]; then +# # whatever you want to do when arr doesn't contain value +# fi # Initialise config_version="0" do_clamd_reload="0" comment_silence="no" +force_verbose="no" logging_enabled="no" force_updates="no" +force_wget="no" enable_log="no" custom_config="no" we_have_a_config="0" -# Solaris which function returns garbage when the program is not found -# only define the new which function if running under Solaris -if [ "$(uname -s)" == "SunOS" ] ; then - which () { - # Use the switch -p to ignore ksh internal commands - ksh whence -p "$@" - } -fi -# Default Binaries & Commands -uname_bin="$(which uname 2> /dev/null)" -clamscan_bin="$(which clamscan 2> /dev/null)" -rsync_bin="$(which rsync 2> /dev/null)" -# Detect support for wget -if [ -x /usr/sfw/bin/wget ] ; then - wget_bin="/usr/sfw/bin/wget" +# Attempt to scan for a valid config dir +if [ -f "/etc/clamav-unofficial-sigs/master.conf" ] ; then + config_dir="/etc/clamav-unofficial-sigs" +elif [ -f "/usr/local/etc/clamav-unofficial-sigs/master.conf" ] ; then + config_dir="/usr/local/etc/clamav-unofficial-sigs/" +elif [ -f "/opt/zimbra/conf/clamav-unofficial-sigs/master.conf" ] ; then + config_dir="/opt/zimbra/conf/clamav-unofficial-sigs/" else - wget_bin="$(which wget 2> /dev/null)" + xshok_pretty_echo_and_log "ERROR: config_dir (/etc/clamav-unofficial-sigs/master.conf) could not be found" + exit 1 fi -if [ -z "$wget_bin" ] ; then - curl_bin="$(which curl 2> /dev/null)" - if [ -z "$curl_bin" ] ; then - xshok_pretty_echo_and_log "ERROR: both wget and curl commands are missing, One of them is required" "=" +# Default config files +if [ -r "${config_dir}/master.conf" ] ; then + config_files+=( "${config_dir}/master.conf" ) +else + xshok_pretty_echo_and_log "ERROR: ${config_dir}/master.conf is not readable" exit 1 - fi fi - -# Detect supprot for gnu grep -if [ -x /usr/gnu/bin/grep ] ; then - grep_bin="/usr/gnu/bin/grep" +if [ -r "${config_dir}/os.conf" ] ; then + config_files+=( "${config_dir}/os.conf" ) +else + #find the a suitable os.*.conf file + os_config_number=$(find "$config_dir" -type f -iname "os.*.conf" | wc -l) + if [ "$os_config_number" == "0" ] ; then + xshok_pretty_echo_and_log "WARNING: no os.conf or os.*.conf found" + elif [ "$os_config_number" == "1" ] ; then + config_file="$(find "$config_dir" -type f -iname "os.*.conf" | head -n1)" + if [ -r "${config_file}" ]; then + config_files+=( "${config_file}" ) + else + xshok_pretty_echo_and_log "WARNING: ${config_file} is not readable" + fi + else + xshok_pretty_echo_and_log "WARNING: Too many os.*.conf configs found" + fi +fi +if [ -r "${config_dir}/user.conf" ] ; then + config_files+=( "${config_dir}/user.conf" ) else - grep_bin="$(which grep 2> /dev/null)" + xshok_pretty_echo_and_log "WARNING: ${config_dir}/user.conf is not readable" fi -dig_bin="$(which dig 2> /dev/null)" -if [ -z "$dig_bin" ] ; then - host_bin="$(which host 2> /dev/null)" - if [ -z "$host_bin" ] ; then - xshok_pretty_echo_and_log "ERROR: both dig and host commands are missing, One of them is required" "=" - exit 1 - fi +# Solaris command -v function returns garbage when the program is not found k +# only define the new command -v function if running under Solaris +if [ "$(uname -s)" == "SunOS" ] ; then + function which() { + # Use the switch -p to ignore ksh internal commands + ksh whence -p "$@" + } fi +# sed_bin, this is required to be known upfront, due to how the configs are read. +if [ -z "$sed_bin" ] ; then + # Detect support for sed or gsed + if [ "$(uname -s)" == "Darwin" ] || [ "$(uname -s)" == "OpenBSD" ] || [ "$(uname -s)" == "NetBSD" ] || [ "$(uname -s)" == "FreeBSD" ] ; then + sed_bin="$(command -v gsed 2> /dev/null)" + if [ -z "$sed_bin" ]; then + xshok_pretty_echo_and_log "ERROR: gsed (gnu sed) is missing" + exit 1 + fi + else + sed_bin="$(command -v sed 2> /dev/null)" + if [ -z "$sed_bin" ]; then + xshok_pretty_echo_and_log "ERROR: sed is missing" + exit 1 + fi + fi +elif [[ "$sed_bin" =~ "/" ]] ; then + if [ ! -x "$sed_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: sed (${sed_bin}) is not executable" + exit 1 + fi +fi +# grep_bin, this is required to be known upfront, due to how the configs are read. +if [ -z "$grep_bin" ] ; then + # Detect support for grep or gnugrep + if [ -x /usr/gnu/bin/grep ] ; then + grep_bin="/usr/gnu/bin/grep" + else + grep_bin="$(command -v grep 2> /dev/null)" + if [ -z "$grep_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: grep binary (grep_bin) not found" + exit 1 + fi + fi +elif [[ "$grep_bin" =~ "/" ]] ; then + if [ ! -x "$grep_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: grep (${grep_bin}) is not executable" + exit 1 + fi +fi # Detect if terminal if [ -t 1 ] ; then @@ -1367,11 +1709,10 @@ else force_verbose="no" fi - # Generic command line options while true ; do - case "$1" in - -c|--config) xshok_check_s2 "$2"; custom_config="$2"; shift 2; break ;; + case "${1}" in + -c|--config) xshok_check_s2 "${2}"; custom_config="${2}"; shift 2; break ;; -F|--force) force_updates="yes"; shift 1; break ;; -v|--verbose) force_verbose="yes"; shift 1; break ;; -s|--silence) force_verbose="no"; shift 1; break ;; @@ -1396,14 +1737,14 @@ fi xshok_pretty_echo_and_log "" "#" "80" xshok_pretty_echo_and_log " eXtremeSHOK.com ClamAV Unofficial Signature Updater" -xshok_pretty_echo_and_log " Version: v$script_version ($script_version_date)" -xshok_pretty_echo_and_log " Required Configuration Version: v$minimum_required_config_version" +xshok_pretty_echo_and_log " Version: v${script_version} (${script_version_date})" +xshok_pretty_echo_and_log " Required Configuration Version: v${minimum_required_config_version}" xshok_pretty_echo_and_log " Copyright (c) Adrian Jon Kriel :: admin@extremeshok.com" xshok_pretty_echo_and_log "" "#" "80" # Generic command line options while true ; do - case "$1" in + case "${1}" in -h|--help) help_and_usage; exit ;; -V|--version) exit ;; *) break ;; @@ -1414,8 +1755,25 @@ done if [ "$custom_config" != "no" ] ; then if [ -d "$custom_config" ] ; then # Assign the custom config dir and remove trailing / (removes / and //) - shopt -s extglob; custom_config="${custom_config%%+(/)}" - config_files=( "$config_dir/master.conf" "$config_dir/os.conf" "$config_dir/user.conf" ) + shopt -s extglob; config_dir="${custom_config%%+(/)}" + config_files=() + if [ -r "${config_dir}/master.conf" ] ; then + config_files+=( "${config_dir}/master.conf" ) + else + xshok_pretty_echo_and_log "WARNING: ${config_dir}/master.conf not found" + fi + #find the a suitable os.conf or os.*.conf file + config_file="$(find "$config_dir" -type f -iname "os.conf" -o -iname "os.*.conf" | tail -n1)" + if [ -r "${config_file}" ] ; then + config_files+=( "${config_file}" ) + else + xshok_pretty_echo_and_log "WARNING: ${config_dir}/os.conf not found" + fi + if [ -r "${config_dir}/user.conf" ] ; then + config_files+=( "${config_dir}/user.conf" ) + else + xshok_pretty_echo_and_log "WARNING: ${config_dir}/user.conf not found" + fi else config_files=( "$custom_config" ) fi @@ -1425,28 +1783,32 @@ for config_file in "${config_files[@]}" ; do if [ -r "$config_file" ] ; then # Exists and readable we_have_a_config="1" # Config stripping - xshok_pretty_echo_and_log "Loading config: $config_file" "=" - - + xshok_pretty_echo_and_log "Loading config: ${config_file}" if [ "$(uname -s)" == "SunOS" ] ; then # Solaris FIXES only, i had issues with running with a single command.. - clean_config="$(command sed -e '/^#.*/d' "$config_file")" # Comment line - #clean_config="$(echo "$clean_config" | sed -e 's/#[[:space:]].*//')" # Comment line (duplicated) + clean_config="$(command "$sed_bin" -e '/^#.*/d' "$config_file")" # Comment line + #clean_config="$(echo "$clean_config" | $sed_bin -e 's/#[[:space:]].*//')" # Comment line (duplicated) clean_config=${clean_config//\#*/} # Comment line (duplicated) - clean_config="$(echo "$clean_config" | sed -e '/^[[:blank:]]*#/d;s/#.*//')" # Comments at end of line - #clean_config="$(echo "$clean_config" | sed -e 's/^[ \t]*//;s/[ \t]*$//')" # trailing and leading whitespace + # shellcheck disable=SC2001 + clean_config="$(echo "$clean_config" | $sed_bin -e '/^[[:blank:]]*#/d;s/#.*//')" # Comments at end of line + #clean_config="$(echo "$clean_config" | $sed_bin -e 's/^[[:blank:]]*//;s/[[:blank:]]*$//')" # trailing and leading whitespace clean_config="$(echo "$clean_config" | xargs)" - clean_config="$(echo "$clean_config" | sed -e '/^\s*$/d')" # Blank lines - - elif [ "$(uname -s)" == "Darwin" ] ; then - # MacOS / OS X fixes, had issues with running with a single command and with SunOS work around.. - clean_config="$(command sed -e '/^#.*/d' "$config_file")" # Comment line - clean_config="$(echo "$clean_config" | sed -e 's/#[[:space:]].*//')" # Comment line (duplicated) - clean_config="$(echo "$clean_config" | sed -e '/^[[:blank:]]*#/d;s/#.*//')" # Comments at end of line - #clean_config="$(echo "$clean_config" | sed -e 's/^[ \t]*//;s/[ \t]*$//')" # trailing and leading whitespace + # shellcheck disable=SC2001 + clean_config="$(echo "$clean_config" | $sed_bin -e '/^\s*$/d')" # Blank lines + + elif [ "$(uname -s)" == "Darwin" ] || [ "$(uname -s)" == "OpenBSD" ] || [ "$(uname -s)" == "NetBSD" ] || [ "$(uname -s)" == "FreeBSD" ] ; then + # macOS / OSX / BSD fixes, had issues with running with a single command and with SunOS work around.. + # shellcheck disable=SC2001 + clean_config="$(command "$sed_bin" -e '/^#.*/d' "$config_file")" # Comment line + # shellcheck disable=SC2001 + clean_config="$(echo "$clean_config" | $sed_bin -e 's/#[[:space:]].*//')" # Comment line (duplicated) + # shellcheck disable=SC2001 + clean_config="$(echo "$clean_config" | $sed_bin -e '/^[[:blank:]]*#/d;s/#.*//')" # Comments at end of line + #clean_config="$(echo "$clean_config" | $sed_bin -e 's/^[[:blank:]]*//;s/[[:blank:]]*$//')" # trailing and leading whitespace #clean_config="$(echo "$clean_config" | xargs)" - clean_config="$(echo "$clean_config" | sed -e '/^\s*$/d')" # Blank lines + # shellcheck disable=SC2001 + clean_config="$(echo "$clean_config" | $sed_bin -e '/^\s*$/d')" # Blank lines else # Delete lines beginning with # @@ -1455,7 +1817,7 @@ for config_file in "${config_files[@]}" ; do # Delete both trailing and leading whitespace # Delete all trailing whitespace # Delete all empty lines - clean_config="$(command sed -e '/^#.*/d' -e 's/[[:space:]]#.*//' -e 's/#[[:space:]].*//' -e 's/^[ \t]*//;s/[ \t]*$//' -e '/^\s*$/d' "$config_file")" + clean_config="$(command "$sed_bin" -e '/^#.*/d' -e 's/[[:space:]]#.*//' -e 's/#[[:space:]].*//' -e 's/^[[:blank:]]*//;s/[[:blank:]]*$//' -e '/^[[:space:]]*$/d' "$config_file")" fi #fix eval of | @@ -1465,15 +1827,15 @@ for config_file in "${config_files[@]}" ; do # Check "" are an even number config_check="${clean_config//[^\"]}" if [ "$(( ${#config_check} % 2 ))" -eq 1 ] ; then - xshok_pretty_echo_and_log "ERROR: Your configuration has errors, every \" requires a closing \"" "=" + xshok_pretty_echo_and_log "ERROR: Your configuration has errors, every \" requires a closing \"" exit 1 fi # Check there is an = for every set of "" optional whitespace \s* between = and " - config_check_vars="$(echo "$clean_config" | $grep_bin -c '=\s*\"' )" + config_check_vars="$(echo "$clean_config" | $grep_bin -c '=[[:space:]]*\"' )" if [ $(( ${#config_check} / 2 )) -ne "$config_check_vars" ] ; then - xshok_pretty_echo_and_log "ERROR: Your configuration has errors, every = requires a pair of \"\"" "=" + xshok_pretty_echo_and_log "ERROR: Your configuration has errors, every = requires a pair of \"\"" exit 1 fi @@ -1482,7 +1844,7 @@ for config_file in "${config_files[@]}" ; do # Config loading for i in "${clean_config[@]}" ; do - eval "$(echo "${i}" | command sed -e 's/[[:space:]]*$//' 2> /dev/null)" + eval "$(echo "${i}" | command "$sed_bin" -e 's/[[:space:]]*$//' 2> /dev/null)" done fi done @@ -1497,27 +1859,27 @@ fi # Make sure we have a readable config file if [ "$we_have_a_config" == "0" ] ; then - xshok_pretty_echo_and_log "ERROR: Config file/s could NOT be read/loaded" "=" - xshok_pretty_echo_and_log "Note: Possible fix would be to checkl the config dir $config_dir exists and contains config files" + xshok_pretty_echo_and_log "ERROR: Config file/s could NOT be read/loaded" + xshok_pretty_echo_and_log "Note: Possible fix would be to checkl the config dir ${config_dir} exists and contains config files" exit 1 fi # Prevent some issues with an incomplete or only a user.conf being loaded if [ "$config_version" == "0" ] ; then - xshok_pretty_echo_and_log "ERROR: Config file/s are missing important contents" "=" + xshok_pretty_echo_and_log "ERROR: Config file/s are missing important contents" xshok_pretty_echo_and_log "Note: Possible fix would be to point the script to the dir with the configs" exit 1 fi # Config version validation if [ "$config_version" -lt "$minimum_required_config_version" ] ; then - xshok_pretty_echo_and_log "ERROR: Your config version $config_version is not compatible with the min required version $minimum_required_config_version" "=" + xshok_pretty_echo_and_log "ERROR: Your config version ${config_version} is not compatible with the min required version ${minimum_required_config_version}" exit 1 fi # Check to see if the script's "USER CONFIGURATION FILE" has been completed. if [ "$user_configuration_complete" != "yes" ] ; then - xshok_pretty_echo_and_log "WARNING: SCRIPT CONFIGURATION HAS NOT BEEN COMPLETED" "*" + xshok_pretty_echo_and_log "WARNING: SCRIPT CONFIGURATION HAS NOT BEEN COMPLETED" xshok_pretty_echo_and_log "Please review the script configuration files" xshok_pretty_echo_and_log "and uncomment the following line in user.conf" xshok_pretty_echo_and_log "#user_configuration_complete=\"yes\"" @@ -1529,48 +1891,63 @@ shopt -s extglob; work_dir="${work_dir%%+(/)}" # Allow overriding of all the individual workdirs, this is mainly to aid package maintainers if [ -z "$work_dir_sanesecurity" ] ; then - work_dir_sanesecurity="$(echo "$work_dir/$sanesecurity_dir" | sed 's:/*$::')" + work_dir_sanesecurity="$(echo "${work_dir}/${sanesecurity_dir}" | $sed_bin 's:/*$::')" else shopt -s extglob; work_dir_sanesecurity="${work_dir_sanesecurity%%+(/)}" fi if [ -z "$work_dir_securiteinfo" ] ; then - work_dir_securiteinfo="$(echo "$work_dir/$securiteinfo_dir" | sed 's:/*$::')" + work_dir_securiteinfo="$(echo "${work_dir}/${securiteinfo_dir}" | $sed_bin 's:/*$::')" else shopt -s extglob; work_dir_securiteinfo="${work_dir_securiteinfo%%+(/)}" fi if [ -z "$work_dir_linuxmalwaredetect" ] ; then - work_dir_linuxmalwaredetect="$(echo "$work_dir/$linuxmalwaredetect_dir" | sed 's:/*$::')" + work_dir_linuxmalwaredetect="$(echo "${work_dir}/${linuxmalwaredetect_dir}" | $sed_bin 's:/*$::')" else shopt -s extglob; work_dir_malwarepatrol="${work_dir_malwarepatrol%%+(/)}" fi +if [ -z "$work_dir_interserver" ] ; then + work_dir_interserver="$(echo "${work_dir}/${interserver_dir}" | $sed_bin 's:/*$::')" +else + shopt -s extglob; work_dir_interserver="${work_dir_interserver%%+(/)}" +fi +if [ -z "$work_dir_malwareexpert" ] ; then + work_dir_malwareexpert="$(echo "${work_dir}/${malwareexpert_dir}" | $sed_bin 's:/*$::')" +else + shopt -s extglob; work_dir_malwareexpert="${work_dir_malwareexpert%%+(/)}" +fi if [ -z "$work_dir_malwarepatrol" ] ; then - work_dir_malwarepatrol="$(echo "$work_dir/$malwarepatrol_dir" | sed 's:/*$::')" + work_dir_malwarepatrol="$(echo "${work_dir}/${malwarepatrol_dir}" | $sed_bin 's:/*$::')" else shopt -s extglob; work_dir_malwarepatrol="${work_dir_malwarepatrol%%+(/)}" fi +if [ -z "$work_dir_urlhaust" ] ; then + work_dir_urlhaus="$(echo "${work_dir}/${urlhaus_dir}" | $sed_bin 's:/*$::')" +else + shopt -s extglob; work_dir_urlhaus="${work_dir_urlhaus%%+(/)}" +fi if [ -z "$work_dir_yararulesproject" ] ; then - work_dir_yararulesproject="$(echo "$work_dir/$yararulesproject_dir" | sed 's:/*$::')" + work_dir_yararulesproject="$(echo "${work_dir}/${yararulesproject_dir}" | $sed_bin 's:/*$::')" else shopt -s extglob; work_dir_yararulesproject="${work_dir_yararulesproject%%+(/)}" fi if [ -z "$work_dir_add" ] ; then - work_dir_add="$(echo "$work_dir/$add_dir" | sed 's:/*$::')" + work_dir_add="$(echo "${work_dir}/${add_dir}" | $sed_bin 's:/*$::')" else shopt -s extglob; work_dir_add="${work_dir_add%%+(/)}" fi if [ -z "$work_dir_work_configs" ] ; then - work_dir_work_configs="$(echo "$work_dir/$work_dir_configs" | sed 's:/*$::')" + work_dir_work_configs="$(echo "${work_dir}/${work_dir_configs}" | $sed_bin 's:/*$::')" else shopt -s extglob; work_dir_work_configs="${work_dir_work_configs%%+(/)}" fi -if [ -z "$work_dir_gpg" ] ; then - work_dir_gpg="$(echo "$work_dir/$gpg_dir" | sed 's:/*$::')" +if [ -z "${work_dir_gpg}" ] ; then + work_dir_gpg="$(echo "${work_dir}/${gpg_dir}" | $sed_bin 's:/*$::')" else shopt -s extglob; work_dir_gpg="${work_dir_gpg%%+(/)}" fi if [ -z "$work_dir_pid" ] ; then - work_dir_pid="$(echo "$work_dir/$pid_dir" | sed 's:/*$::')" + work_dir_pid="$(echo "${work_dir}/${pid_dir}" | $sed_bin 's:/*$::')" else shopt -s extglob; work_dir_pid="${work_dir_pid%%+(/)}" fi @@ -1598,86 +1975,202 @@ if [ -z "$man_filename" ] ; then man_filename="clamav-unofficial-sigs.8" fi if [ -z "$man_log_file_full_path" ] ; then - man_log_file_full_path="$log_file_path/$log_file_name" + man_log_file_full_path="${log_file_path}/${log_file_name}" fi # dont assign , but remove trailing / shopt -s extglob; clam_dbs="${clam_dbs%%+(/)}" -# SANITY checks -# Check default Binaries & Commands are defined -if [ "$reload_dbs" == "yes" ] ; then - if [ -z "$clamd_reload_opt" ] ; then - xshok_pretty_echo_and_log "ERROR: Missing clamd_reload_opt" "=" - exit 1 - fi +##################################################################################################### +# Assign and Check Binaries/Commands +# clamscan_bin +if [ -z "$clamscan_bin" ] && [ "${1}" != "--remove-script" ] ; then + clamscan_bin="$(command -v clamscan 2> /dev/null)" + if [ -z "$clamscan_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: clamscan binary (clamscan_bin) not found" + exit 1 + fi +elif [[ "$clamscan_bin" =~ "/" ]] && [ "${1}" != "--remove-script" ] ; then + if [ ! -x "$clamscan_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: clamscan_bin (${clamscan_bin})is not executable" + exit 1 + + fi fi +# uname_bin if [ -z "$uname_bin" ] ; then - xshok_pretty_echo_and_log "ERROR: uname (uname_bin) not found" "=" - exit 1 -fi -if [ -z "$clamscan_bin" ] ; then - xshok_pretty_echo_and_log "ERROR: clamscan binary (clamscan_bin) not found" "=" - exit 1 + uname_bin="$(command -v uname 2> /dev/null)" + if [ -z "$uname_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: uname binary (uname_bin) not found" + exit 1 + fi +elif [[ "$uname_bin" =~ "/" ]] ; then + if [ ! -x "$uname_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: uname_bin (${uname_bin}) is not executable" + exit 1 + + fi fi +# rsync_bin if [ -z "$rsync_bin" ] ; then - xshok_pretty_echo_and_log "ERROR: rsync binary (rsync_bin) not found" "=" - exit 1 + rsync_bin="$(command -v rsync 2> /dev/null)" + if [ -z "$rsync_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: rsync binary (rsync_bin) not found" + exit 1 + fi +elif [[ "$rsync_bin" =~ "/" ]] ; then + if [ ! -x "$rsync_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: rsync_bin (${rsync_bin}) is not executable" + exit 1 + + fi fi -if [ -z "$wget_bin" ] ; then - if [ -z "$curl_bin" ] ; then - xshok_pretty_echo_and_log "ERROR: wget and curl binaries not found, script requires either wget or curl" "=" - exit 1 - fi +# tar_bin +if [ -z "$tar_bin" ] ; then + tar_bin="$(command -v tar 2> /dev/null)" + if [ -z "$tar_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: tar binary (tar_bin) not found" + exit 1 + fi +elif [[ "$tar_bin" =~ "/" ]] ; then + if [ ! -x "$tar_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: tar_bin (${tar_bin}) is not executable" + exit 1 + fi fi -# Check if GPG is enabled and the binary is found + +# gpg_bin if [ "$enable_gpg" == "yes" ] ; then - if [ -z "$gpg_bin" ] ; then - if [ -x /opt/csw/bin/gpg ] ; then - gpg_bin="/opt/csw/bin/gpg" - else - gpg_bin="$(which gpg 2> /dev/null)" - fi if [ -z "$gpg_bin" ] ; then - gpg_bin="$(which gpg2 2> /dev/null)" + if [ -x "/opt/csw/bin/gpg" ] ; then + gpg_bin="/opt/csw/bin/gpg" + else + gpg_bin="$(command -v gpg 2> /dev/null)" + if [ -z "$gpg_bin" ] ; then + enable_gpg="no" + fi + fi + elif [[ "$gpg_bin" =~ "/" ]] ; then + if [ ! -x "$gpg_bin" ] ; then + enable_gpg="no" + fi + fi +fi +# curl_bin +if [ -z "$curl_bin" ] ; then + curl_bin="$(command -v curl 2> /dev/null)" +elif [[ "$curl_bin" =~ "/" ]] ; then + if [ ! -x "$curl_bin" ] ; then + curl_bin="" + fi +fi +# wget_bin +if [ -z "$curl_bin" ] || [ "$force_wget" == "yes" ] ; then + if [ -z "$wget_bin" ] ; then + if [ -x /usr/sfw/bin/wget ] ; then + wget_bin="/usr/sfw/bin/wget" + else + wget_bin="$(command -v wget 2> /dev/null)" + if [ -z "$wget_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: both wget (wget_bin) and curl (curl_bin) commands are missing, One of them is required" + exit 1 + fi + fi + elif [[ "$wget_bin" =~ "/" ]] ; then + if [ ! -x "$wget_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: wget_bin (${wget_bin}) is not executable" + exit 1 + + fi + fi + if [ -n "$wget_bin" ] ; then + # wget compression support + if $wget_bin --help 2> /dev/null | $grep_bin -q "compression=TYPE" 2> /dev/null ; then + wget_compression="--compression=auto" + else + wget_compression="" + fi + fi +else + wget_bin="" + wget_compression="" + force_wget="no" +fi + + +# dig_bin +if [ -z "$dig_bin" ] ; then + dig_bin="$(command -v dig 2> /dev/null)" +elif [[ "$dig_bin" =~ "/" ]] ; then + if [ ! -x "$dig_bin" ] ; then + dig_bin="" + fi +fi +# host_bin +if [ -z "$dig_bin" ] || [ "$force_host" == "yes" ] ; then + if [ -z "$host_bin" ] ; then + host_bin="$(command -v host 2> /dev/null)" + if [ -z "$host_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: both host (host_bin) and dig (dig_bin) commands are missing, One of them is required" + exit 1 + fi + elif [[ "$host_bin" =~ "/" ]] ; then + if [ ! -x "$host_bin" ] ; then + xshok_pretty_echo_and_log "ERROR: host_bin (${host_bin}) is not executable" + exit 1 + + fi + fi +else + host_bin="" + force_host="no" +fi + + + +##################################################################################################### + + +# SANITY checks +# Check default Binaries & Commands are defined +if [ "$reload_dbs" == "yes" ] ; then + if [ -z "$clamd_reload_opt" ] ; then + xshok_pretty_echo_and_log "ERROR: Missing clamd_reload_opt" + exit 1 fi - fi - if [ -z "$gpg_bin" ] ; then - enable_gpg="no" - fi - if [ ! -x "$gpg_bin" ] ; then - enable_gpg="no" - fi fi if [ "$enable_gpg" != "yes" ] ; then - xshok_pretty_echo_and_log "Notice: GnuPG / signature verification disabled" "-" + xshok_pretty_echo_and_log "NOTICE: GnuPG / signature verification disabled" fi # Check default directories are defined if [ -z "$work_dir" ] ; then - xshok_pretty_echo_and_log "ERROR: working directory (work_dir) not defined" "=" + xshok_pretty_echo_and_log "ERROR: working directory (work_dir) not defined" exit 1 fi if [ -z "$clam_dbs" ] ; then - xshok_pretty_echo_and_log "ERROR: clam database directory (clam_dbs) not defined" "=" + xshok_pretty_echo_and_log "ERROR: clam database directory (clam_dbs) not defined" exit 1 fi # Check default directories are writable if [ -e "$work_dir" ] ; then if [ ! -w "$work_dir" ] ; then - xshok_pretty_echo_and_log "ERROR: working directory (work_dir) not writable $work_dir" "=" + xshok_pretty_echo_and_log "ERROR: working directory (work_dir) not writable ${work_dir}" exit 1 fi fi if [ ! -w "$clam_dbs" ] ; then - xshok_pretty_echo_and_log "ERROR: clam database directory (clam_dbs) not writable $clam_dbs" "=" + xshok_pretty_echo_and_log "ERROR: clam database directory (clam_dbs) not writable ${clam_dbs}" exit 1 fi # Reset the update timers to force a full update. if [ "$force_updates" == "yes" ] ; then - xshok_pretty_echo_and_log "Force Updates: enabled" + xshok_pretty_echo_and_log "NOTICE: forcing updates" sanesecurity_update_hours="0" securiteinfo_update_hours="0" + securiteinfo_premium_update_hours="0" linuxmalwaredetect_update_hours="0" + interserver_update_hours="0" + malwareexpert_update_hours="0" malwarepatrol_update_hours="0" yararulesproject_update_hours="0" additional_update_hours="0" @@ -1690,33 +2183,25 @@ if [ "$enable_locking" == "yes" ] ; then pid_file_fullpath="$work_dir_pid/clamav-unofficial-sigs.pid" if [ -f "$pid_file_fullpath" ] ; then pid_file_pid="$(cat "$pid_file_fullpath")" - ps -p "$pid_file_pid" > /dev/null 2>&1 - if [ $? -eq 0 ] ; then - xshok_pretty_echo_and_log "ERROR: Only one instance can run at the same time." "=" + if ps -p "$pid_file_pid" > /dev/null 2>&1 ; then + xshok_pretty_echo_and_log "ERROR: Only one instance can run at the same time." exit 1 else xshok_create_pid_file "$pid_file_fullpath" fi - else - xshok_create_pid_file "$pid_file_fullpath" - fi + else + xshok_create_pid_file "$pid_file_fullpath" + fi # Run this wehen the script exits trap -- "rm -f $pid_file_fullpath" EXIT fi # Verify the clam_user and clam_group actually exists on the system -if ! xshok_user_group_exists "$clam_user" "$clam_group" ; then - xshok_pretty_echo_and_log "ERROR: Either the user: $clam_user and/or group: $clam_group does not exist on the system." "=" +if ! xshok_user_group_exists "${clam_user}" "${clam_group}" ; then + xshok_pretty_echo_and_log "ERROR: Either the user: ${clam_user} and/or group: ${clam_group} does not exist on the system." exit 1 fi -# Silence rsync output and only report errors - useful if script is run via cron. -if [ "$rsync_silence" == "yes" ] ; then - rsync_output_level="--quiet" -else - rsync_output_level="--progress" -fi - # If the local rsync client supports the "--no-motd" flag, then enable it. if $rsync_bin --help | $grep_bin -q "no-motd" > /dev/null ; then no_motd="--no-motd" @@ -1724,20 +2209,59 @@ fi # If the local rsync client supports the "--contimeout" flag, then enable it. if $rsync_bin --help | $grep_bin -q "contimeout" > /dev/null ; then - connect_timeout="--contimeout=$rsync_connect_timeout" + connect_timeout="--contimeout=${rsync_connect_timeout}" +fi + +if [ "$debug" == "yes" ] ; then + downloader_debug="yes" + clamscan_debug="yes" + curl_debug="yes" + wget_debug="yes" + rsync_debug="yes" +fi +# Show clamscan errors +if [ "$clamscan_debug" == "yes" ] ; then + exec 10>&2 +else + exec 10>/dev/null +fi +# Show curl errors +if [ "$curl_debug" == "yes" ] ; then + exec 11>&2 +else + exec 11>/dev/null +fi +# Show wget errors +if [ "$wget_debug" == "yes" ] ; then + exec 12>&2 +else + exec 12>/dev/null +fi +# Show rsync errors +if [ "$rsync_debug" == "yes" ] ; then + exec 13>&2 +else + exec 13>/dev/null fi # Silence wget output and only report errors - useful if script is run via cron. -if [ "$downloader_silence" == "yes" ] ; then - wget_output_level="--quiet" #--quiet +if [ "$downloader_silence" == "yes" ] && [ "$downloader_debug" != "yes" ] ; then + wget_output_level="--quiet" curl_output_level="--silent --show-error" else wget_output_level="--no-verbose" curl_output_level="" fi +# Silence rsync output and only report errors - useful if script is run via cron. +if [ "$rsync_silence" == "yes" ] && [ "$rsync_debug" != "yes" ] ; then + rsync_output_level="--quiet" +else + rsync_output_level="--progress" +fi + # Suppress ssl warnings -if [ "$downloader_ignore_ssl" == "yes" ] ; then +if [ "$downloader_ignore_ssl_errors" == "yes" ] ; then wget_insecure="--no-check-certificate" curl_insecure="--insecure" else @@ -1745,40 +2269,35 @@ else curl_insecure="" fi -# This scripts name and path -this_script_name="$(basename "$0")" -this_script_path="$( cd "$(dirname "$0")" || exit ; pwd -P )" -this_script_full_path="$this_script_path/$this_script_name" - # Set the script to 755 permissions if xshok_is_root ; then if [ "$setmode" == "yes" ] ; then - if [ ! -x "$this_script_path/$this_script_name" ] ; then - chmod 755 "$this_script_path/$this_script_name" - xshok_pretty_echo_and_log "Fixing permission on $this_script_path/$this_script_name" "=" + if [ ! -x "${this_script_path}/${this_script_name}" ] ; then + chmod 755 "${this_script_path}/${this_script_name}" + xshok_pretty_echo_and_log "Fixing permission on ${this_script_path}/${this_script_name}" "=" fi fi else # Disable setmode setmode="no" fi - ################################################################################ # MAIN LOGIC ################################################################################ while true; do - case "$1" in + case "${1}" in -d|--decode-sig) decode_third_party_signature_by_signature_name; exit ;; -e|--encode-string) hexadecimal_encode_entire_input_string; exit ;; -f|--encode-formatted) hexadecimal_encode_formatted_input_string; exit ;; - -g|--gpg-verify) xshok_check_s2 "$2"; gpg_verify_specific_sanesecurity_database_file "$2"; exit ;; + -g|--gpg-verify) xshok_check_s2 "${2}"; gpg_verify_specific_sanesecurity_database_file "${2}"; exit ;; -i|--information) output_system_configuration_information; exit ;; -m|--make-database) make_signature_database_from_ascii_file; exit ;; - -t|--test-database) xshok_check_s2 "$2"; clamscan_integrity_test_specific_database_file "$2"; exit ;; + -t|--test-database) xshok_check_s2 "${2}"; clamscan_integrity_test_specific_database_file "${2}"; exit ;; -o|--output-triggered) output_signatures_triggered_during_ham_directory_scan; exit ;; - -w|--whitelist) add_signature_whitelist_entry; exit ;; + -w|--whitelist) add_signature_whitelist_entry "${2}"; exit ;; --check-clamav) check_clamav; exit ;; + --upgrade) xshok_upgrade; exit ;; --install-all) install_cron; install_logrotate; install_man; exit ;; --install-cron) install_cron; exit ;; --install-logrotate) install_logrotate; exit ;; @@ -1790,164 +2309,492 @@ done xshok_pretty_echo_and_log "Preparing Databases" "=" +if [ "$default_dbs_rating" == "DISABLE" ] ; then + if [ "$sanesecurity_dbs_rating" != "LOW" ] && [ "$sanesecurity_dbs_rating" != "MEDIUM" ] && [ "$sanesecurity_dbs_rating" != "HIGH" ]; then + sanesecurity_enabled="no" + fi + if [ "$linuxmalwaredetect_dbs_rating" != "LOW" ] && [ "$linuxmalwaredetect_dbs_rating" != "MEDIUM" ] && [ "$linuxmalwaredetect_dbs_rating" != "HIGH" ]; then + linuxmalwaredetect_enabled="no" + fi + if [ "$interserver_dbs_rating" != "LOW" ] && [ "$interserver_dbs_rating" != "MEDIUM" ] && [ "$interserver_dbs_rating" != "HIGH" ]; then + interserver_enabled="no" + fi + if [ "$malwareexpert_dbs_rating" != "LOW" ] && [ "$malwareexpert_dbs_rating" != "MEDIUM" ] && [ "$malwareexpert_dbs_rating" != "HIGH" ]; then + malwareexpert_enabled="no" + fi + if [ "$securiteinfo_dbs_rating" != "LOW" ] && [ "$securiteinfo_dbs_rating" != "MEDIUM" ] && [ "$securiteinfo_dbs_rating" != "HIGH" ]; then + securiteinfo_enabled="no" + fi + if [ "$urlhaus_dbs_rating" != "LOW" ] && [ "$urlhaus_dbs_rating" != "MEDIUM" ] && [ "$urlhaus_dbs_rating" != "HIGH" ]; then + urlhaus_enabled="no" + fi + if [ "$yararulesproject_dbs_rating" != "LOW" ] && [ "$yararulesproject_dbs_rating" != "MEDIUM" ] && [ "$yararulesproject_dbs_rating" != "HIGH" ]; then + yararulesproject_enabled="no" + fi +else + if [ "$sanesecurity_dbs_rating" == "DISABLE" ] ; then + sanesecurity_enabled="no" + fi + if [ "$linuxmalwaredetect_dbs_rating" == "DISABLE" ] ; then + linuxmalwaredetect_enabled="no" + fi + if [ "$interserver_dbs_rating" == "DISABLE" ] ; then + interserver_enabled="no" + fi + if [ "$malwareexpert_dbs_rating" == "DISABLE" ] ; then + malwareexpert_enabled="no" + fi + if [ "$securiteinfo_dbs_rating" == "DISABLE" ] ; then + securiteinfo_enabled="no" + fi + if [ "$urlhaus_dbs_rating" == "DISABLE" ] ; then + urlhaus_enabled="no" + fi + if [ "$yararulesproject_dbs_rating" == "DISABLE" ] ; then + yararulesproject_enabled="no" + fi +fi + # Check yararule support is available if [ "$enable_yararules" == "yes" ] ; then current_clamav_version="$($clamscan_bin -V | cut -d " " -f 2 | cut -d "/" -f 1 | awk -F "." '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }')" minimum_yara_clamav_version="$(echo "$minimum_yara_clamav_version" | awk -F "." '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }')" # Check current clamav version against the minimum required version for yara support - if [ "$current_clamav_version" -le "$minimum_yara_clamav_version" ] ; then # Older + if [ "$current_clamav_version" -lt "$minimum_yara_clamav_version" ] ; then # Older yararulesproject_enabled="no" enable_yararules="no" - xshok_pretty_echo_and_log "Notice: Yararules Disabled due to clamav being older than the minimum required version" + xshok_pretty_echo_and_log "Yararules Disabled due to clamav being older than the minimum required version" fi else yararulesproject_enabled="no" enable_yararules="no" fi +############################################################################################ # Generate the signature databases +############################################################################################ if [ "$sanesecurity_enabled" == "yes" ] ; then if [ -n "$sanesecurity_dbs" ] ; then if [ -n "$sanesecurity_dbs_rating" ] ; then temp_db="$(xshok_database "$sanesecurity_dbs_rating" "${sanesecurity_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$sanesecurity_dbs_rating" "${sanesecurity_dbs[@]}")" + fi else temp_db="$(xshok_database "$default_dbs_rating" "${sanesecurity_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$default_dbs_rating" "${sanesecurity_dbs[@]}")" + fi fi sanesecurity_dbs=( ) - sanesecurity_dbs=( $temp_db ) + if [ -n "$temp_db" ] ; then + read -r -a sanesecurity_dbs <<< "$temp_db" + fi fi +elif [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "DISABLED" "${sanesecurity_dbs[@]}")" fi + +sanesecurity_remove_dbs=( ) +if [ -n "$temp_remove_db" ] && [ "$remove_disabled_databases" == "yes" ] ; then + read -r -a sanesecurity_remove_dbs <<< "$temp_remove_db" +fi +############################################################################################ if [ "$securiteinfo_enabled" == "yes" ] ; then if [ -n "$securiteinfo_dbs" ] ; then if [ -n "$securiteinfo_dbs_rating" ] ; then temp_db="$(xshok_database "$securiteinfo_dbs_rating" "${securiteinfo_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$securiteinfo_dbs_rating" "${securiteinfo_dbs[@]}")" + fi else temp_db="$(xshok_database "$default_dbs_rating" "${securiteinfo_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$default_dbs_rating" "${securiteinfo_dbs[@]}")" + fi + fi + securiteinfo_dbs=( ) + if [ -n "$temp_db" ] ; then + read -r -a securiteinfo_dbs <<< "$temp_db" + fi + fi +elif [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "DISABLED" "${securiteinfo_dbs[@]}")" +fi +securiteinfo_remove_dbs=( ) +if [ -n "$temp_remove_db" ] && [ "$remove_disabled_databases" == "yes" ] ; then + read -r -a securiteinfo_remove_dbs <<< "$temp_remove_db" +fi +if [ "$securiteinfo_enabled" == "yes" ] ; then + if [ -n "$securiteinfo_premium_dbs" ] && [ "$securiteinfo_premium" == "yes" ] ; then + if [ -n "$securiteinfo_dbs_rating" ] ; then + temp_db="$(xshok_database "$securiteinfo_dbs_rating" "${securiteinfo_premium_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$securiteinfo_dbs_rating" "${securiteinfo_premium_dbs[@]}")" + fi + else + temp_db="$(xshok_database "$default_dbs_rating" "${securiteinfo_premium_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$default_dbs_rating" "${securiteinfo_premium_dbs[@]}")" + fi + fi + if [ -n "$temp_db" ] ; then + read -r -a securiteinfo_dbs <<< "$temp_db" fi - securiteinfo_dbs=( ) - securiteinfo_dbs=( $temp_db ) fi +elif [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "DISABLED" "${securiteinfo_premium_dbs[@]}")" +fi +if [ -n "$temp_remove_db" ] && [ "$remove_disabled_databases" == "yes" ] ; then + read -r -a securiteinfo_remove_dbs <<< "$temp_remove_db" fi +############################################################################################ if [ "$linuxmalwaredetect_enabled" == "yes" ] ; then if [ -n "$linuxmalwaredetect_dbs" ] ; then if [ -n "$linuxmalwaredetect_dbs_rating" ] ; then temp_db="$(xshok_database "$linuxmalwaredetect_dbs_rating" "${linuxmalwaredetect_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$linuxmalwaredetect_dbs_rating" "${linuxmalwaredetect_dbs[@]}")" + fi else temp_db="$(xshok_database "$default_dbs_rating" "${linuxmalwaredetect_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$default_dbs_rating" "${linuxmalwaredetect_dbs[@]}")" + fi + fi + linuxmalwaredetect_dbs=( ) + if [ -n "$temp_db" ] ; then + read -r -a linuxmalwaredetect_dbs <<< "$temp_db" + fi + fi +elif [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "DISABLED" "${linuxmalwaredetect_dbs[@]}")" +fi +linuxmalwaredetect_remove_dbs=( ) +if [ -n "$temp_remove_db" ] && [ "$remove_disabled_databases" == "yes" ] ; then + read -r -a linuxmalwaredetect_remove_dbs <<< "$temp_remove_db" +fi +############################################################################################ +if [ "$interserver_enabled" == "yes" ] ; then + if [ -n "$interserver_dbs" ] ; then + if [ -n "$interserver_dbs_rating" ] ; then + temp_db="$(xshok_database "$interserver_dbs_rating" "${interserver_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$interserver_dbs_rating" "${interserver_dbs[@]}")" + fi + else + temp_db="$(xshok_database "$default_dbs_rating" "${interserver_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$default_dbs_rating" "${interserver_dbs[@]}")" + fi + fi + interserver_dbs=( ) + if [ -n "$temp_db" ] ; then + read -r -a interserver_dbs <<< "$temp_db" + fi + fi +elif [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "DISABLED" "${interserver_dbs[@]}")" +fi +interserver_remove_dbs=( ) +if [ -n "$temp_remove_db" ] && [ "$remove_disabled_databases" == "yes" ] ; then + read -r -a interserver_remove_dbs <<< "$temp_remove_db" +fi +############################################################################################ +if [ "$malwareexpert_enabled" == "yes" ] ; then + if [ -n "$malwareexpert_dbs" ] ; then + if [ -n "$malwareexpert_dbs_rating" ] ; then + temp_db="$(xshok_database "$malwareexpert_dbs_rating" "${malwareexpert_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$malwareexpert_dbs_rating" "${malwareexpert_dbs[@]}")" + fi + else + temp_db="$(xshok_database "$default_dbs_rating" "${malwareexpert_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$default_dbs_rating" "${malwareexpert_dbs[@]}")" + fi fi - linuxmalwaredetect_dbs=( ) - linuxmalwaredetect_dbs=( $temp_db ) + malwareexpert_dbs=( ) + if [ -n "$temp_db" ] ; then + read -r -a malwareexpert_dbs <<< "$temp_db" + fi fi +elif [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "DISABLED" "${malwareexpert_dbs[@]}")" +fi +malwareexpert_remove_dbs=( ) +if [ -n "$temp_remove_db" ] && [ "$remove_disabled_databases" == "yes" ] ; then + read -r -a malwareexpert_remove_dbs <<< "$temp_remove_db" fi +############################################################################################ if [ "$yararulesproject_enabled" == "yes" ] ; then if [ -n "$yararulesproject_dbs" ] ; then if [ -n "$yararulesproject_dbs_rating" ] ; then temp_db="$(xshok_database "$yararulesproject_dbs_rating" "${yararulesproject_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$yararulesproject_dbs_rating" "${yararulesproject_dbs[@]}")" + fi else temp_db="$(xshok_database "$default_dbs_rating" "${yararulesproject_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$default_dbs_rating" "${yararulesproject_dbs[@]}")" + fi fi yararulesproject_dbs=( ) - yararulesproject_dbs=( $temp_db ) + if [ -n "$temp_db" ] ; then + read -r -a yararulesproject_dbs <<< "$temp_db" + fi fi +elif [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "DISABLED" "${yararulesproject_dbs[@]}")" fi - -# Set the variables for MalwarePatrol -if [ "$malwarepatrol_free" == "yes" ] ; then - malwarepatrol_product_code="8" - malwarepatrol_list="clamav_basic" -else - if [ -z $malwarepatrol_list ] ; then - malwarepatrol_list="clamav_basic" - fi - if [ -z $malwarepatrol_product_code ] ; then - # Not sure, it may be better to return an error. - malwarepatrol_product_code=8 +yararulesproject_remove_dbs=( ) +if [ -n "$temp_remove_db" ] && [ "$remove_disabled_databases" == "yes" ] ; then + read -r -a yararulesproject_remove_dbs <<< "$temp_remove_db" +fi +############################################################################################ +if [ "$urlhaus_enabled" == "yes" ] ; then + if [ -n "$urlhaus_dbs" ] ; then + if [ -n "$urlhaus_dbs_rating" ] ; then + temp_db="$(xshok_database "$urlhaus_dbs_rating" "${urlhaus_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$urlhaus_dbs_rating" "${urlhaus_dbs[@]}")" + fi + else + temp_db="$(xshok_database "$default_dbs_rating" "${urlhaus_dbs[@]}")" + if [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "$default_dbs_rating" "${urlhaus_dbs[@]}")" + fi + fi + urlhaus_dbs=( ) + if [ -n "$temp_db" ] ; then + #urlhaus_dbs=( $temp_db ) + read -r -a urlhaus_dbs <<< "$temp_db" + fi fi +elif [ "$remove_disabled_databases" == "yes" ] ; then + temp_remove_db="$(xshok_remove_database "DISABLED" "${urlhaus_dbs[@]}")" fi -if [ $malwarepatrol_list == "clamav_basic" ] ; then - malwarepatrol_db="malwarepatrol.db" -else - malwarepatrol_db="malwarepatrol.ndb" +urlhaus_remove_dbs=( ) +if [ -n "$temp_remove_db" ] && [ "$remove_disabled_databases" == "yes" ] ; then + read -r -a urlhaus_remove_dbs <<< "$temp_remove_db" +fi +############################################################################################ +if [ "$malwarepatrol_enabled" == "yes" ] ; then + # Set the variables for MalwarePatrol + if [ "$malwarepatrol_product_code" != "8" ] ; then + # assumption, free product code is always 8 (non-free product code is never 8) + malwarepatrol_free="no" + fi + if [ "$malwarepatrol_free" == "yes" ] ; then + malwarepatrol_product_code="8" + malwarepatrol_list="clamav_basic" + else + if [ -z $malwarepatrol_list ] ; then + malwarepatrol_list="clamav_basic" + fi + if [ -z $malwarepatrol_product_code ] ; then + # Not sure, it may be better to return an error. + malwarepatrol_product_code=8 + fi + fi + if [ -z "$malwarepatrol_db" ] ; then + malwarepatrol_db="malwarepatrol.db" + fi + malwarepatrol_url="${malwarepatrol_url}?receipt=${malwarepatrol_receipt_code}&product=${malwarepatrol_product_code}&list=${malwarepatrol_list}" +elif [ "$remove_disabled_databases" == "yes" ] ; then + malwarepatrol_remove_dbs=( "malwarepatrol.db" ) +fi +############################################################################################ +# CLEANUP UNUSED DATABASES, eg when downgrading a database rating or disabling a database +if [ "$remove_disabled_databases" == "yes" ] ; then + if [ -n "${sanesecurity_remove_dbs[0]}" ] ; then + for db_file in "${sanesecurity_remove_dbs[@]}" ; do + if [ -f "${work_dir_sanesecurity}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${work_dir_sanesecurity}/${db_file}" + rm -f "${work_dir_sanesecurity}/${db_file}" + fi + if [ -f "${clam_dbs}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${clam_dbs}/${db_file}" + rm -f "${clam_dbs}/${db_file}" + fi + done + fi + if [ -n "${securiteinfo_remove_dbs[0]}" ] ; then + for db_file in "${securiteinfo_remove_dbs[@]}" ; do + if [ -f "${work_dir_securiteinfo}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${work_dir_securiteinfo}/${db_file}" + rm -f "${work_dir_securiteinfo}/${db_file}" + fi + if [ -f "${clam_dbs}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${clam_dbs}/${db_file}" + rm -f "${clam_dbs}/${db_file}" + fi + done + fi + if [ -n "${linuxmalwaredetect_remove_dbs[0]}" ] ; then + for db_file in "${linuxmalwaredetect_remove_dbs[@]}" ; do + if [ -f "${work_dir_linuxmalwaredetect}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${work_dir_linuxmalwaredetect}/${db_file}" + rm -f "${work_dir_linuxmalwaredetect}/${db_file}" + fi + if [ -f "${clam_dbs}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${clam_dbs}/${db_file}" + rm -f "${clam_dbs}/${db_file}" + fi + done + fi + if [ -n "${interserver_remove_dbs[0]}" ] ; then + for db_file in "${interserver_remove_dbs[@]}" ; do + if [ -f "${work_dir_interserver}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${work_dir_interserver}/${db_file}" + rm -f "${work_dir_interserver}/${db_file}" + fi + if [ -f "${clam_dbs}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${clam_dbs}/${db_file}" + rm -f "${clam_dbs}/${db_file}" + fi + done + fi + if [ -n "${malwareexpert_remove_dbs[0]}" ] ; then + for db_file in "${malwareexpert_remove_dbs[@]}" ; do + if [ -f "${work_dir_malwareexpert}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${work_dir_malwareexpert}/${db_file}" + rm -f "${work_dir_malwareexpert}/${db_file}" + fi + if [ -f "${clam_dbs}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${clam_dbs}/${db_file}" + rm -f "${clam_dbs}/${db_file}" + fi + done + fi + if [ -n "${yararulesproject_remove_dbs[0]}" ] ; then + for db_file in "${yararulesproject_remove_dbs[@]}" ; do + if echo "$db_file" | $grep_bin -q "/" ; then + yr_dir="/$(echo "$db_file" | cut -d "/" -f 1)" + db_file="$(echo "$db_file" | cut -d "/" -f 2)" + else + yr_dir="" + fi + if [ -f "${work_dir_yararulesproject}/${yr_dir}${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${work_dir_yararulesproject}/${db_file}" + rm -f "${work_dir_yararulesproject}/${db_file}" + fi + if [ -f "${clam_dbs}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${clam_dbs}/${db_file}" + rm -f "${clam_dbs}/${db_file}" + fi + done + fi + if [ -n "${urlhaus_remove_dbs[0]}" ] ; then + for db_file in "${urlhaus_remove_dbs[@]}" ; do + if [ -f "${work_dir_urlhaus}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${work_dir_urlhaus}/${db_file}" + rm -f "${work_dir_urlhaus}/${db_file}" + fi + if [ -f "${clam_dbs}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${clam_dbs}/${db_file}" + rm -f "${clam_dbs}/${db_file}" + fi + done + fi + if [ -n "${malwarepatrol_remove_dbs[0]}" ] ; then + for db_file in "${malwarepatrol_remove_dbs[@]}" ; do + if [ -f "${work_dir_malwarepatrol}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${work_dir_malwarepatrol}/${db_file}" + rm -f "${work_dir_malwarepatrol}/${db_file}" + fi + if [ -f "${clam_dbs}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing unused file: ${clam_dbs}/${db_file}" + rm -f "${clam_dbs}/${db_file}" + fi + done + fi fi -malwarepatrol_url="$malwarepatrol_url?product=$malwarepatrol_product_code&list=$malwarepatrol_list" + +############################################################################################ # If "ham_dir" variable is set, then create initial whitelist files (skipped if first-time script run). test_dir="$work_dir/test" if [ -n "$ham_dir" ] && [ -d "$work_dir" ] && [ ! -d "$test_dir" ] ; then if [ -d "$ham_dir" ] ; then xshok_mkdir_ownership "$test_dir" - cp -f "$work_dir"/*/*.ndb "$test_dir" - $clamscan_bin --infected --no-summary -d "$test_dir" "$ham_dir"/* | command sed 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' >> "$work_dir_work_configs/whitelist.txt" - $grep_bin -h -f "$work_dir_work_configs/whitelist.txt" "$test_dir"/* | cut -d "*" -f 2 | sort | uniq > "$work_dir_work_configs/whitelist.hex" + cp -f -p "$work_dir"/*/*.ndb "$test_dir" + cp -f -p "$work_dir"/*/*.db "$test_dir" + $clamscan_bin --infected --no-summary -d "$test_dir" "$ham_dir"/* | command "$sed_bin" 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' >> "${work_dir_work_configs}/whitelist.txt" + $grep_bin -h -f "${work_dir_work_configs}/whitelist.txt" "${test_dir}/*.ndb" | cut -d "*" -f 2 | sort | uniq > "${work_dir_work_configs}/whitelist.hex" + $grep_bin -h -f "${work_dir_work_configs}/whitelist.txt" "${test_dir}/*.db" | cut -d "=" -f 2 | awk '{ printf("=%s\n", $1);}' | sort | uniq >> "${work_dir_work_configs}/whitelist.hex" cd "$test_dir" || exit for db_file in * ; do - [[ -e $db_file ]] || break # Handle the case of no files - $grep_bin -h -v -f "$work_dir_work_configs/whitelist.hex" "$db_file" > "$db_file-tmp" + [[ -e ${db_file} ]] || break # Handle the case of no files + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "$db_file" > "$db_file-tmp" mv -f "$db_file-tmp" "$db_file" - if $clamscan_bin --quiet -d "$db_file" "$work_dir_work_configs/scan-test.txt" 2>/dev/null ; then + if $clamscan_bin --quiet -d "$db_file" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then if $rsync_bin -pcqt "$db_file" "$clam_dbs" ; then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/$db_file" + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/$db_file" + restorecon "${clam_dbs}/${db_file}" fi do_clamd_reload=1 fi fi done - if [ -r "$work_dir_work_configs/whitelist.hex" ] ; then - xshok_pretty_echo_and_log "Initial HAM directory scan whitelist file created in $work_dir_work_configs" + if [ -r "${work_dir_work_configs}/whitelist.hex" ] ; then + xshok_pretty_echo_and_log "Initial HAM directory scan whitelist file created in ${work_dir_work_configs}" else xshok_pretty_echo_and_log "No false-positives detected in initial HAM directory scan" fi else - xshok_pretty_echo_and_log "WARNING: Cannot locate HAM directory: $ham_dir" + xshok_pretty_echo_and_log "WARNING: Cannot locate HAM directory: ${ham_dir}" xshok_pretty_echo_and_log "Skipping initial whitelist file creation. Fix 'ham_dir' path in config file" fi fi # Check to see if the working directories have been created. If not, create them. Otherwise, ignore and proceed with script. xshok_mkdir_ownership "$work_dir" -xshok_mkdir_ownership "$work_dir_securiteinfo" -xshok_mkdir_ownership "$work_dir_malwarepatrol" +xshok_mkdir_ownership "$work_dir_gpg" +xshok_mkdir_ownership "$work_dir_add" +xshok_mkdir_ownership "$work_dir_pid" +xshok_mkdir_ownership "$work_dir_interserver" xshok_mkdir_ownership "$work_dir_linuxmalwaredetect" +xshok_mkdir_ownership "$work_dir_malwareexpert" +xshok_mkdir_ownership "$work_dir_malwarepatrol" xshok_mkdir_ownership "$work_dir_sanesecurity" -xshok_mkdir_ownership "$work_dir_yararulesproject" +xshok_mkdir_ownership "$work_dir_securiteinfo" xshok_mkdir_ownership "$work_dir_work_configs" -xshok_mkdir_ownership "$work_dir_gpg" -xshok_mkdir_ownership "$work_dir_add" +xshok_mkdir_ownership "$work_dir_yararulesproject" # Set secured access permissions to the GPG directory -perms chmod -f 0700 "$work_dir_gpg" +perms chmod -f 0700 "${work_dir_gpg}" if [ "$enable_gpg" == "yes" ] ; then # If we haven't done so yet, download Sanesecurity public GPG key and import to custom keyring. - if [ ! -s "$work_dir_gpg/publickey.gpg" ] ; then - xshok_file_download "$work_dir_gpg/publickey.gpg" "$sanesecurity_gpg_url" + if [ ! -s "${work_dir_gpg}/publickey.gpg" ] ; then + xshok_file_download "${work_dir_gpg}/publickey.gpg" "$sanesecurity_gpg_url" ret="$?" if [ "$ret" -ne 0 ] ; then - xshok_pretty_echo_and_log "ALERT: Could not download Sanesecurity public GPG key" "*" + xshok_pretty_echo_and_log "ALERT: Could not download Sanesecurity public GPG key" exit 1 else xshok_pretty_echo_and_log "Sanesecurity public GPG key successfully downloaded" - rm -f -- "$work_dir_gpg/ss-keyring.gp*" - if ! $gpg_bin -q --no-options --no-default-keyring --homedir "$work_dir_gpg" --keyring "$work_dir_gpg/ss-keyring.gpg" --import "$work_dir_gpg/publickey.gpg" 2>/dev/null ; then - xshok_pretty_echo_and_log "ALERT: could not import Sanesecurity public GPG key to custom keyring" "*" + rm -f -- "${work_dir_gpg}/ss-keyring.gp*" + if ! $gpg_bin -q --no-options --no-default-keyring --homedir "${work_dir_gpg}" --keyring "${work_dir_gpg}/ss-keyring.gpg" --import "${work_dir_gpg}/publickey.gpg" 2>/dev/null ; then + xshok_pretty_echo_and_log "ALERT: could not import Sanesecurity public GPG key to custom keyring" exit 1 else - chmod -f 0644 "$work_dir_gpg/*.*" + chmod -f 0644 "${work_dir_gpg}/*.*" xshok_pretty_echo_and_log "Sanesecurity public GPG key successfully imported to custom keyring" fi fi fi - # If custom keyring is missing, try to re-import Sanesecurity public GPG key. - if [ ! -s "$work_dir_gpg/ss-keyring.gpg" ] ; then - rm -f -- "$work_dir_gpg/ss-keyring.gp*" - if ! $gpg_bin -q --no-options --no-default-keyring --homedir "$work_dir_gpg" --keyring "$work_dir_gpg/ss-keyring.gpg" --import "$work_dir_gpg/publickey.gpg" 2>/dev/null ; then - xshok_pretty_echo_and_log "ALERT: Custom keyring MISSING or CORRUPT! Could not import Sanesecurity public GPG key to custom keyring" "*" + if [ ! -s "${work_dir_gpg}/ss-keyring.gpg" ] ; then + rm -f -- "${work_dir_gpg}/ss-keyring.gp*" + if ! $gpg_bin -q --no-options --no-default-keyring --homedir "${work_dir_gpg}" --keyring "${work_dir_gpg}/ss-keyring.gpg" --import "${work_dir_gpg}/publickey.gpg" 2>/dev/null ; then + xshok_pretty_echo_and_log "ALERT: Custom keyring MISSING or CORRUPT! Could not import Sanesecurity public GPG key to custom keyring" exit 1 else - chmod -f 0644 "$work_dir_gpg/*.*" + chmod -f 0644 "${work_dir_gpg}/*.*" xshok_pretty_echo_and_log "Sanesecurity custom keyring MISSING! GPG key successfully re-imported to custom keyring" fi fi @@ -1972,8 +2819,12 @@ if [ "$enable_random" == "yes" ] ; then fi # Create "scan-test.txt" file for clamscan database integrity testing. -if [ ! -s "$work_dir_work_configs/scan-test.txt" ] ; then - echo "This is the clamscan test file..." > "$work_dir_work_configs/scan-test.txt" +if [ ! -s "${work_dir_work_configs}/scan-test.txt" ] ; then + echo "This is the clamscan test file..." > "${work_dir_work_configs}/scan-test.txt" +fi + +if [ -z "$git_branch" ] ; then + git_branch="master" fi # If rsync proxy is defined in the config file, then export it for use. @@ -1982,22 +2833,28 @@ if [ -n "$rsync_proxy" ] ; then export RSYNC_PROXY fi +# If rsync connect program is defined in the config file, then export it for use. (to use netcat for socks tunnel) +if [ -n "$rsync_connect_prog" ] ; then + RSYNC_CONNECT_PROG="$rsync_connect_prog" + export RSYNC_CONNECT_PROG +fi + # Create $current_dbsfiles containing lists of current and previously active 3rd-party databases # so that databases and/or backup files that are no longer being used can be removed. -current_tmp="$work_dir_work_configs/current-dbs.tmp" +current_tmp="${work_dir_work_configs}/current-dbs.tmp" -current_dbs_file="$work_dir_work_configs/current-dbs.txt" +current_dbs_file="${work_dir_work_configs}/current-dbs.txt" if [ "$sanesecurity_enabled" == "yes" ] ; then - # Create the Sanesecurity rsync "include" file (defines which files to download). - sanesecurity_include_dbs="$work_dir_work_configs/ss-include-dbs.txt" + # Create the Sanesecurity rsync "include" file (defines command -v files to download). + sanesecurity_include_dbs="${work_dir_work_configs}/ss-include-dbs.txt" if [ -n "${sanesecurity_dbs[0]}" ] ; then - rm -f -- "$sanesecurity_include_dbs" "$work_dir_sanesecurity/*.sha256" + rm -f -- "${sanesecurity_include_dbs}" "${work_dir_sanesecurity}/*.sha256" for db_file in "${sanesecurity_dbs[@]}" ; do - echo "$db_file" >> "$sanesecurity_include_dbs" - echo "$db_file.sig" >> "$sanesecurity_include_dbs" - echo "$work_dir_sanesecurity/$db_file" >> "$current_tmp" - echo "$work_dir_sanesecurity/$db_file.sig" >> "$current_tmp" + echo "$db_file" >> "${sanesecurity_include_dbs}" + echo "${db_file}.sig" >> "${sanesecurity_include_dbs}" + echo "${work_dir_sanesecurity}/${db_file}" >> "${current_tmp}" + echo "${work_dir_sanesecurity}/${db_file}.sig" >> "${current_tmp}" clamav_files done fi @@ -2005,7 +2862,7 @@ fi if [ "$securiteinfo_enabled" == "yes" ] ; then if [ -n "${securiteinfo_dbs[0]}" ] ; then for db in "${securiteinfo_dbs[@]}" ; do - echo "$work_dir_securiteinfo/$db" >> "$current_tmp" + echo "${work_dir_securiteinfo}/${db}" >> "${current_tmp}" clamav_files done fi @@ -2013,24 +2870,40 @@ fi if [ "$linuxmalwaredetect_enabled" == "yes" ] ; then if [ -n "${linuxmalwaredetect_dbs[0]}" ] ; then for db in "${linuxmalwaredetect_dbs[@]}" ; do - echo "$work_dir_linuxmalwaredetect/$db" >> "$current_tmp" + echo "${work_dir_linuxmalwaredetect}/${db}" >> "${current_tmp}" + clamav_files + done + fi +fi +if [ "$interserver_enabled" == "yes" ] ; then + if [ -n "${interserver_dbs[0]}" ] ; then + for db in "${interserver_dbs[@]}" ; do + echo "${work_dir_interserver}/${db}" >> "${current_tmp}" + clamav_files + done + fi +fi +if [ "$malwareexpert_enabled" == "yes" ] ; then + if [ -n "${malwareexpert_dbs[0]}" ] ; then + for db in "${malwareexpert_dbs[@]}" ; do + echo "${work_dir_malwareexpert}/${db}" >> "${current_tmp}" clamav_files done fi fi if [ "$malwarepatrol_enabled" == "yes" ] ; then if [ -n "$malwarepatrol_db" ] ; then - echo "$work_dir_malwarepatrol/$malwarepatrol_db" >> "$current_tmp" + echo "${work_dir_malwarepatrol}/${malwarepatrol_db}" >> "${current_tmp}" clamav_files fi fi if [ "$yararulesproject_enabled" == "yes" ] ; then if [ -n "${yararulesproject_dbs[0]}" ] ; then for db in "${yararulesproject_dbs[@]}" ; do - if echo "$db" | $grep_bin -q "/"; then + if echo "$db" | $grep_bin -q "/" ; then db="$(echo "$db" | cut -d "/" -f 2)" fi - echo "$work_dir_yararulesproject/$db" >> "$current_tmp" + echo "${work_dir_yararulesproject}/${db}" >> "${current_tmp}" clamav_files done fi @@ -2038,24 +2911,24 @@ fi if [ "$additional_enabled" == "yes" ] ; then if [ -n "$additional_dbs" ] ; then for db in "${additional_dbs[@]}" ; do - echo "$work_dir_add/$db" >> "$current_tmp" + echo "${work_dir_add}/${db}" >> "${current_tmp}" clamav_files done fi fi -sort "$current_tmp" > "$current_dbs_file" 2>/dev/null -rm -f "$current_tmp" +sort "${current_tmp}" > "$current_dbs_file" 2>/dev/null +rm -f "${current_tmp}" # Remove 3rd-party databases and/or backup files that are no longer being used. if [ "$remove_disabled_databases" == "yes" ] ; then - previous_dbs="$work_dir_work_configs/previous-dbs.txt" + previous_dbs="${work_dir_work_configs}/previous-dbs.txt" sort "$current_dbs_file" > "$previous_dbs" 2>/dev/null # Do not remove the current_dbs_file #rm -f "$current_dbs_file" - db_changes="$work_dir_work_configs/db-changes.txt" + db_changes="${work_dir_work_configs}/db-changes.txt" if [ ! -s "$previous_dbs" ] ; then - cp -f "$current_dbs_file" "$previous_dbs" 2>/dev/null + cp -f -p "$current_dbs_file" "$previous_dbs" 2>/dev/null fi diff "$current_dbs_file" "$previous_dbs" 2>/dev/null | $grep_bin ">" | awk '{print $2}' > "$db_changes" if [ -r "$db_changes" ] ; then @@ -2064,33 +2937,33 @@ if [ "$remove_disabled_databases" == "yes" ] ; then fi while read -r file ; do rm -f -- "$file" - xshok_pretty_echo_and_log "Unused/Disabled file removed: $file" + xshok_pretty_echo_and_log "Unused/Disabled file removed: ${file}" done < "$db_changes" fi fi # Create "purge.txt" file for package maintainers to support package uninstall. -purge="$work_dir_work_configs/purge.txt" -cp -f "$current_dbs_file" "$purge" +purge="${work_dir_work_configs}/purge.txt" +cp -f -p "$current_dbs_file" "$purge" { - echo "$work_dir_work_configs/current-dbs.txt" - echo "$work_dir_work_configs/db-changes.txt" - echo "$work_dir_work_configs/last-mbl-update.txt" - echo "$work_dir_work_configs/last-si-update.txt" - echo "$work_dir_work_configs/local.ign" - echo "$work_dir_work_configs/monitor-ign.txt" - echo "$work_dir_work_configs/my-whitelist.ign2" - echo "$work_dir_work_configs/tracker.txt" - echo "$work_dir_work_configs/previous-dbs.txt" - echo "$work_dir_work_configs/scan-test.txt" - echo "$work_dir_work_configs/ss-include-dbs.txt" - echo "$work_dir_work_configs/whitelist.hex" - echo "$work_dir_gpg/publickey.gpg" + echo "${work_dir_work_configs}/current-dbs.txt" + echo "${work_dir_work_configs}/db-changes.txt" + echo "${work_dir_work_configs}/last-mbl-update.txt" + echo "${work_dir_work_configs}/last-si-update.txt" + echo "${work_dir_work_configs}/local.ign" + echo "${work_dir_work_configs}/monitor-ign.txt" + echo "${work_dir_work_configs}/my-whitelist.ign2" + echo "${work_dir_work_configs}/tracker.txt" + echo "${work_dir_work_configs}/previous-dbs.txt" + echo "${work_dir_work_configs}/scan-test.txt" + echo "${work_dir_work_configs}/ss-include-dbs.txt" + echo "${work_dir_work_configs}/whitelist.hex" + echo "${work_dir_gpg}/publickey.gpg" echo "$work_dir_gpg/secring.gpg" - echo "$work_dir_gpg/ss-keyring.gpg*" + echo "${work_dir_gpg}/ss-keyring.gpg*" echo "$work_dir_gpg/trustdb.gpg" - echo "$log_file_path/$log_file_name*" - echo "$work_dir_work_configs/purge.txt" + echo "${log_file_path}/${log_file_name}*" + echo "${work_dir_work_configs}/purge.txt" } >> "$purge" # Check and save current system time since epoch for time related database downloads. @@ -2103,23 +2976,21 @@ if [ -n "${securiteinfo_dbs[0]}" ] || [ -n "$malwarepatrol_db" ] ; then current_time="$(perl -le print+time 2> /dev/null)" fi if [ "$current_time" -le 0 ] ; then - xshok_pretty_echo_and_log "WARNING: No support for 'date +%s' or 'perl' was not found , SecuriteInfo and MalwarePatrol updates bypassed" "=" + xshok_pretty_echo_and_log "WARNING: No support for 'date +%s' or 'perl' was not found , SecuriteInfo and MalwarePatrol updates bypassed" securiteinfo_dbs=() malwarepatrol_db=() fi fi - ################################################################ # Check for Sanesecurity database & GPG signature file updates # ################################################################ - if [ "$sanesecurity_enabled" == "yes" ] ; then if [ -n "${sanesecurity_dbs[0]}" ] ; then if [ ${#sanesecurity_dbs} -lt 1 ] ; then xshok_pretty_echo_and_log "Failed sanesecurity_dbs config is invalid or not defined - SKIPPING" else - if [ -r "$work_dir_work_configs/last-ss-update.txt" ] ; then - last_sanesecurity_update="$(cat "$work_dir_work_configs/last-ss-update.txt")" + if [ -r "${work_dir_work_configs}/last-ss-update.txt" ] ; then + last_sanesecurity_update="$(cat "${work_dir_work_configs}/last-ss-update.txt")" else last_sanesecurity_update="0" fi @@ -2127,111 +2998,134 @@ if [ "$sanesecurity_enabled" == "yes" ] ; then update_interval="$((sanesecurity_update_hours * 3600))" time_interval="$((current_time - last_sanesecurity_update))" if [ "$time_interval" -ge $((update_interval - 600)) ] ; then - echo "$current_time" > "$work_dir_work_configs/last-ss-update.txt" + echo "$current_time" > "${work_dir_work_configs}/last-ss-update.txt" xshok_pretty_echo_and_log "Sanesecurity Database & GPG Signature File Updates" "=" xshok_pretty_echo_and_log "Checking for Sanesecurity updates..." - - sanesecurity_mirror_ips="$(dig +ignore +short "$sanesecurity_url")" - # Add fallback to host if dig returns no records + if [ -n "$dig_bin" ] ; then + # shellcheck disable=SC2086 + sanesecurity_mirror_ips="$($dig_bin $dig_proxy +ignore +short "$sanesecurity_url")" + else + # shellcheck disable=SC2086 + sanesecurity_mirror_ips="$($host_bin $host_proxy -t A "$sanesecurity_url" | $sed_bin -n '/has address/{s/.*address \([^ ]*\).*/\1/;p;}')" + fi + # Add fallback if no records are returned if [ ${#sanesecurity_mirror_ips} -lt 1 ] ; then - sanesecurity_mirror_ips="$(host -t A "$sanesecurity_url" | sed -n '/has address/{s/.*address \([^ ]*\).*/\1/;p;}')" + if [ -n "$dig_bin" ] ; then + # shellcheck disable=SC2086 + sanesecurity_mirror_ips="$($dig_bin $dig_proxy +ignore +short "$sanesecurity_url")" + else + # shellcheck disable=SC2086 + sanesecurity_mirror_ips="$($host_bin $host_proxy -t A "$sanesecurity_url" | $sed_bin -n '/has address/{s/.*address \([^ ]*\).*/\1/;p;}')" + fi fi if [ ${#sanesecurity_mirror_ips} -ge 1 ] ; then for sanesecurity_mirror_ip in $sanesecurity_mirror_ips ; do - sanesecurity_mirror_name="" - sanesecurity_mirror_name="$(dig +short -x "$sanesecurity_mirror_ip" | command sed 's/\.$//')" - # Add fallback to host if dig returns no records + if [ -n "$dig_bin" ] ; then + # shellcheck disable=SC2086 + sanesecurity_mirror_name="$($dig_bin $dig_proxy +short -x "$sanesecurity_mirror_ip" | command "$sed_bin" 's/\.$//')" + else + # shellcheck disable=SC2086 + sanesecurity_mirror_name="$($host_bin $host_proxy -t A "$sanesecurity_mirror_ip" | $sed_bin -n '/name pointer/{s/.*pointer \([^ ]*\).*\.$/\1/;p;}')" + fi + # Add fallback if no records are returned if [ -z "$sanesecurity_mirror_name" ] ; then - sanesecurity_mirror_name="$(host "$sanesecurity_mirror_ip" | sed -n '/name pointer/{s/.*pointer \([^ ]*\).*\.$/\1/;p;}')" + if [ -n "$dig_bin" ] ; then + # shellcheck disable=SC2086 + sanesecurity_mirror_name="$($dig_bin $dig_proxy +short -x "$sanesecurity_mirror_ip" | command "$sed_bin" 's/\.$//')" + else + # shellcheck disable=SC2086 + sanesecurity_mirror_name="$($host_bin $host_proxy -t A "$sanesecurity_mirror_ip" | $sed_bin -n '/name pointer/{s/.*pointer \([^ ]*\).*\.$/\1/;p;}')" + fi fi sanesecurity_mirror_site_info="$sanesecurity_mirror_name $sanesecurity_mirror_ip" - xshok_pretty_echo_and_log "Sanesecurity mirror site used: $sanesecurity_mirror_site_info" + xshok_pretty_echo_and_log "Sanesecurity mirror site used: ${sanesecurity_mirror_site_info}" # shellcheck disable=SC2086 - $rsync_bin $rsync_output_level $no_motd --files-from="$sanesecurity_include_dbs" -ctuz $connect_timeout --timeout="$rsync_max_time" "rsync://$sanesecurity_mirror_ip/sanesecurity" "$work_dir_sanesecurity" 2>/dev/null + $rsync_bin $rsync_output_level $no_motd --files-from="${sanesecurity_include_dbs}" -ctuz $connect_timeout --timeout="$rsync_max_time" "rsync://${sanesecurity_mirror_ip}/sanesecurity" "$work_dir_sanesecurity" 2>&13 ret="$?" if [ "$ret" -eq 0 ] || [ "$ret" -eq 23 ] ; then # The correct way, 23 is some files were not transfered, can be ignored and we can assume a success sanesecurity_rsync_success="1" for db_file in "${sanesecurity_dbs[@]}" ; do - if ! cmp -s "$work_dir_sanesecurity/$db_file" "$clam_dbs/$db_file" ; then - xshok_pretty_echo_and_log "Testing updated Sanesecurity database file: $db_file" + if ! cmp -s "${work_dir_sanesecurity}/${db_file}" "${clam_dbs}/${db_file}" ; then + xshok_pretty_echo_and_log "Testing updated Sanesecurity database file: ${db_file}" if [ "$enable_gpg" == "yes" ] ; then - if ! $gpg_bin --trust-model always -q --no-default-keyring --homedir "$work_dir_gpg" --keyring "$work_dir_gpg/ss-keyring.gpg" --verify "$work_dir_sanesecurity/$db_file.sig" "$work_dir_sanesecurity/$db_file" 2>/dev/null ; then - $gpg_bin --always-trust -q --no-default-keyring --homedir "$work_dir_gpg" --keyring "$work_dir_gpg/ss-keyring.gpg" --verify "$work_dir_sanesecurity/$db_file.sig" "$work_dir_sanesecurity/$db_file" 2>/dev/null + if ! $gpg_bin --trust-model always -q --no-default-keyring --homedir "${work_dir_gpg}" --keyring "${work_dir_gpg}/ss-keyring.gpg" --verify "${work_dir_sanesecurity}/${db_file}.sig" "${work_dir_sanesecurity}/${db_file}" 2>/dev/null ; then + $gpg_bin --always-trust -q --no-default-keyring --homedir "${work_dir_gpg}" --keyring "${work_dir_gpg}/ss-keyring.gpg" --verify "${work_dir_sanesecurity}/${db_file}.sig" "${work_dir_sanesecurity}/${db_file}" 2>/dev/null ret="$?" else ret="0" fi if [ "$ret" -eq 0 ] ; then - test "$gpg_silence" = "no" && xshok_pretty_echo_and_log "Sanesecurity GPG Signature tested good on $db_file database" + test "$gpg_silence" = "no" && xshok_pretty_echo_and_log "Sanesecurity GPG Signature tested good on ${db_file} database" else - xshok_pretty_echo_and_log "Sanesecurity GPG Signature test FAILED on $db_file database - SKIPPING" + xshok_pretty_echo_and_log "Sanesecurity GPG Signature test FAILED on ${db_file} database - SKIPPING" fi fi if [ "$ret" -eq 0 ] ; then db_ext="${db_file#*.}" if [ -z "$ham_dir" ] || [ "$db_ext" != "ndb" ] ; then - if $clamscan_bin --quiet -d "$work_dir_sanesecurity/$db_file" "$work_dir_work_configs/scan-test.txt" 2>/dev/null ; then - xshok_pretty_echo_and_log "Clamscan reports Sanesecurity $db_file database integrity tested good" + if $clamscan_bin --quiet -d "${work_dir_sanesecurity}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports Sanesecurity ${db_file} database integrity tested good" true else - xshok_pretty_echo_and_log "Clamscan reports Sanesecurity $db_file database integrity tested BAD" + xshok_pretty_echo_and_log "Clamscan reports Sanesecurity ${db_file} database integrity tested BAD" if [ "$remove_bad_database" == "yes" ] ; then - if rm -f "$work_dir_sanesecurity/$db_file" ; then - xshok_pretty_echo_and_log "Removed invalid database: $work_dir_sanesecurity/$db_file" + if rm -f "${work_dir_sanesecurity}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_sanesecurity}/${db_file}" fi fi false - fi && (test "$keep_db_backup" = "yes" && cp -f "$clam_dbs/$db_file" "$clam_dbs/$db_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "$work_dir_sanesecurity/$db_file" "$clam_dbs" 2>/dev/null ; then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/$db_file" + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${work_dir_sanesecurity}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/$db_file" + restorecon "${clam_dbs}/${db_file}" fi - xshok_pretty_echo_and_log "Successfully updated Sanesecurity production database file: $db_file" + xshok_pretty_echo_and_log "Successfully updated Sanesecurity production database file: ${db_file}" sanesecurity_update=1 do_clamd_reload=1 else - xshok_pretty_echo_and_log "Failed to successfully update Sanesecurity production database file: $db_file - SKIPPING" + xshok_pretty_echo_and_log "Failed to successfully update Sanesecurity production database file: ${db_file} - SKIPPING" false fi else - $grep_bin -h -v -f "$work_dir_work_configs/whitelist.hex" "$work_dir_sanesecurity/$db_file" > "$test_dir/$db_file" - $clamscan_bin --infected --no-summary -d "$test_dir/$db_file" "$ham_dir"/* | command sed 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "$work_dir_work_configs/whitelist.txt" - $grep_bin -h -f "$work_dir_work_configs/whitelist.txt" "$test_dir/$db_file" | cut -d "*" -f 2 | sort | uniq >> "$work_dir_work_configs/whitelist.hex" - $grep_bin -h -v -f "$work_dir_work_configs/whitelist.hex" "$test_dir/$db_file" > "$test_dir/$db_file-tmp" - mv -f "$test_dir/$db_file-tmp" "$test_dir/$db_file" - if $clamscan_bin --quiet -d "$test_dir/$db_file" "$work_dir_work_configs/scan-test.txt" 2>/dev/null ; then - xshok_pretty_echo_and_log "Clamscan reports Sanesecurity $db_file database integrity tested good" + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${work_dir_sanesecurity}/${db_file}" > "${test_dir}/${db_file}" + $clamscan_bin --infected --no-summary -d "${test_dir}/${db_file}" "$ham_dir"/* | command "$sed_bin" 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "${work_dir_work_configs}/whitelist.txt" + $grep_bin -h -f "${work_dir_work_configs}/whitelist.hex" "${test_dir}/${db_file}" | cut -d "*" -f 2 | sort | uniq >> "${work_dir_work_configs}/whitelist.hex-tmp" + mv -f "${work_dir_work_configs}/whitelist.hex-tmp" "${work_dir_work_configs}/whitelist.hex" + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${test_dir}/${db_file}" > "${test_dir}/${db_file}-tmp" + mv -f "${test_dir}/${db_file}-tmp" "${test_dir}/${db_file}" + if $clamscan_bin --quiet -d "${test_dir}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports Sanesecurity ${db_file} database integrity tested good" true else - xshok_pretty_echo_and_log "Clamscan reports Sanesecurity $db_file database integrity tested BAD" + xshok_pretty_echo_and_log "Clamscan reports Sanesecurity ${db_file} database integrity tested BAD" # DO NOT KILL THIS DB false - fi && (test "$keep_db_backup" = "yes" && cp -f "$clam_dbs/$db_file" "$clam_dbs/$db_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "$test_dir/$db_file" "$clam_dbs" 2>/dev/null ; then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/$db_file" + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${test_dir}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/$db_file" + restorecon "${clam_dbs}/${db_file}" fi - xshok_pretty_echo_and_log "Successfully updated Sanesecurity production database file: $db_file" + xshok_pretty_echo_and_log "Successfully updated Sanesecurity production database file: ${db_file}" sanesecurity_update=1 do_clamd_reload=1 else - xshok_pretty_echo_and_log "Failed to successfully update Sanesecurity production database file: $db_file - SKIPPING" + xshok_pretty_echo_and_log "Failed to successfully update Sanesecurity production database file: ${db_file} - SKIPPING" fi fi fi fi done if [ ! "$sanesecurity_update" == "1" ] ; then - xshok_pretty_echo_and_log "No Sanesecurity database file updates found" "-" + xshok_pretty_echo_and_log "No Sanesecurity database file updates" "-" break else break fi else - xshok_pretty_echo_and_log "Connection to $sanesecurity_mirror_site_info failed - Trying next mirror site..." + xshok_pretty_echo_and_log "Connection to ${sanesecurity_mirror_site_info} failed - Trying next mirror site..." fi done if [ ! "$sanesecurity_rsync_success" == "1" ] ; then @@ -2243,7 +3137,7 @@ if [ "$sanesecurity_enabled" == "yes" ] ; then fi else xshok_pretty_echo_and_log "Sanesecurity Database File Updates" "=" - xshok_draw_time_remaining "$((update_interval - time_interval))" "$sanesecurity_update_hours" "sanesecurity" + xshok_draw_time_remaining "$((update_interval - time_interval))" "$sanesecurity_update_hours" "Sanesecurity" fi fi fi @@ -2252,17 +3146,17 @@ else if [ "$remove_disabled_databases" == "yes" ] ; then xshok_pretty_echo_and_log "Removing disabled Sanesecurity Database files" for db_file in "${sanesecurity_dbs[@]}" ; do - if echo "$db_file" | $grep_bin -q "|"; then + if echo "$db_file" | $grep_bin -q "|" ; then db_file="${db_file%|*}" fi - if [ -r "$work_dir_sanesecurity/$db_file" ] ; then - xshok_pretty_echo_and_log "Removing $work_dir_sanesecurity/$db_file" - rm -f "$work_dir_sanesecurity/$db_file" + if [ -r "${work_dir_sanesecurity}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing ${work_dir_sanesecurity}/${db_file}" + rm -f "${work_dir_sanesecurity}/${db_file}" do_clamd_reload=1 fi - if [ -r "$clam_dbs/$db_file" ] ; then - xshok_pretty_echo_and_log "Removing $clam_dbs/$db_file" - rm -f "$clam_dbs/$db_file" + if [ -r "${clam_dbs}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing ${clam_dbs}/${db_file}" + rm -f "${clam_dbs}/${db_file}" do_clamd_reload=1 fi done @@ -2275,22 +3169,26 @@ fi ############################################################################################################################################## if [ "$securiteinfo_enabled" == "yes" ] ; then if [ "$securiteinfo_authorisation_signature" != "YOUR-SIGNATURE-NUMBER" ] ; then - if [ -n "$securiteinfo_dbs" ] ; then + if [ -n "${securiteinfo_dbs[0]}" ] ; then if [ ${#securiteinfo_dbs} -lt 1 ] ; then xshok_pretty_echo_and_log "Failed securiteinfo_dbs config is invalid or not defined - SKIPPING" else - rm -f "$work_dir_securiteinfo/*.gz" - if [ -r "$work_dir_work_configs/last-si-update.txt" ] ; then - last_securiteinfo_update="$(cat "$work_dir_work_configs/last-si-update.txt")" + rm -f "${work_dir_securiteinfo}/*.gz" + if [ -r "${work_dir_work_configs}/last-si-update.txt" ] ; then + last_securiteinfo_update="$(cat "${work_dir_work_configs}/last-si-update.txt")" else last_securiteinfo_update="0" fi db_file="" loop="" - update_interval="$((securiteinfo_update_hours * 3600))" + if [ "$securiteinfo_premium" == "yes" ] ; then + update_interval="$((securiteinfo_premium_update_hours * 3600))" + else + update_interval="$((securiteinfo_update_hours * 3600))" + fi time_interval="$((current_time - last_securiteinfo_update))" if [ "$time_interval" -ge "$((update_interval - 600))" ] ; then - echo "$current_time" > "$work_dir_work_configs/last-si-update.txt" + echo "$current_time" > "${work_dir_work_configs}/last-si-update.txt" xshok_pretty_echo_and_log "SecuriteInfo Database File Updates" "=" xshok_pretty_echo_and_log "Checking for SecuriteInfo updates..." securiteinfo_updates="0" @@ -2298,90 +3196,89 @@ if [ "$securiteinfo_enabled" == "yes" ] ; then if [ "$loop" == "1" ] ; then xshok_pretty_echo_and_log "---" fi - xshok_pretty_echo_and_log "Checking for updated SecuriteInfo database file: $db_file" + xshok_pretty_echo_and_log "Checking for updated SecuriteInfo database file: ${db_file}" securiteinfo_db_update="0" - xshok_file_download "$work_dir_securiteinfo/$db_file" "$securiteinfo_url/$securiteinfo_authorisation_signature/$db_file" + xshok_file_download "${work_dir_securiteinfo}/${db_file}" "${securiteinfo_url}/${securiteinfo_authorisation_signature}/${db_file}" ret="$?" if [ "$ret" -eq 0 ] ; then loop="1" - if ! cmp -s "$work_dir_securiteinfo/$db_file" "$clam_dbs/$db_file" ; then - if [ $? -eq 0 ] ; then - db_ext="${db_file#*.}" - - xshok_pretty_echo_and_log "Testing updated SecuriteInfo database file: $db_file" - if [ -z "$ham_dir" ] || [ "$db_ext" != "ndb" ] - then - if $clamscan_bin --quiet -d "$work_dir_securiteinfo/$db_file" "$work_dir_work_configs/scan-test.txt" 2>/dev/null - then - xshok_pretty_echo_and_log "Clamscan reports SecuriteInfo $db_file database integrity tested good" - true - else - xshok_pretty_echo_and_log "Clamscan reports SecuriteInfo $db_file database integrity tested BAD" - if [ "$remove_bad_database" == "yes" ] ; then - if rm -f "$work_dir_securiteinfo/$db_file" ; then - xshok_pretty_echo_and_log "Removed invalid database: $work_dir_securiteinfo/$db_file" - fi - fi - false - fi && (test "$keep_db_backup" = "yes" && cp -f "$clam_dbs/$db_file" "$clam_dbs/$db_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "$work_dir_securiteinfo/$db_file" "$clam_dbs" 2>/dev/null ; then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/$db_file" - if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/$db_file" + if ! cmp -s "${work_dir_securiteinfo}/${db_file}" "${clam_dbs}/${db_file}" ; then + db_ext="${db_file#*.}" + + xshok_pretty_echo_and_log "Testing updated SecuriteInfo database file: ${db_file}" + if [ -z "$ham_dir" ] || [ "$db_ext" != "ndb" ] ; then + if $clamscan_bin --quiet -d "${work_dir_securiteinfo}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports SecuriteInfo ${db_file} database integrity tested good" + true + else + xshok_pretty_echo_and_log "Clamscan reports SecuriteInfo ${db_file} database integrity tested BAD" + if [ "$remove_bad_database" == "yes" ] ; then + if rm -f "${work_dir_securiteinfo}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_securiteinfo}/${db_file}" fi - xshok_pretty_echo_and_log "Successfully updated SecuriteInfo production database file: $db_file" - securiteinfo_updates=1 - securiteinfo_db_update=1 - do_clamd_reload=1 - else - xshok_pretty_echo_and_log "Failed to successfully update SecuriteInfo production database file: $db_file - SKIPPING" fi + false + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${work_dir_securiteinfo}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/${db_file}" + fi + xshok_pretty_echo_and_log "Successfully updated SecuriteInfo production database file: ${db_file}" + securiteinfo_updates=1 + securiteinfo_db_update=1 + do_clamd_reload=1 else - $grep_bin -h -v -f "$work_dir_work_configs/whitelist.hex" "$work_dir_securiteinfo/$db_file" > "$test_dir/$db_file" - $clamscan_bin --infected --no-summary -d "$test_dir/$db_file" "$ham_dir"/* | command sed 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "$work_dir_work_configs/whitelist.txt" - $grep_bin -h -f "$work_dir_work_configs/whitelist.txt" "$test_dir/$db_file" | cut -d "*" -f 2 | sort | uniq >> "$work_dir_work_configs/whitelist.hex" - $grep_bin -h -v -f "$work_dir_work_configs/whitelist.hex" "$test_dir/$db_file" > "$test_dir/$db_file-tmp" - mv -f "$test_dir/$db_file-tmp" "$test_dir/$db_file" - if $clamscan_bin --quiet -d "$test_dir/$db_file" "$work_dir_work_configs/scan-test.txt" 2>/dev/null - then - xshok_pretty_echo_and_log "Clamscan reports SecuriteInfo $db_file database integrity tested good" - true - else - xshok_pretty_echo_and_log "Clamscan reports SecuriteInfo $db_file database integrity tested BAD" - rm -f "$work_dir_securiteinfo/$db_file" - if [ "$remove_bad_database" == "yes" ] ; then - if rm -f "$work_dir_securiteinfo/$db_file" ; then - xshok_pretty_echo_and_log "Removed invalid database: $work_dir_securiteinfo/$db_file" - fi - fi - false - fi && (test "$keep_db_backup" = "yes" && cp -f "$clam_dbs/$db_file" "$clam_dbs/$db_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "$test_dir/$db_file" "$clam_dbs" 2>/dev/null ; then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/$db_file" - if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/$db_file" + xshok_pretty_echo_and_log "Failed to successfully update SecuriteInfo production database file: ${db_file} - SKIPPING" + fi + else + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${work_dir_securiteinfo}/${db_file}" > "${test_dir}/${db_file}" + $clamscan_bin --infected --no-summary -d "${test_dir}/${db_file}" "$ham_dir"/* | command "$sed_bin" 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "${work_dir_work_configs}/whitelist.txt" + $grep_bin -h -f "${work_dir_work_configs}/whitelist.txt" "${test_dir}/${db_file}" | cut -d "*" -f 2 | sort | uniq >> "${work_dir_work_configs}/whitelist.hex" + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${test_dir}/${db_file}" > "${test_dir}/${db_file}-tmp" + mv -f "${test_dir}/${db_file}-tmp" "${test_dir}/${db_file}" + if $clamscan_bin --quiet -d "${test_dir}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports SecuriteInfo ${db_file} database integrity tested good" + true + else + xshok_pretty_echo_and_log "Clamscan reports SecuriteInfo ${db_file} database integrity tested BAD" + rm -f "${work_dir_securiteinfo}/${db_file}" + if [ "$remove_bad_database" == "yes" ] ; then + if rm -f "${work_dir_securiteinfo}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_securiteinfo}/${db_file}" fi - xshok_pretty_echo_and_log "Successfully updated SecuriteInfo production database file: $db_file" - securiteinfo_updates=1 - securiteinfo_db_update=1 - do_clamd_reload=1 - else - xshok_pretty_echo_and_log "Failed to successfully update SecuriteInfo production database file: $db_file - SKIPPING" fi + false + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${test_dir}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/${db_file}" + fi + xshok_pretty_echo_and_log "Successfully updated SecuriteInfo production database file: ${db_file}" + securiteinfo_updates=1 + securiteinfo_db_update=1 + do_clamd_reload=1 + else + xshok_pretty_echo_and_log "Failed to successfully update SecuriteInfo production database file: ${db_file} - SKIPPING" fi fi fi else - xshok_pretty_echo_and_log "Failed connection to $securiteinfo_url - SKIPPED SecuriteInfo $db_file update" + xshok_pretty_echo_and_log "Failed connection to ${securiteinfo_url} - SKIPPED SecuriteInfo ${db_file} update" fi if [ "$securiteinfo_db_update" != "1" ] ; then - xshok_pretty_echo_and_log "No updated SecuriteInfo $db_file database file found" "-" + xshok_pretty_echo_and_log "No updated SecuriteInfo ${db_file} database file" "-" fi done if [ "$securiteinfo_updates" != "1" ] ; then - xshok_pretty_echo_and_log "No SecuriteInfo database file updates found" "-" + xshok_pretty_echo_and_log "No SecuriteInfo database file updates" "-" fi else xshok_pretty_echo_and_log "SecuriteInfo Database File Updates" "=" - xshok_draw_time_remaining "$((update_interval - time_interval))" "$securiteinfo_update_hours" "SecuriteInfo" + if [ "$securiteinfo_premium" == "yes" ] ; then + xshok_draw_time_remaining "$((update_interval - time_interval))" "$securiteinfo_premium_update_hours" "SecuriteInfo" + else + xshok_draw_time_remaining "$((update_interval - time_interval))" "$securiteinfo_update_hours" "SecuriteInfo" + fi fi fi fi @@ -2391,17 +3288,17 @@ else if [ "$remove_disabled_databases" == "yes" ] ; then xshok_pretty_echo_and_log "Removing disabled SecuriteInfo Database files" for db_file in "${securiteinfo_dbs[@]}" ; do - if echo "$db_file" | $grep_bin -q "|"; then + if echo "$db_file" | $grep_bin -q "|" ; then db_file="${db_file%|*}" fi - if [ -r "$work_dir_securiteinfo/$db_file" ] ; then - xshok_pretty_echo_and_log "Removing $work_dir_securiteinfo/$db_file" - rm -f "$work_dir_securiteinfo/$db_file" + if [ -r "${work_dir_securiteinfo}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing ${work_dir_securiteinfo}/${db_file}" + rm -f "${work_dir_securiteinfo}/${db_file}" do_clamd_reload=1 fi - if [ -r "$clam_dbs/$db_file" ] ; then - xshok_pretty_echo_and_log "Removing $clam_dbs/$db_file" - rm -f "$clam_dbs/$db_file" + if [ -r "${clam_dbs}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing ${clam_dbs}/${db_file}" + rm -f "${clam_dbs}/${db_file}" do_clamd_reload=1 fi done @@ -2409,18 +3306,17 @@ else fi fi - ############################################################################################################################################## -# Check for updated linuxmalwaredetect database files every set number of hours as defined in the "USER CONFIGURATION" section of this script +# Check for updated LinuxMalwareDetect database files every set number of hours as defined in the "USER CONFIGURATION" section of this script ############################################################################################################################################## if [ "$linuxmalwaredetect_enabled" == "yes" ] ; then if [ -n "${linuxmalwaredetect_dbs[0]}" ] ; then if [ ${#linuxmalwaredetect_dbs} -lt 1 ] ; then xshok_pretty_echo_and_log "Failed linuxmalwaredetect_dbs config is invalid or not defined - SKIPPING" else - rm -f "$work_dir_linuxmalwaredetect/*.gz" - if [ -r "$work_dir_work_configs/last-linuxmalwaredetect-update.txt" ] ; then - last_linuxmalwaredetect_update="$(cat "$work_dir_work_configs/last-linuxmalwaredetect-update.txt")" + rm -f "${work_dir_linuxmalwaredetect}/*.gz" + if [ -r "${work_dir_work_configs}/last-linuxmalwaredetect-update.txt" ] ; then + last_linuxmalwaredetect_update="$(cat "${work_dir_work_configs}/last-linuxmalwaredetect-update.txt")" else last_linuxmalwaredetect_update="0" fi @@ -2429,116 +3325,836 @@ if [ "$linuxmalwaredetect_enabled" == "yes" ] ; then update_interval="$((linuxmalwaredetect_update_hours * 3600))" time_interval="$((current_time - last_linuxmalwaredetect_update))" if [ "$time_interval" -ge "$((update_interval - 600))" ] ; then - echo "$current_time" > "$work_dir_work_configs/last-linuxmalwaredetect-update.txt" + echo "$current_time" > "${work_dir_work_configs}/last-linuxmalwaredetect-update.txt" - xshok_pretty_echo_and_log "linuxmalwaredetect Database File Updates" "=" - xshok_pretty_echo_and_log "Checking for linuxmalwaredetect updates..." - linuxmalwaredetect_updates="0" - for db_file in "${linuxmalwaredetect_dbs[@]}" ; do - if [ "$loop" == "1" ] ; then - xshok_pretty_echo_and_log "---" - fi - xshok_pretty_echo_and_log "Checking for updated linuxmalwaredetect database file: $db_file" - linuxmalwaredetect_db_update="0" - xshok_file_download "$work_dir_linuxmalwaredetect/$db_file" "$linuxmalwaredetect_url/$db_file" - ret="$?" - if [ "$ret" -eq 0 ] ; then - loop="1" - if ! cmp -s "$work_dir_linuxmalwaredetect/$db_file" "$clam_dbs/$db_file" ; then - if [ $? -eq 0 ] ; then + xshok_pretty_echo_and_log "LinuxMalwareDetect Database File Updates" "=" + xshok_pretty_echo_and_log "Checking for LinuxMalwareDetect updates..." + + # Check for a new version + found_upgrade="no" + if [ -n "$curl_bin" ] ; then + # shellcheck disable=SC2086 + latest_linuxmalwaredetect_version="$($curl_bin --compressed $curl_proxy $curl_insecure $curl_output_level --connect-timeout "${downloader_connect_timeout}" --remote-time --location --retry "${downloader_tries}" --max-time "${downloader_max_time}" "$linuxmalwaredetect_version_url" 2>&11 | head -n1 | xargs)" + else + # shellcheck disable=SC2086 + latest_linuxmalwaredetect_version="$($wget_bin $wget_compression $wget_proxy $wget_insecure $wget_output_level --connect-timeout="${downloader_connect_timeout}" --random-wait --tries="${downloader_tries}" --timeout="${downloader_max_time}" "$linuxmalwaredetect_version_url" -O - 2>&12 | $grep_bin "^script_version=" | head -n1 | xargs)" + fi + + if [ "$latest_linuxmalwaredetect_version" ] ; then + # shellcheck disable=SC2183,SC2086 + if [ -f "${work_dir_linuxmalwaredetect}/current_linuxmalwaredetect_version" ] ; then + current_linuxmalwaredetect_version="$(head -n1 "${work_dir_linuxmalwaredetect}/current_linuxmalwaredetect_version" | xargs)" + else + current_linuxmalwaredetect_version="-1" + fi + if [ "$latest_linuxmalwaredetect_version" != "$current_linuxmalwaredetect_version" ] ; then + xshok_pretty_echo_and_log "LinuxMalwareDetect Database File Updates" "=" + found_upgrade="yes" + fi + fi + + if [ "$found_upgrade" == "yes" ] ; then + mkdir -p "${work_dir_linuxmalwaredetect}/tmp/" + xshok_file_download "${work_dir_linuxmalwaredetect}/tmp/sigpack.tgz" "${linuxmalwaredetect_sigpack_url}" + ret="$?" + if [ "$ret" -eq 0 ] ; then + mkdir -p "${work_dir_linuxmalwaredetect}/tmp/" + $tar_bin --strip-components=1 -xzf "${work_dir_linuxmalwaredetect}/tmp/sigpack.tgz" --directory "${work_dir_linuxmalwaredetect}/tmp/" + #ls -l "${work_dir_linuxmalwaredetect}/tmp/" + if [ "$enable_yararules" == "yes" ] ; then + find "${work_dir_linuxmalwaredetect}/tmp/" -type f -iname "rfxn.*" -exec mv -f '{}' "${work_dir_linuxmalwaredetect}/" \; + else + find "${work_dir_linuxmalwaredetect}/tmp/" -type f -iname "rfxn.*" ! \( -iname "*.yara" -o -iname "*.yar" \) -exec mv -f '{}' "${work_dir_linuxmalwaredetect}/" \; + fi + # cleanup + rm -rf -- "${work_dir_linuxmalwaredetect:?}/tmp" + #ls -l "${work_dir_linuxmalwaredetect}/" + + for db_file in "${linuxmalwaredetect_dbs[@]}" ; do + if [ "$loop" == "1" ] ; then + xshok_pretty_echo_and_log "---" + fi + loop="1" + if ! cmp -s "${work_dir_linuxmalwaredetect}/${db_file}" "${clam_dbs}/${db_file}" ; then db_ext="${db_file#*.}" - xshok_pretty_echo_and_log "Testing updated linuxmalwaredetect database file: $db_file" + xshok_pretty_echo_and_log "Testing updated LinuxMalwareDetect database file: ${db_file}" if [ -z "$ham_dir" ] || [ "$db_ext" != "ndb" ] ; then - if $clamscan_bin --quiet -d "$work_dir_linuxmalwaredetect/$db_file" "$work_dir_work_configs/scan-test.txt" 2>/dev/null - then - xshok_pretty_echo_and_log "Clamscan reports linuxmalwaredetect $db_file database integrity tested good" + if $clamscan_bin --quiet -d "${work_dir_linuxmalwaredetect}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports LinuxMalwareDetect ${db_file} database integrity tested good" true else - xshok_pretty_echo_and_log "Clamscan reports linuxmalwaredetect $db_file database integrity tested BAD" + xshok_pretty_echo_and_log "Clamscan reports LinuxMalwareDetect ${db_file} database integrity tested BAD" if [ "$remove_bad_database" == "yes" ] ; then - if rm -f "$work_dir_linuxmalwaredetect/$db_file" ; then - xshok_pretty_echo_and_log "Removed invalid database: $work_dir_linuxmalwaredetect/$db_file" + if rm -f "${work_dir_linuxmalwaredetect}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_linuxmalwaredetect}/${db_file}" fi fi false - fi && (test "$keep_db_backup" = "yes" && cp -f "$clam_dbs/$db_file" "$clam_dbs/$db_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "$work_dir_linuxmalwaredetect/$db_file" "$clam_dbs" 2>/dev/null ; then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/$db_file" + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${work_dir_linuxmalwaredetect}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/local.ign" + restorecon "${clam_dbs}/local.ign" fi - xshok_pretty_echo_and_log "Successfully updated linuxmalwaredetect production database file: $db_file" - linuxmalwaredetect_updates=1 - linuxmalwaredetect_db_update=1 + xshok_pretty_echo_and_log "Successfully updated LinuxMalwareDetect production database file: ${db_file}" do_clamd_reload=1 else - xshok_pretty_echo_and_log "Failed to successfully update linuxmalwaredetect production database file: $db_file - SKIPPING" + xshok_pretty_echo_and_log "Failed to successfully update LinuxMalwareDetect production database file: ${db_file} - SKIPPING" fi else - $grep_bin -h -v -f "$work_dir_work_configs/whitelist.hex" "$work_dir_linuxmalwaredetect/$db_file" > "$test_dir/$db_file" - $clamscan_bin --infected --no-summary -d "$test_dir/$db_file" "$ham_dir"/* | command sed 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "$work_dir_work_configs/whitelist.txt" - $grep_bin -h -f "$work_dir_work_configs/whitelist.txt" "$test_dir/$db_file" | cut -d "*" -f 2 | sort | uniq >> "$work_dir_work_configs/whitelist.hex" - $grep_bin -h -v -f "$work_dir_work_configs/whitelist.hex" "$test_dir/$db_file" > "$test_dir/$db_file-tmp" - mv -f "$test_dir/$db_file-tmp" "$test_dir/$db_file" - if $clamscan_bin --quiet -d "$test_dir/$db_file" "$work_dir_work_configs/scan-test.txt" 2>/dev/null ; then - xshok_pretty_echo_and_log "Clamscan reports linuxmalwaredetect $db_file database integrity tested good" + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${work_dir_linuxmalwaredetect}/${db_file}" > "${test_dir}/${db_file}" + $clamscan_bin --infected --no-summary -d "${test_dir}/${db_file}" "$ham_dir"/* | command "$sed_bin" 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "${work_dir_work_configs}/whitelist.txt" + $grep_bin -h -f "${work_dir_work_configs}/whitelist.txt" "${test_dir}/${db_file}" | cut -d "*" -f 2 | sort | uniq >> "${work_dir_work_configs}/whitelist.hex" + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${test_dir}/${db_file}" > "${test_dir}/${db_file}-tmp" + mv -f "${test_dir}/${db_file}-tmp" "${test_dir}/${db_file}" + if $clamscan_bin --quiet -d "${test_dir}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports LinuxMalwareDetect ${db_file} database integrity tested good" + true + else + xshok_pretty_echo_and_log "Clamscan reports LinuxMalwareDetect ${db_file} database integrity tested BAD" + if [ "$remove_bad_database" == "yes" ] ; then + if rm -f "${work_dir_linuxmalwaredetect}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_linuxmalwaredetect}/${db_file}" + fi + fi + false + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${test_dir}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/${db_file}" + fi + xshok_pretty_echo_and_log "Successfully updated LinuxMalwareDetect production database file: ${db_file}" + do_clamd_reload=1 + else + xshok_pretty_echo_and_log "Failed to successfully update LinuxMalwareDetect production database file: ${db_file} - SKIPPING" + fi + fi + fi + + done + #save the current version + echo "$latest_linuxmalwaredetect_version" > "${work_dir_linuxmalwaredetect}/current_linuxmalwaredetect_version" + + else + xshok_pretty_echo_and_log "WARNING: Failed connection to ${linuxmalwaredetect_sigpack_url} - SKIPPED LinuxMalwareDetect update" + fi + else + xshok_pretty_echo_and_log "No LinuxMalwareDetect database file updates" "-" + fi + else + xshok_pretty_echo_and_log "LinuxMalwareDetect Database File Updates" "=" + xshok_draw_time_remaining "$((update_interval - time_interval))" "$linuxmalwaredetect_update_hours" "linuxmalwaredetect" + fi + fi + fi +else + if [ -n "${linuxmalwaredetect_dbs[0]}" ] ; then + if [ "$remove_disabled_databases" == "yes" ] ; then + xshok_pretty_echo_and_log "Removing disabled LinuxMalwareDetect Database files" + + if [ -f "${work_dir_linuxmalwaredetect}/current_linuxmalwaredetect_version" ] ; then + rm -f "${work_dir_linuxmalwaredetect}/current_linuxmalwaredetect_version" + fi + for db_file in "${linuxmalwaredetect_dbs[@]}" ; do + if echo "$db_file" | $grep_bin -q "|" ; then + db_file="${db_file%|*}" + fi + if [ -r "${work_dir_linuxmalwaredetect}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing ${work_dir_linuxmalwaredetect}/${db_file}" + rm -f "${work_dir_linuxmalwaredetect}/${db_file}" + do_clamd_reload=1 + fi + if [ -r "${clam_dbs}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing ${clam_dbs}/${db_file}" + rm -f "${clam_dbs}/${db_file}" + do_clamd_reload=1 + fi + done + fi + fi +fi +############################################################################################################################################## +# Check for updated interServer database files every set number of hours as defined in the "USER CONFIGURATION" section of this script # +############################################################################################################################################## +if [ "$interserver_enabled" == "yes" ] ; then + if [ -n "${interserver_dbs[0]}" ] ; then + if [ ${#interserver_dbs} -lt 1 ] ; then + xshok_pretty_echo_and_log "Failed interserver_dbs config is invalid or not defined - SKIPPING" + else + rm -f "${work_dir_interserver}/*.gz" + if [ -r "${work_dir_work_configs}/last-is-update.txt" ] ; then + last_interserver_update="$(cat "${work_dir_work_configs}/last-is-update.txt")" + else + last_interserver_update="0" + fi + db_file="" + loop="" + if [ "$interserver_premium" == "yes" ] ; then + update_interval="$((interserver_premium_update_hours * 3600))" + else + update_interval="$((interserver_update_hours * 3600))" + fi + time_interval="$((current_time - last_interserver_update))" + if [ "$time_interval" -ge "$((update_interval - 600))" ] ; then + echo "$current_time" > "${work_dir_work_configs}/last-is-update.txt" + xshok_pretty_echo_and_log "interserver Database File Updates" "=" + xshok_pretty_echo_and_log "Checking for interserver updates..." + interserver_updates="0" + for db_file in "${interserver_dbs[@]}" ; do + if [ "$loop" == "1" ] ; then + xshok_pretty_echo_and_log "---" + fi + xshok_pretty_echo_and_log "Checking for updated interServer database file: ${db_file}" + interserver_db_update="0" + xshok_file_download "${work_dir_interserver}/${db_file}" "${interserver_url}/${db_file}" + ret="$?" + if [ "$ret" -eq 0 ] ; then + loop="1" + if ! cmp -s "${work_dir_interserver}/${db_file}" "${clam_dbs}/${db_file}" ; then + db_ext="${db_file#*.}" + + xshok_pretty_echo_and_log "Testing updated interServer database file: ${db_file}" + if [ -z "$ham_dir" ] || [ "$db_ext" != "ndb" ] ; then + if $clamscan_bin --quiet -d "${work_dir_interserver}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports interServer ${db_file} database integrity tested good" true else - xshok_pretty_echo_and_log "Clamscan reports linuxmalwaredetect $db_file database integrity tested BAD" + xshok_pretty_echo_and_log "Clamscan reports interServer ${db_file} database integrity tested BAD" if [ "$remove_bad_database" == "yes" ] ; then - if rm -f "$work_dir_linuxmalwaredetect/$db_file" ; then - xshok_pretty_echo_and_log "Removed invalid database: $work_dir_linuxmalwaredetect/$db_file" + if rm -f "${work_dir_interserver}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_interserver}/${db_file}" fi fi false - fi && (test "$keep_db_backup" = "yes" && cp -f "$clam_dbs/$db_file" "$clam_dbs/$db_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "$test_dir/$db_file" "$clam_dbs" 2>/dev/null ; then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/$db_file" + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${work_dir_interserver}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/$db_file" + restorecon "${clam_dbs}/${db_file}" + fi + xshok_pretty_echo_and_log "Successfully updated interServer production database file: ${db_file}" + interserver_updates=1 + interserver_db_update=1 + do_clamd_reload=1 + else + xshok_pretty_echo_and_log "Failed to successfully update interServer production database file: ${db_file} - SKIPPING" + fi + else + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${work_dir_interserver}/${db_file}" > "${test_dir}/${db_file}" + $clamscan_bin --infected --no-summary -d "${test_dir}/${db_file}" "$ham_dir"/* | command "$sed_bin" 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "${work_dir_work_configs}/whitelist.txt" + $grep_bin -h -f "${work_dir_work_configs}/whitelist.txt" "${test_dir}/${db_file}" | cut -d "*" -f 2 | sort | uniq >> "${work_dir_work_configs}/whitelist.hex" + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${test_dir}/${db_file}" > "${test_dir}/${db_file}-tmp" + mv -f "${test_dir}/${db_file}-tmp" "${test_dir}/${db_file}" + if $clamscan_bin --quiet -d "${test_dir}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports interServer ${db_file} database integrity tested good" + true + else + xshok_pretty_echo_and_log "Clamscan reports interServer ${db_file} database integrity tested BAD" + rm -f "${work_dir_interserver}/${db_file}" + if [ "$remove_bad_database" == "yes" ] ; then + if rm -f "${work_dir_interserver}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_interserver}/${db_file}" + fi + fi + false + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${test_dir}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/${db_file}" + fi + xshok_pretty_echo_and_log "Successfully updated interServer production database file: ${db_file}" + interserver_updates=1 + interserver_db_update=1 + do_clamd_reload=1 + else + xshok_pretty_echo_and_log "Failed to successfully update interServer production database file: ${db_file} - SKIPPING" + fi + fi + fi + else + xshok_pretty_echo_and_log "Failed connection to ${interserver_url} - SKIPPED interServer ${db_file} update" + fi + if [ "$interserver_db_update" != "1" ] ; then + xshok_pretty_echo_and_log "No updated interServer ${db_file} database file" "-" + fi + done + if [ "$interserver_updates" != "1" ] ; then + xshok_pretty_echo_and_log "No interServer database file updates" "-" + fi + else + xshok_pretty_echo_and_log "interServer Database File Updates" "=" + if [ "$interserver_premium" == "yes" ] ; then + xshok_draw_time_remaining "$((update_interval - time_interval))" "$interserver_premium_update_hours" "interserver" + else + xshok_draw_time_remaining "$((update_interval - time_interval))" "$interserver_update_hours" "interserver" + fi + fi + fi + fi +else + if [ -n "$interserver_dbs" ] ; then + if [ "$remove_disabled_databases" == "yes" ] ; then + xshok_pretty_echo_and_log "Removing disabled interServer Database files" + for db_file in "${interserver_dbs[@]}" ; do + if echo "$db_file" | $grep_bin -q "|" ; then + db_file="${db_file%|*}" + fi + if [ -r "${work_dir_interserver}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing ${work_dir_interserver}/${db_file}" + rm -f "${work_dir_interserver}/${db_file}" + do_clamd_reload=1 + fi + if [ -r "${clam_dbs}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing ${clam_dbs}/${db_file}" + rm -f "${clam_dbs}/${db_file}" + do_clamd_reload=1 + fi + done + fi + fi +fi + +############################################################################################################################################## +# Check for updated Malware Expert database files every set number of hours as defined in the "USER CONFIGURATION" section of this script # +############################################################################################################################################## +if [ "$malwareexpert_enabled" == "yes" ] ; then + if [ "$malwareexpert_serial_key" != "YOUR-SERIAL-KEY" ] && [ -n "$malwareexpert_serial_key" ]; then + if [ -n "${malwareexpert_dbs[0]}" ] ; then + if [ ${#malwareexpert_dbs} -lt 1 ] ; then + xshok_pretty_echo_and_log "Failed malwareexpert_dbs config is invalid or not defined - SKIPPING" + else + rm -f "${work_dir_malwareexpert}/*.gz" + if [ -r "${work_dir_work_configs}/last-me-update.txt" ] ; then + last_malwareexpert_update="$(cat "${work_dir_work_configs}/last-me-update.txt")" + else + last_malwareexpert_update="0" + fi + db_file="" + loop="" + if [ "$malwareexpert_premium" == "yes" ] ; then + update_interval="$((malwareexpert_premium_update_hours * 3600))" + else + update_interval="$((malwareexpert_update_hours * 3600))" + fi + time_interval="$((current_time - last_malwareexpert_update))" + if [ "$time_interval" -ge "$((update_interval - 600))" ] ; then + echo "$current_time" > "${work_dir_work_configs}/last-me-update.txt" + xshok_pretty_echo_and_log "malwareexpert Database File Updates" "=" + xshok_pretty_echo_and_log "Checking for malwareexpert updates..." + malwareexpert_updates="0" + for db_file in "${malwareexpert_dbs[@]}" ; do + if [ "$loop" == "1" ] ; then + xshok_pretty_echo_and_log "---" + fi + xshok_pretty_echo_and_log "Checking for updated Malware Expert database file: ${db_file}" + malwareexpert_db_update="0" + xshok_file_download "${work_dir_malwareexpert}/${db_file}" "${malwareexpert_url}/${malwareexpert_serial_key}/${db_file}" + ret="$?" + if [ "$ret" -eq 0 ] ; then + loop="1" + if ! cmp -s "${work_dir_malwareexpert}/${db_file}" "${clam_dbs}/${db_file}" ; then + db_ext="${db_file#*.}" + + xshok_pretty_echo_and_log "Testing updated Malware Expert database file: ${db_file}" + if [ -z "$ham_dir" ] || [ "$db_ext" != "ndb" ] ; then + if $clamscan_bin --quiet -d "${work_dir_malwareexpert}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports Malware Expert ${db_file} database integrity tested good" + true + else + xshok_pretty_echo_and_log "Clamscan reports Malware Expert ${db_file} database integrity tested BAD" + if [ "$remove_bad_database" == "yes" ] ; then + if rm -f "${work_dir_malwareexpert}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_malwareexpert}/${db_file}" + fi + fi + false + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${work_dir_malwareexpert}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/${db_file}" + fi + xshok_pretty_echo_and_log "Successfully updated Malware Expert production database file: ${db_file}" + malwareexpert_updates=1 + malwareexpert_db_update=1 + do_clamd_reload=1 + else + xshok_pretty_echo_and_log "Failed to successfully update Malware Expert production database file: ${db_file} - SKIPPING" + fi + else + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${work_dir_malwareexpert}/${db_file}" > "${test_dir}/${db_file}" + $clamscan_bin --infected --no-summary -d "${test_dir}/${db_file}" "$ham_dir"/* | command "$sed_bin" 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "${work_dir_work_configs}/whitelist.txt" + $grep_bin -h -f "${work_dir_work_configs}/whitelist.txt" "${test_dir}/${db_file}" | cut -d "*" -f 2 | sort | uniq >> "${work_dir_work_configs}/whitelist.hex" + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${test_dir}/${db_file}" > "${test_dir}/${db_file}-tmp" + mv -f "${test_dir}/${db_file}-tmp" "${test_dir}/${db_file}" + if $clamscan_bin --quiet -d "${test_dir}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports Malware Expert ${db_file} database integrity tested good" + true + else + xshok_pretty_echo_and_log "Clamscan reports Malware Expert ${db_file} database integrity tested BAD" + rm -f "${work_dir_malwareexpert}/${db_file}" + if [ "$remove_bad_database" == "yes" ] ; then + if rm -f "${work_dir_malwareexpert}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_malwareexpert}/${db_file}" + fi + fi + false + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${test_dir}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/${db_file}" + fi + xshok_pretty_echo_and_log "Successfully updated Malware Expert production database file: ${db_file}" + malwareexpert_updates=1 + malwareexpert_db_update=1 + do_clamd_reload=1 + else + xshok_pretty_echo_and_log "Failed to successfully update Malware Expert production database file: ${db_file} - SKIPPING" + fi + fi + fi + else + xshok_pretty_echo_and_log "Failed connection to ${malwareexpert_url} - SKIPPED Malware Expert ${db_file} update" + fi + if [ "$malwareexpert_db_update" != "1" ] ; then + xshok_pretty_echo_and_log "No updated Malware Expert ${db_file} database file" "-" + fi + done + if [ "$malwareexpert_updates" != "1" ] ; then + xshok_pretty_echo_and_log "No Malware Expert database file updates" "-" + fi + else + xshok_pretty_echo_and_log "Malware Expert Database File Updates" "=" + if [ "$malwareexpert_premium" == "yes" ] ; then + xshok_draw_time_remaining "$((update_interval - time_interval))" "$malwareexpert_premium_update_hours" "malwareexpert" + else + xshok_draw_time_remaining "$((update_interval - time_interval))" "$malwareexpert_update_hours" "malwareexpert" + fi + fi + fi + fi + fi +else + if [ -n "$malwareexpert_dbs" ] ; then + if [ "$remove_disabled_databases" == "yes" ] ; then + xshok_pretty_echo_and_log "Removing disabled Malware Expert Database files" + for db_file in "${malwareexpert_dbs[@]}" ; do + if echo "$db_file" | $grep_bin -q "|" ; then + db_file="${db_file%|*}" + fi + if [ -r "${work_dir_malwareexpert}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing ${work_dir_malwareexpert}/${db_file}" + rm -f "${work_dir_malwareexpert}/${db_file}" + do_clamd_reload=1 + fi + if [ -r "${clam_dbs}/${db_file}" ] ; then + xshok_pretty_echo_and_log "Removing ${clam_dbs}/${db_file}" + rm -f "${clam_dbs}/${db_file}" + do_clamd_reload=1 + fi + done + fi + fi +fi + +######################################################################################################################################### +# Download MalwarePatrol database file every set number of hours as defined in the "USER CONFIGURATION" section of this script. # +########################################################################################################################################## +if [ "$malwarepatrol_enabled" == "yes" ] ; then + if [ "$malwarepatrol_receipt_code" != "YOUR-RECEIPT-NUMBER" ] ; then + if [ -n "${malwarepatrol_db}" ] ; then + rm -f "${work_dir_malwarepatrol}/*.gz" + if [ -r "${work_dir_work_configs}/last-mbl-update.txt" ] ; then + last_malwarepatrol_update="$(cat "${work_dir_work_configs}/last-mbl-update.txt")" + else + last_malwarepatrol_update="0" + fi + loop="" + update_interval="$((malwarepatrol_update_hours * 3600))" + time_interval="$((current_time - last_malwarepatrol_update))" + if [ "$time_interval" -ge "$((update_interval - 600))" ] ; then + echo "$current_time" > "${work_dir_work_configs}/last-mbl-update.txt" + xshok_pretty_echo_and_log "MalwarePatrol Database File Updates" "=" + xshok_pretty_echo_and_log "Checking for MalwarePatrol updates..." + malwarepatrol_updates="0" + + # Cleanup any not required database files + if [ "$malwarepatrol_db" != "malwarepatrol.db" ] && [ -f "${clam_dbs}/malwarepatrol.db" ] ; then + rm -f "${clam_dbs}/malwarepatrol.db"; + fi + if [ "$malwarepatrol_db" != "malwarepatrol.ndb" ] && [ -f "${clam_dbs}/malwarepatrol.ndb" ] ; then + rm -f "${clam_dbs}/malwarepatrol.ndb"; + fi + + if [ "$loop" == "1" ] ; then + xshok_pretty_echo_and_log "---" + fi + xshok_pretty_echo_and_log "Checking for updated MalwarePatrol database file: ${malwarepatrol_db}" + malwarepatrol_db_update="0" + + xshok_file_download "${work_dir_malwarepatrol}/${malwarepatrol_db}" "${malwarepatrol_url}" + + ret="$?" + if [ "$ret" -eq 0 ] ; then + loop="1" + if ! cmp -s "${work_dir_malwarepatrol}/${malwarepatrol_db}" "${clam_dbs}/${malwarepatrol_db}" ; then + db_ext="${malwarepatrol_db#*.}" + + xshok_pretty_echo_and_log "Testing updated MalwarePatrol database file: ${malwarepatrol_db}" + if [ -z "$ham_dir" ] || [ "$db_ext" != "ndb" ] ; then + if $clamscan_bin --quiet -d "${work_dir_malwarepatrol}/${malwarepatrol_db}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports MalwarePatrol ${malwarepatrol_db} database integrity tested good" + true + else + xshok_pretty_echo_and_log "Clamscan reports MalwarePatrol ${malwarepatrol_db} database integrity tested BAD" + if [ "$remove_bad_database" == "yes" ] ; then + if rm -f "${work_dir_malwarepatrol}/${malwarepatrol_db}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_malwarepatrol}/${malwarepatrol_db}" + fi + fi + false + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${malwarepatrol_db}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${work_dir_malwarepatrol}/${malwarepatrol_db}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${malwarepatrol_db}" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/${malwarepatrol_db}" + fi + xshok_pretty_echo_and_log "Successfully updated MalwarePatrol production database file: ${malwarepatrol_db}" + malwarepatrol_updates=1 + malwarepatrol_db_update=1 + do_clamd_reload=1 + else + xshok_pretty_echo_and_log "Failed to successfully update MalwarePatrol production database file: ${malwarepatrol_db} - SKIPPING" + fi + else + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${work_dir_malwarepatrol}/${malwarepatrol_db}" > "${test_dir}/${malwarepatrol_db}" + $clamscan_bin --infected --no-summary -d "${test_dir}/${malwarepatrol_db}" "$ham_dir"/* | command "$sed_bin" 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "${work_dir_work_configs}/whitelist.txt" + $grep_bin -h -f "${work_dir_work_configs}/whitelist.txt" "${test_dir}/${malwarepatrol_db}" | cut -d "*" -f 2 | sort | uniq >> "${work_dir_work_configs}/whitelist.hex" + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${test_dir}/${malwarepatrol_db}" > "${test_dir}/${malwarepatrol_db}-tmp" + mv -f "${test_dir}/${malwarepatrol_db}-tmp" "${test_dir}/${malwarepatrol_db}" + if $clamscan_bin --quiet -d "${test_dir}/${malwarepatrol_db}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports MalwarePatrol ${malwarepatrol_db} database integrity tested good" + true + else + xshok_pretty_echo_and_log "Clamscan reports MalwarePatrol ${malwarepatrol_db} database integrity tested BAD" + rm -f "${work_dir_malwarepatrol}/${malwarepatrol_db}" + if [ "$remove_bad_database" == "yes" ] ; then + if rm -f "${work_dir_malwarepatrol}/${malwarepatrol_db}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_malwarepatrol}/${malwarepatrol_db}" + fi + fi + false + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${malwarepatrol_db}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${test_dir}/${malwarepatrol_db}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${malwarepatrol_db}" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/${malwarepatrol_db}" + fi + xshok_pretty_echo_and_log "Successfully updated MalwarePatrol production database file: ${malwarepatrol_db}" + malwarepatrol_updates=1 + malwarepatrol_db_update=1 + do_clamd_reload=1 + else + xshok_pretty_echo_and_log "Failed to successfully update MalwarePatrol production database file: ${malwarepatrol_db} - SKIPPING" + fi + fi + fi + else + xshok_pretty_echo_and_log "Failed connection to ${malwarepatrol_url} - SKIPPED MalwarePatrol ${malwarepatrol_db} update" + fi + if [ "$malwarepatrol_db_update" != "1" ] ; then + xshok_pretty_echo_and_log "No updated MalwarePatrol ${malwarepatrol_db} database file" "-" + fi + if [ "$malwarepatrol_updates" != "1" ] ; then + xshok_pretty_echo_and_log "No MalwarePatrol database file updates" "-" + fi + else + xshok_pretty_echo_and_log "MalwarePatrol Database File Updates" "=" + xshok_draw_time_remaining "$((update_interval - time_interval))" "$malwarepatrol_update_hours" "malwarepatrol" + fi + fi + fi +else + if [ -n "$malwarepatrol_dbs" ] ; then + if [ "$remove_disabled_databases" == "yes" ] ; then + xshok_pretty_echo_and_log "Removing disabled MalwarePatrol Database files" + if [ -r "${work_dir_malwarepatrol}/${malwarepatrol_db}" ] ; then + xshok_pretty_echo_and_log "Removing ${work_dir_malwarepatrol}/${malwarepatrol_db}" + rm -f "${work_dir_malwarepatrol}/${malwarepatrol_db}" + do_clamd_reload=1 + fi + if [ -r "${clam_dbs}/${malwarepatrol_db}" ] ; then + xshok_pretty_echo_and_log "Removing ${clam_dbs}/${malwarepatrol_db}" + rm -f "${clam_dbs}/${malwarepatrol_db}" + do_clamd_reload=1 + fi + fi + fi +fi + +############################################################################################################################################## +# Check for updated urlhaus database files every set number of hours as defined in the "USER CONFIGURATION" section of this script +############################################################################################################################################## +if [ "$urlhaus_enabled" == "yes" ] ; then + if [ -n "${urlhaus_dbs[0]}" ] ; then + if [ ${#urlhaus_dbs} -lt 1 ] ; then + xshok_pretty_echo_and_log "Failed urlhaus_dbs config is invalid or not defined - SKIPPING" + else + rm -f "${work_dir_urlhaus}/*.gz" + if [ -r "${work_dir_work_configs}/last-urlhaus-update.txt" ] ; then + last_urlhaus_update="$(cat "${work_dir_work_configs}/last-urlhaus-update.txt")" + else + last_urlhaus_update="0" + fi + db_file="" + loop="" + update_interval="$((urlhaus_update_hours * 3600))" + time_interval="$((current_time - last_urlhaus_update))" + if [ "$time_interval" -ge "$((update_interval - 600))" ] ; then + echo "$current_time" > "${work_dir_work_configs}/last-urlhaus-update.txt" + + xshok_pretty_echo_and_log "URLhaus Database File Updates" "=" + xshok_pretty_echo_and_log "Checking for urlhaus updates..." + urlhaus_updates="0" + for db_file in "${urlhaus_dbs[@]}" ; do + if echo "$db_file" | $grep_bin -q "/" ; then + yr_dir="/$(echo "$db_file" | cut -d "/" -f 1)" + db_file="$(echo "$db_file" | cut -d "/" -f 2)" + else yr_dir="" + fi + if [ "$loop" == "1" ] ; then + xshok_pretty_echo_and_log "---" + fi + xshok_pretty_echo_and_log "Checking for updated urlhaus database file: ${db_file}" + urlhaus_db_update="0" + if xshok_file_download "${work_dir_urlhaus}/${db_file}" "${urlhaus_url}/${db_file}" ; then + loop="1" + if ! cmp -s "${work_dir_urlhaus}/${db_file}" "${clam_dbs}/${db_file}" ; then + db_ext="${db_file#*.}" + xshok_pretty_echo_and_log "Testing updated urlhaus database file: ${db_file}" + if [ -z "$ham_dir" ] || [ "$db_ext" != "ndb" ] ; then + if $clamscan_bin --quiet -d "${work_dir_urlhaus}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports urlhaus ${db_file} database integrity tested good" + true + else + xshok_pretty_echo_and_log "Clamscan reports urlhaus ${db_file} database integrity tested BAD" + if [ "$remove_bad_database" == "yes" ] ; then + if rm -f "${work_dir_urlhaus}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_urlhaus}/${db_file}" + fi + fi + false + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${work_dir_urlhaus}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/${db_file}" + fi + xshok_pretty_echo_and_log "Successfully updated urlhaus production database file: ${db_file}" + urlhaus_updates=1 + urlhaus_db_update=1 + do_clamd_reload=1 + else + xshok_pretty_echo_and_log "Failed to successfully update urlhaus production database file: ${db_file} - SKIPPING" + fi + else + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${work_dir_urlhaus}/${db_file}" > "${test_dir}/${db_file}" + $clamscan_bin --infected --no-summary -d "${test_dir}/${db_file}" "$ham_dir"/* | command "$sed_bin" 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "${work_dir_work_configs}/whitelist.txt" + $grep_bin -h -f "${work_dir_work_configs}/whitelist.txt" "${test_dir}/${db_file}" | cut -d "*" -f 2 | sort | uniq >> "${work_dir_work_configs}/whitelist.hex" + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${test_dir}/${db_file}" > "${test_dir}/${db_file}-tmp" + mv -f "${test_dir}/${db_file}-tmp" "${test_dir}/${db_file}" + if $clamscan_bin --quiet -d "${test_dir}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports urlhaus ${db_file} database integrity tested good" + true + else + xshok_pretty_echo_and_log "Clamscan reports urlhaus ${db_file} database integrity tested BAD" + if [ "$remove_bad_database" == "yes" ] ; then + if rm -f "${work_dir_urlhaus}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_urlhaus}/${db_file}" + fi + fi + false + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${test_dir}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/${db_file}" + fi + xshok_pretty_echo_and_log "Successfully updated urlhaus production database file: ${db_file}" + urlhaus_updates=1 + urlhaus_db_update=1 + do_clamd_reload=1 + else + xshok_pretty_echo_and_log "Failed to successfully update urlhaus production database file: ${db_file} - SKIPPING" + fi + fi + + fi + else + xshok_pretty_echo_and_log "WARNING: Failed connection to $urlhaus_url - SKIPPED urlhaus ${db_file} update" + fi + if [ "$urlhaus_db_update" != "1" ] ; then + xshok_pretty_echo_and_log "No updated urlhaus ${db_file} database file" + fi + done + if [ "$urlhaus_updates" != "1" ] ; then + xshok_pretty_echo_and_log "No urlhaus database file updates" "-" + fi + else + + xshok_pretty_echo_and_log "URLhaus Database File Updates" "=" + xshok_draw_time_remaining "$((update_interval - time_interval))" "$urlhaus_update_hours" "urlhaus" + fi + fi + fi +else + if [ -n "${urlhaus_dbs[0]}" ] ; then + if [ "$remove_disabled_databases" == "yes" ] ; then + xshok_pretty_echo_and_log "Removing disabled urlhaus Database files" + for db_file in "${urlhaus_dbs[@]}" ; do + if echo "$db_file" | $grep_bin -q "/" ; then + db_file="$(echo "$db_file" | cut -d "/" -f 2)" + fi + if echo "$db_file" | $grep_bin -q "|" ; then + db_file="${db_file%|*}" + fi + if [ -r "${work_dir_urlhaus}/${db_file}" ] ; then + rm -f "${work_dir_urlhaus}/${db_file}" + do_clamd_reload="1" + fi + if [ -r "${clam_dbs}/${db_file}" ] ; then + rm -f "${clam_dbs}/${db_file}" + do_clamd_reload=1 + fi + done + fi + fi +fi + +############################################################################################################################################## +# Check for updated yararulesproject database files every set number of hours as defined in the "USER CONFIGURATION" section of this script +############################################################################################################################################## +if [ "$yararulesproject_enabled" == "yes" ] ; then + if [ -n "${yararulesproject_dbs[0]}" ] ; then + if [ ${#yararulesproject_dbs} -lt 1 ] ; then + xshok_pretty_echo_and_log "Failed yararulesproject_dbs config is invalid or not defined - SKIPPING" + else + rm -f "${work_dir_yararulesproject}/*.gz" + if [ -r "${work_dir_work_configs}/last-yararulesproject-update.txt" ] ; then + last_yararulesproject_update="$(cat "${work_dir_work_configs}/last-yararulesproject-update.txt")" + else + last_yararulesproject_update="0" + fi + db_file="" + loop="" + update_interval="$((yararulesproject_update_hours * 3600))" + time_interval="$((current_time - last_yararulesproject_update))" + if [ "$time_interval" -ge "$((update_interval - 600))" ] ; then + echo "$current_time" > "${work_dir_work_configs}/last-yararulesproject-update.txt" + + xshok_pretty_echo_and_log "Yara-Rules Database File Updates" "=" + xshok_pretty_echo_and_log "Checking for yararulesproject updates..." + yararulesproject_updates="0" + for db_file in "${yararulesproject_dbs[@]}" ; do + if echo "$db_file" | $grep_bin -q "/" ; then + yr_dir="/$(echo "$db_file" | cut -d "/" -f 1)" + db_file="$(echo "$db_file" | cut -d "/" -f 2)" + else yr_dir="" + fi + if [ "$loop" == "1" ] ; then + xshok_pretty_echo_and_log "---" + fi + xshok_pretty_echo_and_log "Checking for updated yararulesproject database file: ${db_file}" + yararulesproject_db_update="0" + if xshok_file_download "${work_dir_yararulesproject}/${db_file}" "$yararulesproject_url/$yr_dir/${db_file}" ; then + loop="1" + if ! cmp -s "${work_dir_yararulesproject}/${db_file}" "${clam_dbs}/${db_file}" ; then + db_ext="${db_file#*.}" + xshok_pretty_echo_and_log "Testing updated yararulesproject database file: ${db_file}" + if [ -z "$ham_dir" ] || [ "$db_ext" != "ndb" ] ; then + if $clamscan_bin --quiet -d "${work_dir_yararulesproject}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports yararulesproject ${db_file} database integrity tested good" + true + else + xshok_pretty_echo_and_log "Clamscan reports yararulesproject ${db_file} database integrity tested BAD" + if [ "$remove_bad_database" == "yes" ] ; then + if rm -f "${work_dir_yararulesproject}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_yararulesproject}/${db_file}" + fi + fi + false + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${work_dir_yararulesproject}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/${db_file}" + fi + xshok_pretty_echo_and_log "Successfully updated yararulesproject production database file: ${db_file}" + yararulesproject_updates=1 + yararulesproject_db_update=1 + do_clamd_reload=1 + else + xshok_pretty_echo_and_log "Failed to successfully update yararulesproject production database file: ${db_file} - SKIPPING" + fi + else + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${work_dir_yararulesproject}/${db_file}" > "${test_dir}/${db_file}" + $clamscan_bin --infected --no-summary -d "${test_dir}/${db_file}" "$ham_dir"/* | command "$sed_bin" 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "${work_dir_work_configs}/whitelist.txt" + $grep_bin -h -f "${work_dir_work_configs}/whitelist.txt" "${test_dir}/${db_file}" | cut -d "*" -f 2 | sort | uniq >> "${work_dir_work_configs}/whitelist.hex" + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${test_dir}/${db_file}" > "${test_dir}/${db_file}-tmp" + mv -f "${test_dir}/${db_file}-tmp" "${test_dir}/${db_file}" + if $clamscan_bin --quiet -d "${test_dir}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports yararulesproject ${db_file} database integrity tested good" + true + else + xshok_pretty_echo_and_log "Clamscan reports yararulesproject ${db_file} database integrity tested BAD" + if [ "$remove_bad_database" == "yes" ] ; then + if rm -f "${work_dir_yararulesproject}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_yararulesproject}/${db_file}" fi - xshok_pretty_echo_and_log "Successfully updated linuxmalwaredetect production database file: $db_file" - linuxmalwaredetect_updates=1 - linuxmalwaredetect_db_update=1 - do_clamd_reload=1 - else - xshok_pretty_echo_and_log "Failed to successfully update linuxmalwaredetect production database file: $db_file - SKIPPING" fi + false + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${test_dir}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/${db_file}" + fi + xshok_pretty_echo_and_log "Successfully updated yararulesproject production database file: ${db_file}" + yararulesproject_updates=1 + yararulesproject_db_update=1 + do_clamd_reload=1 + else + xshok_pretty_echo_and_log "Failed to successfully update yararulesproject production database file: ${db_file} - SKIPPING" fi fi + fi else - xshok_pretty_echo_and_log "WARNING: Failed connection to $linuxmalwaredetect_url - SKIPPED linuxmalwaredetect $db_file update" + xshok_pretty_echo_and_log "WARNING: Failed connection to $yararulesproject_url - SKIPPED yararulesproject ${db_file} update" fi - if [ "$linuxmalwaredetect_db_update" != "1" ] ; then - - xshok_pretty_echo_and_log "No updated linuxmalwaredetect $db_file database file found" + if [ "$yararulesproject_db_update" != "1" ] ; then + xshok_pretty_echo_and_log "No updated yararulesproject ${db_file} database file" fi done - if [ "$linuxmalwaredetect_updates" != "1" ] ; then - xshok_pretty_echo_and_log "No linuxmalwaredetect database file updates found" "-" + if [ "$yararulesproject_updates" != "1" ] ; then + xshok_pretty_echo_and_log "No yararulesproject database file updates" "-" fi else - xshok_pretty_echo_and_log "linuxmalwaredetect Database File Updates" "=" - xshok_draw_time_remaining "$((update_interval - time_interval))" "$linuxmalwaredetect_update_hours" "linuxmalwaredetect" + + xshok_pretty_echo_and_log "Yara-Rules Database File Updates" "=" + xshok_draw_time_remaining "$((update_interval - time_interval))" "$yararulesproject_update_hours" "yararulesproject" fi fi fi else - if [ -n "${linuxmalwaredetect_dbs[0]}" ] ; then + if [ -n "${yararulesproject_dbs[0]}" ] ; then if [ "$remove_disabled_databases" == "yes" ] ; then - xshok_pretty_echo_and_log "Removing disabled linuxmalwaredetect Database files" - for db_file in "${linuxmalwaredetect_dbs[@]}" ; do - if echo "$db_file" | $grep_bin -q "|"; then + xshok_pretty_echo_and_log "Removing disabled yararulesproject Database files" + for db_file in "${yararulesproject_dbs[@]}" ; do + if echo "$db_file" | $grep_bin -q "/" ; then + db_file="$(echo "$db_file" | cut -d "/" -f 2)" + fi + if echo "$db_file" | $grep_bin -q "|" ; then db_file="${db_file%|*}" fi - if [ -r "$work_dir_linuxmalwaredetect/$db_file" ] ; then - xshok_pretty_echo_and_log "Removing $work_dir_linuxmalwaredetect/$db_file" - rm -f "$work_dir_linuxmalwaredetect/$db_file" - do_clamd_reload=1 + if [ -r "${work_dir_yararulesproject}/${db_file}" ] ; then + rm -f "${work_dir_yararulesproject}/${db_file}" + do_clamd_reload="1" fi - if [ -r "$clam_dbs/$db_file" ] ; then - xshok_pretty_echo_and_log "Removing $clam_dbs/$db_file" - rm -f "$clam_dbs/$db_file" + if [ -r "${clam_dbs}/${db_file}" ] ; then + rm -f "${clam_dbs}/${db_file}" do_clamd_reload=1 fi done @@ -2546,625 +4162,348 @@ else fi fi - -########################################################################################################################################## -# Download MalwarePatrol database file every set number of hours as defined in the "USER CONFIGURATION" section of this script. # -########################################################################################################################################## -if [ "$malwarepatrol_enabled" == "yes" ] ; then - if [ "$malwarepatrol_receipt_code" != "YOUR-RECEIPT-NUMBER" ] ; then - if [ -n "$malwarepatrol_db" ] ; then - if [ -r "$work_dir_work_configs/last-mbl-update.txt" ] ; then - last_malwarepatrol_update="$(cat "$work_dir_work_configs/last-mbl-update.txt")" +############################################################################################################################################## +# Check for updated additional database files every set number of hours as defined in the "USER CONFIGURATION" section of this script +############################################################################################################################################## +if [ "$additional_enabled" == "yes" ] ; then + if [ -n "$additional_dbs" ] ; then + if [ ${#additional_dbs} -lt 1 ] ; then + xshok_pretty_echo_and_log "Failed additional_dbs config is invalid or not defined - SKIPPING" + else + rm -f "${work_dir_add}/*.gz" + if [ -r "${work_dir_work_configs}/last-additional-update.txt" ] ; then + last_additional_update="$(cat "${work_dir_work_configs}/last-additional-update.txt")" else - last_malwarepatrol_update="0" + last_additional_update="0" fi db_file="" - update_interval="$((malwarepatrol_update_hours * 3600))" - time_interval="$((current_time - last_malwarepatrol_update))" + loop="" + update_interval="$((additional_update_hours * 3600))" + time_interval="$((current_time - last_additional_update))" if [ "$time_interval" -ge "$((update_interval - 600))" ] ; then - echo "$current_time" > "$work_dir_work_configs"/last-mbl-update.txt - xshok_pretty_echo_and_log "Checking for MalwarePatrol updates..." - # Delete the old MBL (mbl.db) database file if it exists and start using the newer - # format (mbl.ndb) database file instead. - # test -e $clam_dbs/$malwarepatrol_db -o -e $clam_dbs/$malwarepatrol_db-bak && rm -f -- "$clam_dbs/mbl.d*" + echo "$current_time" > "${work_dir_work_configs}/last-additional-update.txt" - # Remove the .db is the new format if ndb and - # symetrically - if [ "$malwarepatrol_db" == "malwarepatrol.db" ] && [ -f "$clam_dbs/malwarepatrol.ndb" ] ; then - rm "$clam_dbs/malwarepatrol.ndb"; - fi + xshok_pretty_echo_and_log "Additional Database File Updates" "=" + xshok_pretty_echo_and_log "Checking for additional updates..." + additional_updates="0" + for db_url in "${additional_dbs[@]}" ; do + # Left for future dir manipulation + # if echo "$db_file" | $grep_bin -q "/" ; then + # add_dir="/$(echo "$db_file" | cut -d "/" -f 1)" + # db_file="$(echo "$db_file" | cut -d "/" -f 2)" + # else + # add_dir="" + # fi - if [ "$malwarepatrol_db" == "malwarepatrol.ndb" ] && [ -f "$clam_dbs/malwarepatrol.db" ] ; then - rm "$clam_dbs/malwarepatrol.db"; - fi + #cleanup any leading and trailing whitespace. + db_url="$(echo -e "$db_url" | $sed_bin -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - xshok_pretty_echo_and_log "MalwarePatrol $db_file Database File Update" "=" + db_file="$(basename "$db_url")" - malwarepatrol_reloaded=0 - if [ "$malwarepatrol_free" == "yes" ] ; then - xshok_file_download "$work_dir_malwarepatrol/$malwarepatrol_db" "$malwarepatrol_url&receipt=$malwarepatrol_receipt_code" - ret="$?" - if [ "$ret" -eq 0 ] ; then - if ! cmp -s "$work_dir_malwarepatrol/$malwarepatrol_db" "$clam_dbs/$malwarepatrol_db" ; then - if [ $? -eq 0 ] ; then - malwarepatrol_reloaded=1 - else - malwarepatrol_reloaded=2 - fi - fi - else # Wget failed - malwarepatrol_reloaded=-1 + if [ "$loop" == "1" ] ; then + xshok_pretty_echo_and_log "---" fi - else # The not free branch - xshok_file_download "$work_dir_malwarepatrol/$malwarepatrol_db.md5" "$malwarepatrol_url&receipt=$malwarepatrol_receipt_code&hash=1" - ret="$?" + xshok_pretty_echo_and_log "Checking for updated additional database file: ${db_file}" + + additional_db_update="0" + + if [ "${db_url%:*}" == "rsync" ] ; then + # shellcheck disable=SC2086 + $rsync_bin $rsync_output_level $no_motd -ctuz $connect_timeout --timeout="$rsync_max_time" --exclude=*.txt --exclude=*.sha256 --exclude=*.sig --exclude=*.gz "$db_url" "$work_dir_add" 2>&13 + ret="$?" + else + xshok_file_download "${work_dir_add}/${db_file}" "$db_url" + ret="$?" + fi + + # This needs enhancement for rsync, as it will only work with single files... + # Maybe better to process each file inside work_dir_add in its own for loop. if [ "$ret" -eq 0 ] ; then - if [ -f "$clam_dbs/$malwarepatrol_db" ] ; then - malwarepatrol_md5="$(openssl md5 -r "$clam_dbs/$malwarepatrol_db" 2>/dev/null | cut -d " " -f 1)" - if [ ! "$malwarepatrol_md5" ] ; then - # Fallback for missing -r option - malwarepatrol_md5="$(openssl md5 "$clam_dbs/$malwarepatrol_db" 2>/dev/null | cut -d " " -f 2)" - fi - fi - malwarepatrol_md5_new="$(cat "$work_dir_malwarepatrol/$malwarepatrol_db.md5")" - if [ -n "$malwarepatrol_md5_new" ] && [ "$malwarepatrol_md5" != "$malwarepatrol_md5_new" ] ; then - xshok_file_download "$work_dir_malwarepatrol/$malwarepatrol_db" "$malwarepatrol_url&receipt=$malwarepatrol_receipt_code" - ret="$?" - if [ "$ret" -eq 0 ] ; then - malwarepatrol_reloaded="1" - else # Wget DB fail - malwarepatrol_reloaded="-1" - fi # Wget DB - fi # MD5 not equal - else # Wget MD5 fail - malwarepatrol_reloaded="-1" - fi # Wget MD5 - fi - - case "$malwarepatrol_reloaded" in - 1) # Database was updated, need test and reload - xshok_pretty_echo_and_log "Testing updated MalwarePatrol database file: $malwarepatrol_db" - if $clamscan_bin --quiet -d "$work_dir_malwarepatrol/$malwarepatrol_db" "$work_dir_work_configs/scan-test.txt" 2>/dev/null ; then - xshok_pretty_echo_and_log "Clamscan reports MalwarePatrol $malwarepatrol_db database integrity tested good" - true - else - xshok_pretty_echo_and_log "Clamscan reports MalwarePatrol $malwarepatrol_db database integrity tested BAD" - if [ "$remove_bad_database" == "yes" ] ; then - if rm -f "$work_dir_malwarepatrol/$malwarepatrol_db" ; then - xshok_pretty_echo_and_log "Removed invalid database: $work_dir_malwarepatrol/$malwarepatrol_db" - fi - fi - false - fi \ - && ( - test "$keep_db_backup" = "yes" && cp -f "$clam_dbs/$malwarepatrol_db" "$clam_dbs/$malwarepatrol_db-bak" 2>/dev/null - true - ) \ - && if $rsync_bin -pcqt "$work_dir_malwarepatrol/$malwarepatrol_db" "$clam_dbs" 2>/dev/null ; then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/$malwarepatrol_db" + loop="1" + if ! cmp -s "${work_dir_add}/${db_file}" "${clam_dbs}/${db_file}" ; then + db_ext="${db_file#*.}" + xshok_pretty_echo_and_log "Testing updated additional database file: ${db_file}" + if [ -z "$ham_dir" ] || [ "$db_ext" != "ndb" ] ; then + if $clamscan_bin --quiet -d "${work_dir_add}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports additional ${db_file} database integrity tested good" + true + else + xshok_pretty_echo_and_log "Clamscan reports additional ${db_file} database integrity tested BAD" + if [ "$remove_bad_database" == "yes" ] ; then + if rm -f "${work_dir_add}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_add}/${db_file}" + fi + fi + false + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${work_dir_add}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/$malwarepatrol_db" + restorecon "${clam_dbs}/${db_file}" fi - xshok_pretty_echo_and_log "Successfully updated MalwarePatrol production database file: $malwarepatrol_db" + xshok_pretty_echo_and_log "Successfully updated additional production database file: ${db_file}" + additional_updates=1 + additional_db_update=1 do_clamd_reload=1 else - xshok_pretty_echo_and_log "Failed to successfully update MalwarePatrol production database file: $malwarepatrol_db - SKIPPING" + xshok_pretty_echo_and_log "Failed to successfully update additional production database file: ${db_file} - SKIPPING" + fi + else + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${work_dir_add}/${db_file}" > "${test_dir}/${db_file}" + $clamscan_bin --infected --no-summary -d "${test_dir}/${db_file}" "$ham_dir"/* | command "$sed_bin" 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "${work_dir_work_configs}/whitelist.txt" + if [[ "${work_dir_add}/${db_file}" == *.db ]] ; then + $grep_bin -h -f "${work_dir_work_configs}/whitelist.hex" "${test_dir}/${db_file}" | cut -d "=" -f 2 | awk '{ printf("=%s\n", $1);}' |sort | uniq >> "${work_dir_work_configs}/whitelist.hex-tmp" + mv -f "${work_dir_work_configs}/whitelist.hex-tmp" "${work_dir_work_configs}/whitelist.hex" + else + $grep_bin -h -f "${work_dir_work_configs}/whitelist.hex" "${test_dir}/${db_file}" | cut -d "=" -f 2 | sort | uniq >> "${work_dir_work_configs}/whitelist.hex-tmp" + mv -f "${work_dir_work_configs}/whitelist.hex-tmp" "${work_dir_work_configs}/whitelist.hex" fi - ;; # The strange case when $? != 0 in the original - 2) - $grep_bin -h -v -f "$work_dir_work_configs/whitelist.hex" "$work_dir_malwarepatrol/$malwarepatrol_db" > "$test_dir/$malwarepatrol_db" - $clamscan_bin --infected --no-summary -d "$test_dir/$malwarepatrol_db" "$ham_dir"/* | command sed 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "$work_dir_work_configs/whitelist.txt" - $grep_bin -h -f "$work_dir_work_configs/whitelist.txt" "$test_dir/$malwarepatrol_db" | cut -d "*" -f 2 | sort | uniq >> "$work_dir_work_configs/whitelist.hex" - $grep_bin -h -v -f "$work_dir_work_configs/whitelist.hex" "$test_dir/$malwarepatrol_db" > "$test_dir/$malwarepatrol_db-tmp" - mv -f "$test_dir/$malwarepatrol_db-tmp" "$test_dir/$malwarepatrol_db" - if $clamscan_bin --quiet -d "$test_dir/$malwarepatrol_db" "$work_dir_work_configs/scan-test.txt" 2>/dev/null ; then - xshok_pretty_echo_and_log "Clamscan reports MalwarePatrol $malwarepatrol_db database integrity tested good" + $grep_bin -h -v -f "${work_dir_work_configs}/whitelist.hex" "${test_dir}/${db_file}" > "${test_dir}/${db_file}-tmp" + mv -f "${test_dir}/${db_file}-tmp" "${test_dir}/${db_file}" + if $clamscan_bin --quiet -d "${test_dir}/${db_file}" "${work_dir_work_configs}/scan-test.txt" 2>&10 ; then + xshok_pretty_echo_and_log "Clamscan reports additional ${db_file} database integrity tested good" true else - xshok_pretty_echo_and_log "Clamscan reports MalwarePatrol $malwarepatrol_db database integrity tested BAD" + xshok_pretty_echo_and_log "Clamscan reports additional ${db_file} database integrity tested BAD" if [ "$remove_bad_database" == "yes" ] ; then - if rm -f "$test_dir/$malwarepatrol_db" ; then - xshok_pretty_echo_and_log "Removed invalid database: $test_dir/$malwarepatrol_db" + if rm -f "${work_dir_add}/${db_file}" ; then + xshok_pretty_echo_and_log "Removed invalid database: ${work_dir_add}/${db_file}" fi fi false - fi \ - && ( - test "$keep_db_backup" = "yes" && cp -f "$clam_dbs/$malwarepatrol_db" "$clam_dbs/$malwarepatrol_db-bak" 2>/dev/null - true - ) \ - && if $rsync_bin -pcqt "$test_dir/$malwarepatrol_db" "$clam_dbs" 2>/dev/null ; then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/$malwarepatrol_db" - if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/$malwarepatrol_db" - fi - xshok_pretty_echo_and_log "Successfully updated MalwarePatrol production database file: $malwarepatrol_db" - do_clamd_reload=1 - else - xshok_pretty_echo_and_log "Failed to successfully update MalwarePatrol production database file: $malwarepatrol_db - SKIPPING" - fi - ;; - 0) # The database did not update - xshok_pretty_echo_and_log "MalwarePatrol signature database ($malwarepatrol_db) did not change - skipping" - ;; - -1) # Wget failed - xshok_pretty_echo_and_log "WARNING - Failed connection to $malwarepatrol_url - SKIPPED MalwarePatrol $malwarepatrol_db update" - ;; - esac - - else - xshok_pretty_echo_and_log "MalwarePatrol Database File Update" "=" - xshok_draw_time_remaining "$((update_interval - time_interval))" "$malwarepatrol_update_hours" "MalwarePatrol" - fi - fi - fi - else - if [ -n "$malwarepatrol_db" ] ; then - if [ "$remove_disabled_databases" == "yes" ] ; then - xshok_pretty_echo_and_log "Removing disabled MalwarePatrol Database file" - if [ -r "$work_dir_malwarepatrol/$malwarepatrol_db" ] ; then - rm -f "$work_dir_malwarepatrol/$malwarepatrol_db" - do_clamd_reload=1 - fi - if [ -r "$clam_dbs/$malwarepatrol_db" ] ; then - rm -f "$clam_dbs/$malwarepatrol_db" - do_clamd_reload=1 - fi - fi - fi - fi - - ############################################################################################################################################## - # Check for updated yararulesproject database files every set number of hours as defined in the "USER CONFIGURATION" section of this script - ############################################################################################################################################## - if [ "$yararulesproject_enabled" == "yes" ] ; then - if [ -n "${yararulesproject_dbs[0]}" ] ; then - if [ ${#yararulesproject_dbs} -lt 1 ] ; then - xshok_pretty_echo_and_log "Failed yararulesproject_dbs config is invalid or not defined - SKIPPING" - else - rm -f "$work_dir_yararulesproject/*.gz" - if [ -r "$work_dir_work_configs/last-yararulesproject-update.txt" ] ; then - last_yararulesproject_update="$(cat "$work_dir_work_configs/last-yararulesproject-update.txt")" - else - last_yararulesproject_update="0" - fi - db_file="" - loop="" - update_interval="$((yararulesproject_update_hours * 3600))" - time_interval="$((current_time - last_yararulesproject_update))" - if [ "$time_interval" -ge "$((update_interval - 600))" ] ; then - echo "$current_time" > "$work_dir_work_configs/last-yararulesproject-update.txt" - - xshok_pretty_echo_and_log "Yara-Rules Database File Updates" "=" - xshok_pretty_echo_and_log "Checking for yararulesproject updates..." - yararulesproject_updates="0" - for db_file in "${yararulesproject_dbs[@]}" ; do - if echo "$db_file" | $grep_bin -q "/"; then - yr_dir="/$(echo "$db_file" | cut -d "/" -f 1)" - db_file="$(echo "$db_file" | cut -d "/" -f 2)" - else yr_dir="" - fi - if [ "$loop" == "1" ] ; then - xshok_pretty_echo_and_log "---" - fi - xshok_pretty_echo_and_log "Checking for updated yararulesproject database file: $db_file" - yararulesproject_db_update="0" - xshok_file_download "$work_dir_yararulesproject/$db_file" "$yararulesproject_url/$yr_dir/$db_file" - ret="$?" - if [ "$ret" -eq 0 ] ; then - loop="1" - if ! cmp -s "$work_dir_yararulesproject/$db_file" "$clam_dbs/$db_file" ; then - if [ $? -eq 0 ] ; then - db_ext="${db_file#*.}" - - xshok_pretty_echo_and_log "Testing updated yararulesproject database file: $db_file" - if [ -z "$ham_dir" ] || [ "$db_ext" != "ndb" ] ; then - if $clamscan_bin --quiet -d "$work_dir_yararulesproject/$db_file" "$work_dir_work_configs/scan-test.txt" 2>/dev/null - then - xshok_pretty_echo_and_log "Clamscan reports yararulesproject $db_file database integrity tested good" - true - else - xshok_pretty_echo_and_log "Clamscan reports yararulesproject $db_file database integrity tested BAD" - if [ "$remove_bad_database" == "yes" ] ; then - if rm -f "$work_dir_yararulesproject/$db_file" ; then - xshok_pretty_echo_and_log "Removed invalid database: $work_dir_yararulesproject/$db_file" - fi - fi - false - fi && (test "$keep_db_backup" = "yes" && cp -f "$clam_dbs/$db_file" "$clam_dbs/$db_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "$work_dir_yararulesproject/$db_file" "$clam_dbs" 2>/dev/null ; then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/$db_file" - if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/$db_file" - fi - xshok_pretty_echo_and_log "Successfully updated yararulesproject production database file: $db_file" - yararulesproject_updates=1 - yararulesproject_db_update=1 - do_clamd_reload=1 - else - xshok_pretty_echo_and_log "Failed to successfully update yararulesproject production database file: $db_file - SKIPPING" - fi - else - $grep_bin -h -v -f "$work_dir_work_configs/whitelist.hex" "$work_dir_yararulesproject/$db_file" > "$test_dir/$db_file" - $clamscan_bin --infected --no-summary -d "$test_dir/$db_file" "$ham_dir"/* | command sed 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "$work_dir_work_configs/whitelist.txt" - $grep_bin -h -f "$work_dir_work_configs/whitelist.txt" "$test_dir/$db_file" | cut -d "*" -f 2 | sort | uniq >> "$work_dir_work_configs/whitelist.hex" - $grep_bin -h -v -f "$work_dir_work_configs/whitelist.hex" "$test_dir/$db_file" > "$test_dir/$db_file-tmp" - mv -f "$test_dir/$db_file-tmp" "$test_dir/$db_file" - if $clamscan_bin --quiet -d "$test_dir/$db_file" "$work_dir_work_configs/scan-test.txt" 2>/dev/null ; then - xshok_pretty_echo_and_log "Clamscan reports yararulesproject $db_file database integrity tested good" - true - else - xshok_pretty_echo_and_log "Clamscan reports yararulesproject $db_file database integrity tested BAD" - if [ "$remove_bad_database" == "yes" ] ; then - if rm -f "$work_dir_yararulesproject/$db_file" ; then - xshok_pretty_echo_and_log "Removed invalid database: $work_dir_yararulesproject/$db_file" - fi - fi - false - fi && (test "$keep_db_backup" = "yes" && cp -f "$clam_dbs/$db_file" "$clam_dbs/$db_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "$test_dir/$db_file" "$clam_dbs" 2>/dev/null ; then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/$db_file" - if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/$db_file" - fi - xshok_pretty_echo_and_log "Successfully updated yararulesproject production database file: $db_file" - yararulesproject_updates=1 - yararulesproject_db_update=1 - do_clamd_reload=1 - else - xshok_pretty_echo_and_log "Failed to successfully update yararulesproject production database file: $db_file - SKIPPING" - fi - fi + fi && (test "$keep_db_backup" = "yes" && cp -f -p "${clam_dbs}/${db_file}" "${clam_dbs}/${db}_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "${test_dir}/${db_file}" "$clam_dbs" 2>&13 ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/${db_file}" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/${db_file}" fi + xshok_pretty_echo_and_log "Successfully updated additional production database file: ${db_file}" + additional_updates=1 + additional_db_update=1 + do_clamd_reload=1 + else + xshok_pretty_echo_and_log "Failed to successfully update additional production database file: ${db_file} - SKIPPING" fi - else - xshok_pretty_echo_and_log "WARNING: Failed connection to $yararulesproject_url - SKIPPED yararulesproject $db_file update" fi - if [ "$yararulesproject_db_update" != "1" ] ; then - xshok_pretty_echo_and_log "No updated yararulesproject $db_file database file found" - fi - done - if [ "$yararulesproject_updates" != "1" ] ; then - xshok_pretty_echo_and_log "No yararulesproject database file updates found" "-" fi else - - xshok_pretty_echo_and_log "Yara-Rules Database File Updates" "=" - xshok_draw_time_remaining "$((update_interval - time_interval))" "$yararulesproject_update_hours" "yararulesproject" + xshok_pretty_echo_and_log "WARNING: Failed connection to ${db_url} - SKIPPED additional ${db_file} update" + fi + if [ "$additional_db_update" != "1" ] ; then + xshok_pretty_echo_and_log "No updated additional ${db_file} database file" fi + done + if [ "$additional_updates" != "1" ] ; then + xshok_pretty_echo_and_log "No additional database file updates" "-" fi + else + xshok_pretty_echo_and_log "Additional Database File Updates" "=" + xshok_draw_time_remaining "$((update_interval - time_interval))" "$additional_update_hours" "additionaldatabaseupdate" fi - else - if [ -n "${yararulesproject_dbs[0]}" ] ; then - if [ "$remove_disabled_databases" == "yes" ] ; then - xshok_pretty_echo_and_log "Removing disabled yararulesproject Database files" - for db_file in "${yararulesproject_dbs[@]}" ; do - if echo "$db_file" | $grep_bin -q "/"; then - db_file="$(echo "$db_file" | cut -d "/" -f 2)" - fi - if echo "$db_file" | $grep_bin -q "|"; then - db_file="${db_file%|*}" - fi - if [ -r "$work_dir_yararulesproject/$db_file" ] ; then - rm -f "$work_dir_yararulesproject/$db_file" - do_clamd_reload="1" - fi - if [ -r "$clam_dbs/$db_file" ] ; then - rm -f "$clam_dbs/$db_file" - do_clamd_reload=1 - fi - done + fi + fi +else + if [ -n "$additional_dbs" ] ; then + if [ "$remove_disabled_databases" == "yes" ] ; then + xshok_pretty_echo_and_log "Removing disabled additional Database files" + for db_file in "${additional_dbs[@]}" ; do + if echo "$db_file" | $grep_bin -q "/" ; then + db_file="$(echo "$db_file" | cut -d "/" -f 2)" fi - fi + if [ -r "${work_dir_add}/${db_file}" ] ; then + rm -f "${work_dir_add}/${db_file}" + do_clamd_reload=1 + fi + if [ -r "${clam_dbs}/${db_file}" ] ; then + rm -f "${clam_dbs}/${db_file}" + do_clamd_reload=1 + fi + done fi + fi +fi - ############################################################################################################################################## - # Check for updated additional database files every set number of hours as defined in the "USER CONFIGURATION" section of this script - ############################################################################################################################################## - if [ "$additional_enabled" == "yes" ] ; then - if [ -n "$additional_dbs" ] ; then - if [ ${#additional_dbs} -lt 1 ] ; then - xshok_pretty_echo_and_log "Failed additional_dbs config is invalid or not defined - SKIPPING" - else - rm -f "$work_dir_add/*.gz" - if [ -r "$work_dir_work_configs/last-additional-update.txt" ] ; then - last_additional_update="$(cat "$work_dir_work_configs/last-additional-update.txt")" - else - last_additional_update="0" - fi - db_file="" - loop="" - update_interval="$((additional_update_hours * 3600))" - time_interval="$((current_time - last_additional_update))" - if [ "$time_interval" -ge "$((update_interval - 600))" ] ; then - echo "$current_time" > "$work_dir_work_configs/last-additional-update.txt" - - xshok_pretty_echo_and_log "Additional Database File Updates" "=" - xshok_pretty_echo_and_log "Checking for additional updates..." - additional_updates="0" - for db_url in "${additional_dbs[@]}" ; do - # Left for future dir manipulation - # if echo "$db_file" | $grep_bin -q "/"; then - # add_dir="/$(echo "$db_file" | cut -d "/" -f 1)" - # db_file="$(echo "$db_file" | cut -d "/" -f 2)" - # else - # add_dir="" - # fi - - #cleanup any leading and trailing whitespace. - db_url="$(echo -e "$db_url" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - - db_file="$(basename "$db_url")" - - if [ "$loop" == "1" ] ; then - xshok_pretty_echo_and_log "---" - fi - xshok_pretty_echo_and_log "Checking for updated additional database file: $db_file" - - additional_db_update="0" - - if [ "${db_url%:*}" == "rsync" ] ; then - # shellcheck disable=SC2086 - $rsync_bin $rsync_output_level $no_motd -ctuz $connect_timeout --timeout="$rsync_max_time" --exclude=*.txt --exclude=*.sha256 --exclude=*.sig --exclude=*.gz "$db_url" "$work_dir_add" 2>/dev/null - ret="$?" - else - xshok_file_download "$work_dir_add/$db_file" "$db_url" - ret="$?" - fi - - # This needs enhancement for rsync, as it will only work with single files... - # Maybe better to process each file inside work_dir_add in its own for loop. - if [ "$ret" -eq 0 ] ; then - loop="1" - if ! cmp -s "$work_dir_add/$db_file" "$clam_dbs/$db_file" ; then - if [ $? -eq 0 ] ; then - db_ext="${db_file#*.}" - - xshok_pretty_echo_and_log "Testing updated additional database file: $db_file" - if [ -z "$ham_dir" ] || [ "$db_ext" != "ndb" ] ; then - if $clamscan_bin --quiet -d "$work_dir_add/$db_file" "$work_dir_work_configs/scan-test.txt" 2>/dev/null - then - xshok_pretty_echo_and_log "Clamscan reports additional $db_file database integrity tested good" - true - else - xshok_pretty_echo_and_log "Clamscan reports additional $db_file database integrity tested BAD" - if [ "$remove_bad_database" == "yes" ] ; then - if rm -f "$work_dir_add/$db_file" ; then - xshok_pretty_echo_and_log "Removed invalid database: $work_dir_add/$db_file" - fi - fi - false - fi && (test "$keep_db_backup" = "yes" && cp -f "$clam_dbs/$db_file" "$clam_dbs/$db_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "$work_dir_add/$db_file" "$clam_dbs" 2>/dev/null ; then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/$db_file" - if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/$db_file" - fi - xshok_pretty_echo_and_log "Successfully updated additional production database file: $db_file" - additional_updates=1 - additional_db_update=1 - do_clamd_reload=1 - else - xshok_pretty_echo_and_log "Failed to successfully update additional production database file: $db_file - SKIPPING" - fi - else - $grep_bin -h -v -f "$work_dir_work_configs/whitelist.hex" "$work_dir_add/$db_file" > "$test_dir/$db_file" - $clamscan_bin --infected --no-summary -d "$test_dir/$db_file" "$ham_dir"/* | command sed 's/\.UNOFFICIAL FOUND//' | awk '{print $NF}' > "$work_dir_work_configs/whitelist.txt" - $grep_bin -h -f "$work_dir_work_configs/whitelist.txt" "$test_dir/$db_file" | cut -d "*" -f 2 | sort | uniq >> "$work_dir_work_configs/whitelist.hex" - $grep_bin -h -v -f "$work_dir_work_configs/whitelist.hex" "$test_dir/$db_file" > "$test_dir/$db_file-tmp" - mv -f "$test_dir/$db_file-tmp" "$test_dir/$db_file" - if $clamscan_bin --quiet -d "$test_dir/$db_file" "$work_dir_work_configs/scan-test.txt" 2>/dev/null ; then - xshok_pretty_echo_and_log "Clamscan reports additional $db_file database integrity tested good" - true - else - xshok_pretty_echo_and_log "Clamscan reports additional $db_file database integrity tested BAD" - if [ "$remove_bad_database" == "yes" ] ; then - if rm -f "$work_dir_add/$db_file" ; then - xshok_pretty_echo_and_log "Removed invalid database: $work_dir_add/$db_file" - fi - fi - false - fi && (test "$keep_db_backup" = "yes" && cp -f "$clam_dbs/$db_file" "$clam_dbs/$db_file-bak" 2>/dev/null ; true) && if $rsync_bin -pcqt "$test_dir/$db_file" "$clam_dbs" 2>/dev/null ; then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/$db_file" - if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/$db_file" - fi - xshok_pretty_echo_and_log "Successfully updated additional production database file: $db_file" - additional_updates=1 - additional_db_update=1 - do_clamd_reload=1 - else - xshok_pretty_echo_and_log "Failed to successfully update additional production database file: $db_file - SKIPPING" - fi - fi - fi - fi - else - xshok_pretty_echo_and_log "WARNING: Failed connection to $db_url - SKIPPED additional $db_file update" - fi - if [ "$additional_db_update" != "1" ] ; then - xshok_pretty_echo_and_log "No updated additional $db_file database file found" - fi - done - if [ "$additional_updates" != "1" ] ; then - xshok_pretty_echo_and_log "No additional database file updates found" "-" - fi - else - xshok_pretty_echo_and_log "Additional Database File Updates" "=" - xshok_draw_time_remaining "$((update_interval - time_interval))" "$additional_update_hours" "additionaldatabaseupdate" - fi - fi +################################################### +# Generate whitelists +################################################### +# Check to see if the local.ign file exists, and if it does, check to see if any of the script +# added bypass entries can be removed due to offending signature modifications or removals. +if [ -r "${clam_dbs}/local.ign" ] && [ -s "${work_dir_work_configs}/monitor-ign.txt" ] ; then + ign_updated=0 + cd "$clam_dbs" || exit + cp -f -p local.ign "${work_dir_work_configs}/local.ign" + cp -f -p "${work_dir_work_configs}/monitor-ign.txt" "${work_dir_work_configs}/monitor-ign-old.txt" + + xshok_pretty_echo_and_log "" "=" "80" + while read -r entry ; do + sig_file="$(echo "$entry" | tr -d "\\r" | awk -F ":" '{print $1}')" + sig_hex="$(echo "$entry" | tr -d "\\r" | awk -F ":" '{print $NF}')" + sig_name_old="$(echo "$entry" | tr -d "\\r" | awk -F ":" '{print $3}')" + sig_ign_old="$($grep_bin ":$sig_name_old" "${work_dir_work_configs}/local.ign")" + sig_old="$(echo "$entry" | tr -d "\\r" | cut -d ":" -f 3-)" + sig_new="$($grep_bin -hwF ":$sig_hex" "$sig_file" | tr -d "\\r" 2>/dev/null)" + sig_mon_new="$($grep_bin -HwF -n ":$sig_hex" "$sig_file" | tr -d "\\r")" + if [ -n "$sig_new" ] ; then + if [ "$sig_old" != "$sig_new" ] || [ "$entry" != "$sig_mon_new" ] ; then + sig_name_new="$(echo "$sig_new" | tr -d "\\r" | awk -F ":" '{print $1}')" + sig_ign_new="$(echo "$sig_mon_new" | cut -d ":" -f 1-3)" + perl -i -ne "print unless /$sig_ign_old/" "${work_dir_work_configs}/monitor-ign.txt" + echo "$sig_mon_new" >> "${work_dir_work_configs}/monitor-ign.txt" + perl -p -i -e "s/$sig_ign_old/$sig_ign_new/" "${work_dir_work_configs}/local.ign" + xshok_pretty_echo_and_log "${sig_name_old} hexadecimal signature is unchanged, however signature name and/or line placement" + xshok_pretty_echo_and_log "in ${sig_file} has changed to ${sig_name_new} - updated local.ign to reflect this change." + ign_updated=1 fi else - if [ -n "$additional_dbs" ] ; then - if [ "$remove_disabled_databases" == "yes" ] ; then - xshok_pretty_echo_and_log "Removing disabled additional Database files" - for db_file in "${additional_dbs[@]}" ; do - if echo "$db_file" | $grep_bin -q "/"; then - db_file="$(echo "$db_file" | cut -d "/" -f 2)" - fi - if [ -r "$work_dir_add/$db_file" ] ; then - rm -f "$work_dir_add/$db_file" - do_clamd_reload=1 - fi - if [ -r "$clam_dbs/$db_file" ] ; then - rm -f "$clam_dbs/$db_file" - do_clamd_reload=1 - fi - done - fi - fi - fi + perl -i -ne "print unless /$sig_ign_old/" "${work_dir_work_configs}/monitor-ign.txt" "${work_dir_work_configs}/local.ign" - ################################################### - # Generate whitelists - ################################################### - # Check to see if the local.ign file exists, and if it does, check to see if any of the script - # added bypass entries can be removed due to offending signature modifications or removals. - if [ -r "$clam_dbs/local.ign" ] && [ -s "$work_dir_work_configs/monitor-ign.txt" ] ; then - ign_updated=0 - cd "$clam_dbs" || exit - cp -f local.ign "$work_dir_work_configs/local.ign" - cp -f "$work_dir_work_configs/monitor-ign.txt" "$work_dir_work_configs/monitor-ign-old.txt" - - xshok_pretty_echo_and_log "" "=" "80" - while read -r entry ; do - sig_file="$(echo "$entry" | tr -d "\r" | awk -F ":" '{print $1}')" - sig_hex="$(echo "$entry" | tr -d "\r" | awk -F ":" '{print $NF}')" - sig_name_old="$(echo "$entry" | tr -d "\r" | awk -F ":" '{print $3}')" - sig_ign_old="$($grep_bin ":$sig_name_old" "$work_dir_work_configs/local.ign")" - sig_old="$(echo "$entry" | tr -d "\r" | cut -d ":" -f 3-)" - sig_new="$($grep_bin -hwF ":$sig_hex" "$sig_file" | tr -d "\r" 2>/dev/null)" - sig_mon_new="$($grep_bin -HwF -n ":$sig_hex" "$sig_file" | tr -d "\r")" - if [ -n "$sig_new" ] ; then - if [ "$sig_old" != "$sig_new" ] || [ "$entry" != "$sig_mon_new" ] ; then - sig_name_new="$(echo "$sig_new" | tr -d "\r" | awk -F ":" '{print $1}')" - sig_ign_new="$(echo "$sig_mon_new" | cut -d ":" -f 1-3)" - perl -i -ne "print unless /$sig_ign_old/" "$work_dir_work_configs/monitor-ign.txt" - echo "$sig_mon_new" >> "$work_dir_work_configs/monitor-ign.txt" - perl -p -i -e "s/$sig_ign_old/$sig_ign_new/" "$work_dir_work_configs/local.ign" - xshok_pretty_echo_and_log "$sig_name_old hexadecimal signature is unchanged, however signature name and/or line placement" - xshok_pretty_echo_and_log "in $sig_file has changed to $sig_name_new - updated local.ign to reflect this change." - ign_updated=1 - fi - else - perl -i -ne "print unless /$sig_ign_old/" "$work_dir_work_configs/monitor-ign.txt" "$work_dir_work_configs/local.ign" - - xshok_pretty_echo_and_log "$sig_name_old signature has been removed from $sig_file, entry removed from local.ign." - ign_updated=1 - fi - done < "$work_dir_work_configs/monitor-ign-old.txt" - if [ "$ign_updated" == "1" ] ; then - if $clamscan_bin --quiet -d "$work_dir_work_configs/local.ign" "$work_dir_work_configs/scan-test.txt" - then - if $rsync_bin -pcqt "$work_dir_work_configs/local.ign" "$clam_dbs" - then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/local.ign" - perms chmod -f 0644 "$clam_dbs/local.ign" "$work_dir_work_configs/monitor-ign.txt" - if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/local.ign" - fi - do_clamd_reload=3 - else - xshok_pretty_echo_and_log "Failed to successfully update local.ign file - SKIPPING" - fi - else - xshok_pretty_echo_and_log "Clamscan reports local.ign database integrity is bad - SKIPPING" + xshok_pretty_echo_and_log "${sig_name_old} signature has been removed from ${sig_file}, entry removed from local.ign." + ign_updated=1 + fi + done < "${work_dir_work_configs}/monitor-ign-old.txt" + if [ "$ign_updated" == "1" ] ; then + if $clamscan_bin --quiet -d "${work_dir_work_configs}/local.ign" "${work_dir_work_configs}/scan-test.txt" ; then + if $rsync_bin -pcqt "${work_dir_work_configs}/local.ign" "$clam_dbs" ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/local.ign" + perms chmod -f 0644 "${clam_dbs}/local.ign" "${work_dir_work_configs}/monitor-ign.txt" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/local.ign" fi + do_clamd_reload=3 else - xshok_pretty_echo_and_log "No whitelist signature changes found in local.ign" "=" + xshok_pretty_echo_and_log "Failed to successfully update local.ign file - SKIPPING" fi + else + xshok_pretty_echo_and_log "Clamscan reports local.ign database integrity is bad - SKIPPING" fi + else + xshok_pretty_echo_and_log "No whitelist signature changes found in local.ign" "=" + fi +fi - # Check to see if my-whitelist.ign2 file exists, and if it does, check to see if any of the script - # added whitelist entries can be removed due to offending signature modifications or removals. - if [ -r "$clam_dbs/my-whitelist.ign2" ] && [ -s "$work_dir_work_configs/tracker.txt" ] ; then - ign2_updated=0 - cd "$clam_dbs" || exit - cp -f my-whitelist.ign2 "$work_dir_work_configs/my-whitelist.ign2" +# Check to see if my-whitelist.ign2 file exists, and if it does, check to see if any of the script +# added whitelist entries can be removed due to offending signature modifications or removals. +if [ -r "${clam_dbs}/my-whitelist.ign2" ] && [ -s "${work_dir_work_configs}/tracker.txt" ] ; then + ign2_updated=0 + cd "$clam_dbs" || exit + cp -f -p my-whitelist.ign2 "${work_dir_work_configs}/my-whitelist.ign2" - xshok_pretty_echo_and_log "" "=" "80" + xshok_pretty_echo_and_log "" "=" "80" + touch "${work_dir_work_configs}/tracker-tmp.txt" + while read -r entry ; do - while read -r entry ; do + yaratest="$(echo "$entry" | cut -d "." -f 1)" + shopt -s nocasematch + if [ "$yaratest" != "YARA" ] ; then sig_file="$(echo "$entry" | cut -d ":" -f 1)" sig_full="$(echo "$entry" | cut -d ":" -f 2-)" sig_name="$(echo "$entry" | cut -d ":" -f 2)" if ! $grep_bin -F "$sig_full" "$sig_file" > /dev/null 2>&1 ; then - perl -i -ne "print unless /$sig_name$/" "$work_dir_work_configs/my-whitelist.ign2" - perl -i -ne "print unless /:$sig_name:/" "$work_dir_work_configs/tracker-tmp.txt" - - xshok_pretty_echo_and_log "$sig_name signature no longer exists in $sig_file, whitelist entry removed from my-whitelist.ign2" + perl -i -ne "print unless /$sig_name$/" "${work_dir_work_configs}/my-whitelist.ign2" + perl -i -ne "print unless /:$sig_name:/" "${work_dir_work_configs}/tracker-tmp.txt" + xshok_pretty_echo_and_log "${sig_name} signature no longer exists in ${sig_file}, whitelist entry removed from my-whitelist.ign2" ign2_updated="1" fi - done < "$work_dir_work_configs/tracker.txt" - mv -f "$work_dir_work_configs/tracker-tmp.txt" "$work_dir_work_configs/tracker.txt" - - xshok_pretty_echo_and_log "" "=" "80" - if [ "$ign2_updated" == "1" ] - then - if $clamscan_bin --quiet -d "$work_dir_work_configs/my-whitelist.ign2" "$work_dir_work_configs/scan-test.txt" - then - if $rsync_bin -pcqt "$work_dir_work_configs/my-whitelist.ign2" "$clam_dbs" - then - perms chown -f "$clam_user:$clam_group" "$clam_dbs/my-whitelist.ign2" - perms chmod -f 0644 "$clam_dbs/my-whitelist.ign2" "$work_dir_work_configs/tracker.txt" - if [ "$selinux_fixes" == "yes" ] ; then - restorecon "$clam_dbs/my-whitelist.ign2" - restorecon "$work_dir_work_configs/tracker.txt" - fi - do_clamd_reload=4 - else - xshok_pretty_echo_and_log "Failed to successfully update my-whitelist.ign2 file - SKIPPING" - fi - else - xshok_pretty_echo_and_log "Clamscan reports my-whitelist.ign2 database integrity is bad - SKIPPING" - fi - else - xshok_pretty_echo_and_log "No whitelist signature changes found in my-whitelist.ign2" - fi fi + done < "${work_dir_work_configs}/tracker.txt" + if [ -f "${work_dir_work_configs}/tracker-tmp.txt" ] ; then + mv -f "${work_dir_work_configs}/tracker-tmp.txt" "${work_dir_work_configs}/tracker.txt" + fi - # Check for non-matching whitelist.hex signatures and remove them from the whitelist file (signature modified or removed). - if [ -n "$ham_dir" ] ; then - if [ -r "$work_dir_work_configs/whitelist.hex" ] ; then - $grep_bin -h -f "$work_dir_work_configs/whitelist.hex" "$work_dir"/*/*.ndb | cut -d "*" -f 2 | tr -d "\r" | sort | uniq > "$work_dir_work_configs/whitelist.tmp" - mv -f "$work_dir_work_configs/whitelist.tmp" "$work_dir_work_configs/whitelist.hex" - rm -f "$work_dir_work_configs/whitelist.txt" - rm -f "$test_dir"/*.* - xshok_pretty_echo_and_log "WARNING: Signature(s) triggered on HAM directory scan - signature(s) removed" "*" + + xshok_pretty_echo_and_log "" "=" "80" + if [ "$ign2_updated" == "1" ] ; then + if $clamscan_bin --quiet -d "${work_dir_work_configs}/my-whitelist.ign2" "${work_dir_work_configs}/scan-test.txt" ; then + if $rsync_bin -pcqt "${work_dir_work_configs}/my-whitelist.ign2" "$clam_dbs" ; then + perms chown -f "${clam_user}:${clam_group}" "${clam_dbs}/my-whitelist.ign2" + perms chmod -f 0644 "${clam_dbs}/my-whitelist.ign2" "${work_dir_work_configs}/tracker.txt" + if [ "$selinux_fixes" == "yes" ] ; then + restorecon "${clam_dbs}/my-whitelist.ign2" + restorecon "${work_dir_work_configs}/tracker.txt" + fi + do_clamd_reload=4 else - xshok_pretty_echo_and_log "No signatures triggered on HAM directory scan" "=" + xshok_pretty_echo_and_log "Failed to successfully update my-whitelist.ign2 file - SKIPPING" fi + else + xshok_pretty_echo_and_log "Clamscan reports my-whitelist.ign2 database integrity is bad - SKIPPING" fi + else + xshok_pretty_echo_and_log "No whitelist signature changes found in my-whitelist.ign2" + fi +fi - # Set appropriate directory and file permissions to all production signature files - # and set file access mode to 0644 on all working directory files. - - if [ "$setmode" == "yes" ] ; then - xshok_pretty_echo_and_log "Setting permissions and ownership" "=" - perms chown -f -R "$clam_user:$clam_group" "$work_dir" - if ! find "$work_dir" -type f -exec chmod -f 0644 "{}" "+" 2>/dev/null ; then - if ! find "$work_dir" -type f -print0 | xargs -0 chmod -f 0644 2>/dev/null ; then - find "$work_dir" -type f -exec chmod -f 0644 "{}" ";" - fi - fi +# Check for non-matching whitelist.hex signatures and remove them from the whitelist file (signature modified or removed). +if [ -n "$ham_dir" ] ; then + if [ -r "${work_dir_work_configs}/whitelist.hex" ] ; then + $grep_bin -h -f "${work_dir_work_configs}/whitelist.hex" "$work_dir"/*/*.ndb | cut -d "*" -f 2 | tr -d "\\r" | sort | uniq > "${work_dir_work_configs}/whitelist.tmp" + $grep_bin -h -f "${work_dir_work_configs}/whitelist.hex" "$work_dir"/*/*.db | cut -d "=" -f 2 | awk '{ printf("=%s\n", $1);}' | sort | uniq >> "${work_dir_work_configs}/whitelist.tmp" + mv -f "${work_dir_work_configs}/whitelist.tmp" "${work_dir_work_configs}/whitelist.hex" + rm -f "${work_dir_work_configs}/whitelist.txt" + rm -f "${test_dir}/*.*" + xshok_pretty_echo_and_log "WARNING: Signature(s) triggered on HAM directory scan - signature(s) removed" + else + xshok_pretty_echo_and_log "No signatures triggered on HAM directory scan" "=" + fi +fi +# Set appropriate directory and file permissions to all production signature files +# and set file access mode to 0644 on all working directory files. + +if [ "$setmode" == "yes" ] ; then + xshok_pretty_echo_and_log "Setting permissions and ownership" "=" + perms chown -f -R "${clam_user}:${clam_group}" "$work_dir" + if ! find "$work_dir" -type f -exec chmod -f 0644 "{}" "+" 2>/dev/null ; then + if ! find "$work_dir" -type f -print0 | xargs -0 chmod -f 0644 2>/dev/null ; then + find "$work_dir" -type f -exec chmod -f 0644 "{}" ";" + fi + fi - # If enabled, set file access mode for all production signature database files to 0644. - perms chown -f -R "$clam_user:$clam_group" "$clam_dbs" - if ! find "$clam_dbs" -type f -exec chmod -f 0644 "{}" "+" 2>/dev/null ; then - if ! find "$clam_dbs" -type f -print0 | xargs -0 chmod -f 0644 2>/dev/null ; then - find "$clam_dbs" -type f -exec chmod -f 0644 "{}" ";" - fi - fi + # If enabled, set file access mode for all production signature database files to 0644. + perms chown -f -R "${clam_user}:${clam_group}" "$clam_dbs" + if ! find "$clam_dbs" -type f -exec chmod -f 0644 "{}" "+" 2>/dev/null ; then + if ! find "$clam_dbs" -type f -print0 | xargs -0 chmod -f 0644 2>/dev/null ; then + find "$clam_dbs" -type f -exec chmod -f 0644 "{}" ";" fi + fi +fi # Reload all clamd databases clamscan_reload_dbs xshok_pretty_echo_and_log "Issue tracker : https://github.com/extremeshok/clamav-unofficial-sigs/issues" "-" -check_new_version +if [ "$allow_update_checks" != "no" ] ; then + + if [ -r "${work_dir_work_configs}/last-version-check.txt" ] ; then + last_version_check="$(cat "${work_dir_work_configs}/last-version-check.txt")" + else + last_version_check="0" + fi + db_file="" + update_check_interval="$((update_check_hours * 3600))" + time_interval="$((current_time - last_version_check))" + if [ "$time_interval" -ge $((update_check_interval - 600)) ] ; then + echo "$current_time" > "${work_dir_work_configs}/last-version-check.txt" + if xshok_is_root ; then + perms chown -f "${clam_user}:${clam_group}" "${work_dir_work_configs}/last-version-check.txt" + fi + check_new_version + fi -check_new_config_version +fi xshok_cleanup +# Set the permission of the log file, to fix any permission errors, this is done to fix cron errors after running the script as root. +if xshok_is_root ; then + if [ "$enable_log" == "yes" ] ; then + # check if the file is owned by root (the current user) + if [ -O "${log_file_path}/${log_file_name}" ] ; then + # checks the file is writable and a file (not a symlink/link) + if [ -w "${log_file_path}/${log_file_name}" ] && [ -f "${log_file_path}/${log_file_name}" ] ; then + perms chown -f "${clam_user}:${clam_group}" "${log_file_path}/${log_file_name}" + fi + fi + fi +fi + # And lastly we exit, Note: the exit is always on the 2nd last line exit $? diff --git a/rootfs/usr/local/bin/setup.sh b/rootfs/usr/local/bin/setup.sh index 725555b6..b5eb479d 100644 --- a/rootfs/usr/local/bin/setup.sh +++ b/rootfs/usr/local/bin/setup.sh @@ -777,9 +777,9 @@ if [ -f "/var/mail/clamav-unofficial-sigs/user.conf" ]; then echo "[INFO] clamav-unofficial-sigs is enabled (user configuration found)" rm -rf /var/lib/clamav-unofficial-sigs ln -s /var/mail/clamav-unofficial-sigs /var/lib/clamav-unofficial-sigs - cp -f /var/mail/clamav-unofficial-sigs/user.conf /etc/clamav/unofficial-sigs + cp -f /var/mail/clamav-unofficial-sigs/user.conf /etc/clamav-unofficial-sigs mkdir -p /var/log/clamav-unofficial-sigs - clamav-unofficial-sigs.sh --install-cron &>/dev/null + clamav-unofficial-sigs.sh --install-cron &>/dev/null clamav-unofficial-sigs.sh --install-logrotate &>/dev/null else echo "[INFO] clamav-unofficial-sigs is disabled (user configuration not found)" diff --git a/test/share/clamav/unofficial-sigs/user.conf b/test/share/clamav-unofficial-sigs/user.conf similarity index 100% rename from test/share/clamav/unofficial-sigs/user.conf rename to test/share/clamav-unofficial-sigs/user.conf From 00a1964f55542dbe6986aa1d4fdb090ee8387639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Tue, 16 May 2023 00:38:44 +0200 Subject: [PATCH 40/50] Update base image to mailserver2/debian-mail-overlay:1.0.11 (#54) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 98d51e0b..2e4ce421 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.10 +FROM mailserver2/debian-mail-overlay:1.0.11 LABEL description="Simple and full-featured mail server using Docker" From e10532a2261ae10d87dae4fbc36232538e005a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Fri, 1 Sep 2023 15:22:55 +0200 Subject: [PATCH 41/50] Update to Debian Bookworm (mailserver2/debian-mail-overlay:1.0.12) (#56) * Install python3-watchdog package instead of pip3 install * Update to base image 1.0.12. Small changes to Dockerfile for debian bookworm. Fix a few failing tests. * Fix waiting for containers. Wait until a successful connection is established. --- Dockerfile | 9 ++++----- Makefile | 14 ++++++++------ test/default.bats | 4 ++-- test/ecdsa.bats | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2e4ce421..e13d58ac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.11 +FROM mailserver2/debian-mail-overlay:1.0.12 LABEL description="Simple and full-featured mail server using Docker" @@ -10,8 +10,8 @@ RUN apt-get update && apt-get install -y -q --no-install-recommends \ dovecot-core dovecot-imapd dovecot-lmtpd dovecot-pgsql dovecot-mysql dovecot-ldap dovecot-sieve dovecot-managesieved dovecot-pop3d \ fetchmail libdbi-perl libdbd-pg-perl libdbd-mysql-perl liblockfile-simple-perl \ clamav clamav-daemon \ - python3-pip python3-setuptools python3-wheel python3-gpg \ - rsyslog dnsutils curl unbound jq rsync \ + python3-pip python3-setuptools python3-wheel python3-gpg python3-watchdog \ + rsyslog dnsutils curl unbound unbound-anchor jq rsync \ inotify-tools \ # To enable compression in imap arj bzip2 cabextract cpio file gzip nomarch pax unzip zip \ @@ -19,8 +19,7 @@ RUN apt-get update && apt-get install -y -q --no-install-recommends \ && ln -s /var/mail/postfix/spool /var/spool/postfix \ && apt-get autoremove -y \ && apt-get clean \ - && rm -rf /tmp/* /var/lib/apt/lists/* /var/cache/debconf/*-old \ - && pip3 install watchdog + && rm -rf /tmp/* /var/lib/apt/lists/* /var/cache/debconf/*-old EXPOSE 25 143 465 587 993 4190 11334 COPY rootfs / diff --git a/Makefile b/Makefile index 1cd73bb2..429b399b 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ init_redis: docker run \ -d \ --name redis \ - -t redis:6.2-alpine + -t redis:7.0-alpine sleep 10 init_mariadb: @@ -51,7 +51,7 @@ init_mariadb: -e MYSQL_PASSWORD=testpasswd \ -v "`pwd`/test/config/mariadb/struct.sql":/docker-entrypoint-initdb.d/struct.sql \ -v "`pwd`/test/config/mariadb/bind.cnf":/etc/mysql/conf.d/bind.cnf \ - -t mysql:5.7 + -t mysql:8 init_postgres: -docker rm -f \ @@ -198,7 +198,8 @@ init_ldap2: init_openldap init_redis -h mail.domain.tld \ -t $(NAME) fixtures_ldap2: - sleep 20 + docker exec mailserver_ldap2 /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 25 ; do sleep 1 ; done" + sleep 30 docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-user.txt" docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-valid-user-subaddress.txt" docker exec mailserver_ldap2 /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-non-existing-user.txt" @@ -282,6 +283,7 @@ init_reverse: init_redis init_postgres -v "`pwd`/test/share/letsencrypt":/etc/letsencrypt \ -t $(NAME) fixtures_reverse: + docker exec mailserver_reverse /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 25 ; do sleep 1 ; done" sleep 30 docker exec mailserver_reverse /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-existing-user.txt" docker exec mailserver_reverse /bin/sh -c "nc 0.0.0.0 25 < /tmp/tests/email-templates/external-to-valid-user-subaddress-with-default-separator.txt" @@ -318,8 +320,8 @@ init_ecdsa: init_redis init_mariadb -v "`pwd`/test/share/postfix/custom.ecdsa.conf":/var/mail/postfix/custom.conf \ -h mail.domain.tld \ -t $(NAME) - sleep 10 run_ecdsa: + docker exec mailserver_ecdsa /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 587 ; do sleep 1 ; done" ./test/bats/bin/bats test/ecdsa.bats stop_ecdsa: -docker rm -f \ @@ -344,7 +346,7 @@ init_traefik_acmev1: init_redis init_mariadb -h mail.domain.tld \ -t $(NAME) run_traefik_acmev1: - sleep 20 + docker exec mailserver_traefik_acmev1 /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 587 ; do sleep 1 ; done" ./test/bats/bin/bats test/traefik_acmev1.bats stop_traefik_acmev1: -docker rm -f \ @@ -369,7 +371,7 @@ init_traefik_acmev2: init_redis init_mariadb -h mail.domain.tld \ -t $(NAME) run_traefik_acmev2: - sleep 20 + docker exec mailserver_traefik_acmev2 /bin/sh -c "while ! echo PING | nc -z 0.0.0.0 587 ; do sleep 1 ; done" ./test/bats/bin/bats test/traefik_acmev2.bats stop_traefik_acmev2: -docker rm -f \ diff --git a/test/default.bats b/test/default.bats index 0a4d86ac..809acaec 100644 --- a/test/default.bats +++ b/test/default.bats @@ -626,7 +626,7 @@ load 'test_helper/bats-assert/load' # @test "checking clamav: TCP Bound to 3310 port" { - run docker exec mailserver_default grep -i 'TCP: Bound to \[0.0.0.0\]:3310' /var/log/mail.log + run docker exec mailserver_default grep -i 'TCP: Bound to \[\]:3310' /var/log/mail.log assert_success } @@ -804,7 +804,7 @@ load 'test_helper/bats-assert/load' # @test "checking ssl: generated default cert works correctly" { - run docker exec mailserver_default /bin/sh -c "timeout 1 openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp | grep 'Verify return code: 18 (self signed certificate)'" + run docker exec mailserver_default /bin/sh -c "timeout 1 openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp | grep 'Verify return code: 18 (self-signed certificate)'" assert_success } diff --git a/test/ecdsa.bats b/test/ecdsa.bats index cc77449f..6055888d 100644 --- a/test/ecdsa.bats +++ b/test/ecdsa.bats @@ -26,7 +26,7 @@ load 'test_helper/bats-assert/load' # @test "checking ssl: ECDSA P-384 cert works correctly" { - run docker exec mailserver_ecdsa /bin/sh -c "timeout 1 openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp | grep 'Verify return code: 18 (self signed certificate)'" + run docker exec mailserver_ecdsa /bin/sh -c "timeout 1 openssl s_client -ign_eof -connect 0.0.0.0:587 -starttls smtp | grep 'Verify return code: 18 (self-signed certificate)'" assert_success } From 26c8ab63404198c297a02fe29a9ba9fe81b2fb92 Mon Sep 17 00:00:00 2001 From: Andrew Savinykh <658865+AndrewSav@users.noreply.github.com> Date: Fri, 19 Jan 2024 18:55:00 +1300 Subject: [PATCH 42/50] Updated base image to https://github.com/mailserver2/debian-mail-overlay/releases/tag/v1.0.14 * Updated base image to https://github.com/mailserver2/debian-mail-overlay/releases/tag/v1.0.14 * Update rsyslog.conf to ignore some rspamd warnings for tests (#61) * add smtp smuggling mitigation * update checkout action from v2 to v3 --------- Co-authored-by: diroots <30463065+diroots@users.noreply.github.com> --- .github/workflows/default.yml | 2 +- .github/workflows/ecdsa.yml | 2 +- .github/workflows/ldap.yml | 2 +- .github/workflows/ldap2.yml | 2 +- .github/workflows/master.yml | 2 +- .github/workflows/reverse.yml | 2 +- .github/workflows/sieve.yml | 2 +- .github/workflows/traefik_acmev1.yml | 2 +- .github/workflows/traefik_acmev2.yml | 2 +- Dockerfile | 2 +- rootfs/etc/postfix/main.cf | 12 ++++++++++++ rootfs/etc/rsyslog/rsyslog.conf | 3 +++ 12 files changed, 25 insertions(+), 10 deletions(-) diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 5c56a468..6c232734 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout repository and submodules - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive diff --git a/.github/workflows/ecdsa.yml b/.github/workflows/ecdsa.yml index 231a730b..ae6d8b29 100644 --- a/.github/workflows/ecdsa.yml +++ b/.github/workflows/ecdsa.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout repository and submodules - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive diff --git a/.github/workflows/ldap.yml b/.github/workflows/ldap.yml index 5162ae7b..910a18ae 100644 --- a/.github/workflows/ldap.yml +++ b/.github/workflows/ldap.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout repository and submodules - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive diff --git a/.github/workflows/ldap2.yml b/.github/workflows/ldap2.yml index 5ffd807f..89acc85e 100644 --- a/.github/workflows/ldap2.yml +++ b/.github/workflows/ldap2.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout repository and submodules - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 7eadc88c..cc2102a2 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout repository and submodules - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive diff --git a/.github/workflows/reverse.yml b/.github/workflows/reverse.yml index 5a7e045f..9ce33da1 100644 --- a/.github/workflows/reverse.yml +++ b/.github/workflows/reverse.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout repository and submodules - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive diff --git a/.github/workflows/sieve.yml b/.github/workflows/sieve.yml index 51c5e3b5..0109f015 100644 --- a/.github/workflows/sieve.yml +++ b/.github/workflows/sieve.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout repository and submodules - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive diff --git a/.github/workflows/traefik_acmev1.yml b/.github/workflows/traefik_acmev1.yml index 0532d090..fb64984e 100644 --- a/.github/workflows/traefik_acmev1.yml +++ b/.github/workflows/traefik_acmev1.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout repository and submodules - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive diff --git a/.github/workflows/traefik_acmev2.yml b/.github/workflows/traefik_acmev2.yml index d7ea87b2..802642ba 100644 --- a/.github/workflows/traefik_acmev2.yml +++ b/.github/workflows/traefik_acmev2.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout repository and submodules - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive diff --git a/Dockerfile b/Dockerfile index e13d58ac..18b6d511 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.12 +FROM mailserver2/debian-mail-overlay:1.0.14 LABEL description="Simple and full-featured mail server using Docker" diff --git a/rootfs/etc/postfix/main.cf b/rootfs/etc/postfix/main.cf index beac1cda..eb425c9e 100644 --- a/rootfs/etc/postfix/main.cf +++ b/rootfs/etc/postfix/main.cf @@ -25,6 +25,18 @@ mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 {{ .RELAY_NETWORKS alias_maps = hash:/etc/aliases + +################################ +## SMTP smuggling mitigation ## +################################ +smtpd_forbid_bare_newline = yes +smtpd_forbid_bare_newline_exclusions = $mynetworks + +# https://www.postfix.org/smtp-smuggling.html#long +# Optionally disconnect remote SMTP clients that send bare newlines, +# but allow local clients with non-standard SMTP implementations +# such as netcat, fax machines, or load balancer health checks. + ############### ## SMTP/UTF8 ## ############### diff --git a/rootfs/etc/rsyslog/rsyslog.conf b/rootfs/etc/rsyslog/rsyslog.conf index b9ddf549..ab95cfa7 100644 --- a/rootfs/etc/rsyslog/rsyslog.conf +++ b/rootfs/etc/rsyslog/rsyslog.conf @@ -7,6 +7,9 @@ $IncludeConfig /etc/rsyslog.d/*.conf # https://github.com/vstakhov/rspamd/issues/1693 :msg,contains,"map file is unavailable for reading" ~ :msg,contains,"cannot load controller stats from /var/mail/rspamd/stats.ucl" ~ +:msg,contains,"rspamd_register_symbol_fromlua: duplicate symbol" ~ +:msg,contains,"trying to add virtual symbol MID" ~ +:msg,contains,"init of /usr/share/rspamd/lualib/lua_ffi/spf.lua failed" ~ :msg,contains,"database is locked" ~ :msg,contains,"http error occurred: IO read error: unexpected EOF" ~ :msg,contains,"http error occurred: Not found" ~ From fd944a75b42ec10837e335bb53e56bbe6413c4aa Mon Sep 17 00:00:00 2001 From: Kayvan Sylvan Date: Mon, 11 Mar 2024 06:47:19 -0700 Subject: [PATCH 43/50] Add option to have ClamAV clamd use less memory on startup (#64) --- README.md | 9 +++++++-- rootfs/usr/local/bin/setup.sh | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c76f064b..868951b2 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Thank you very much. - [mailserver2/mailserver](#mailserver2mailserver) - [Fork](#fork) - - [Chat & questions](#chat--questions) + - [Chat \& questions](#chat--questions) - [Build](#build) - [Docker image](#docker-image) - [Summary](#summary) @@ -86,6 +86,7 @@ Thank you very much. - [IPv6 support](#ipv6-support) - [Persistent files and folders in /mnt/docker/mail Docker volume](#persistent-files-and-folders-in-mntdockermail-docker-volume) - [Override postfix configuration](#override-postfix-configuration) + - [Whitelist Hosts/IP Addresses In Postfix](#whitelist-hostsip-addresses-in-postfix) - [Custom configuration for Dovecot](#custom-configuration-for-dovecot) - [Postfix blacklist](#postfix-blacklist) - [Email client settings](#email-client-settings) @@ -114,6 +115,9 @@ Please check, if your system meets the following minimum requirements: | CPU | 1 GHz | 1 GHz | | RAM | 512 MiB | 1 GiB | +**NOTE**: If you are having problems with clamd using too much memory on startup, set `CLAMD_MEMORY_FIX` to `true`. +See [the ClamAV documentation](https://docs.clamav.net/manual/Installing/Docker.html#what-can-i-do-to-minimize-ram-usage) for details. +

Back to table of contents :arrow_up_small:

### Prerequisites @@ -331,6 +335,7 @@ If you use Ansible, I recommend you to go to see [@ksylvan](https://github.com/k | **WHITELIST_SPAM_ADDRESSES** | List of whitelisted email addresses separated by commas | *optional* | null | | **DISABLE_RSPAMD_MODULE** | List of disabled modules separated by commas | *optional* | null | | **DISABLE_CLAMAV** | Disable virus scanning | *optional* | false | +| **CLAMD_MEMORY_FIX** | Will add `ConcurrentDatabaseReload no` in clamd.conf | *optional* | false | | **DISABLE_SIEVE** | Disable ManageSieve protocol | *optional* | false | | **DISABLE_SIGNING** | Disable DKIM/ARC signing | *optional* | false | | **DISABLE_GREYLISTING** | Disable greylisting policy | *optional* | false | @@ -1123,7 +1128,7 @@ NOQUEUE: reject: 554 5.7.1 : Sender address rejected: Acces - Dovecot 2.2.27 - Rspamd 1.9.4 - Fetchmail 6.3.26 -- ClamAV 0.100.3 +- ClamAV 1.0.3 - Clamav Unofficial Sigs 5.6.2 - Zeyple 1.2.2 - Unbound 1.6.0 diff --git a/rootfs/usr/local/bin/setup.sh b/rootfs/usr/local/bin/setup.sh index b5eb479d..4a8b6821 100644 --- a/rootfs/usr/local/bin/setup.sh +++ b/rootfs/usr/local/bin/setup.sh @@ -733,6 +733,10 @@ sed -i -e 's/^Foreground .*$/Foreground true/g' \ -e 's/^LogFacility .*$/LogFacility LOG_MAIL/g' \ /etc/clamav/clamd.conf +if [ "$CLAMD_MEMORY_FIX" = true ]; then + echo "ConcurrentDatabaseReload no" >> /etc/clamav/clamd.conf +fi + # FRESHCLAM # --------------------------------------------------------------------------------------------- From d65054f3bdf4aa55d43daadb24d02b9fd8b72abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Sat, 8 Jun 2024 08:59:07 +0200 Subject: [PATCH 44/50] Update base image to mailserver2/debian-mail-overlay:1.0.15 (#67) * Update base image to mailserver2/debian-mail-overlay:1.0.15 * Fix environment variables replacement tests. The newest rspamd version has a template that caused a false positive. Now only check our files in /etc/rspamd/local.d instead of all in /etc/rspamd --- Dockerfile | 2 +- test/default.bats | 2 +- test/ldap.bats | 2 +- test/ldap2.bats | 2 +- test/reverse.bats | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 18b6d511..e12fc723 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.14 +FROM mailserver2/debian-mail-overlay:1.0.15 LABEL description="Simple and full-featured mail server using Docker" diff --git a/test/default.bats b/test/default.bats index 809acaec..10d4c92c 100644 --- a/test/default.bats +++ b/test/default.bats @@ -41,7 +41,7 @@ load 'test_helper/bats-assert/load' } @test "checking system: all environment variables have been replaced (default configuration)" { - run docker exec mailserver_default /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd /etc/cron.d /etc/mailname /usr/local/bin" + run docker exec mailserver_default /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd/local.d /etc/cron.d /etc/mailname /usr/local/bin" assert_failure } diff --git a/test/ldap.bats b/test/ldap.bats index a0536824..262904b6 100644 --- a/test/ldap.bats +++ b/test/ldap.bats @@ -12,7 +12,7 @@ load 'test_helper/bats-assert/load' } @test "checking system: all environment variables have been replaced (ldap configuration)" { - run docker exec mailserver_ldap /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd /etc/cron.d /etc/mailname /usr/local/bin" + run docker exec mailserver_ldap /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd/local.d /etc/cron.d /etc/mailname /usr/local/bin" assert_failure } diff --git a/test/ldap2.bats b/test/ldap2.bats index a9b289f4..eb26d387 100644 --- a/test/ldap2.bats +++ b/test/ldap2.bats @@ -6,7 +6,7 @@ load 'test_helper/bats-assert/load' # @test "checking system: all environment variables have been replaced (ldap2 configuration)" { - run docker exec mailserver_ldap2 /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd /etc/cron.d /etc/mailname /usr/local/bin" + run docker exec mailserver_ldap2 /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd/local.d /etc/cron.d /etc/mailname /usr/local/bin" assert_failure } diff --git a/test/reverse.bats b/test/reverse.bats index 968ef125..e70b5b32 100644 --- a/test/reverse.bats +++ b/test/reverse.bats @@ -12,7 +12,7 @@ load 'test_helper/bats-assert/load' } @test "checking system: all environment variables have been replaced (reverse configuration)" { - run docker exec mailserver_reverse /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd /etc/cron.d /etc/mailname /usr/local/bin" + run docker exec mailserver_reverse /bin/bash -c "egrep -R -I "{{.*}}" /etc/postfix /etc/postfixadmin/fetchmail.conf /etc/dovecot /etc/rspamd/local.d /etc/cron.d /etc/mailname /usr/local/bin" assert_failure } From 5a3df6d64909259d499caf6b7f0f26e6d0ef1b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:55:42 +0200 Subject: [PATCH 45/50] Update base image to mailserver2/debian-mail-overlay:1.0.16 (#69) --- Dockerfile | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index e12fc723..c315c8a4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.15 +FROM mailserver2/debian-mail-overlay:1.0.16 LABEL description="Simple and full-featured mail server using Docker" diff --git a/Makefile b/Makefile index 429b399b..3b834618 100644 --- a/Makefile +++ b/Makefile @@ -63,7 +63,7 @@ init_postgres: -e POSTGRES_USER=postfix \ -e POSTGRES_PASSWORD=testpasswd \ -v "`pwd`/test/config/postgres":/docker-entrypoint-initdb.d \ - -t postgres:14-alpine + -t postgres:16-alpine init_ldap: init_openldap init_redis -docker rm -f \ From 8cb6ee22ce644bfe9fc540dc4ce64c59a69659eb Mon Sep 17 00:00:00 2001 From: Andrew Savinykh <658865+AndrewSav@users.noreply.github.com> Date: Mon, 23 Dec 2024 08:38:18 +1300 Subject: [PATCH 46/50] attempt to fix: s6-svc: fatal: unable to control /services/rsyslogd: supervisor not listening (#70) --- rootfs/services/_parent/run | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rootfs/services/_parent/run b/rootfs/services/_parent/run index 177b6a1f..b7e5d51a 100644 --- a/rootfs/services/_parent/run +++ b/rootfs/services/_parent/run @@ -12,6 +12,9 @@ # 8. clamd (antivirus, is launched after database update) # 9. watcher (watches for cert file changes) +# give s6-svscan time to start +sleep 1 + s6-svc -u /services/rsyslogd && s6-svwait -u /services/rsyslogd s6-svc -u /services/unbound && [ "$DISABLE_DNS_RESOLVER" = true ] || s6-svwait -u /services/unbound s6-svc -u /services/postfix && s6-svwait -u /services/postfix From ef9a90bdda92cfcea4b329f19703ad65563b4dcb Mon Sep 17 00:00:00 2001 From: Andrew Savinykh <658865+AndrewSav@users.noreply.github.com> Date: Fri, 14 Feb 2025 13:22:43 +1300 Subject: [PATCH 47/50] update based image to 1.0.17 (#71) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c315c8a4..2760fde9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.16 +FROM mailserver2/debian-mail-overlay:1.0.17 LABEL description="Simple and full-featured mail server using Docker" From 6d0befbc2258121a56e557703fbe36d230691001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Fri, 14 Mar 2025 17:18:49 +0100 Subject: [PATCH 48/50] Update base image to mailserver2/debian-mail-overlay:1.0.18 (#72) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 2760fde9..557918d9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.17 +FROM mailserver2/debian-mail-overlay:1.0.18 LABEL description="Simple and full-featured mail server using Docker" From 276990bd66ec678191a9b0eb3eb208dcfcfc94c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Fri, 29 Aug 2025 06:56:40 +0200 Subject: [PATCH 49/50] Update base image to mailserver2/debian-mail-overlay:1.0.19 (#77) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 557918d9..fc5ddedd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.18 +FROM mailserver2/debian-mail-overlay:1.0.19 LABEL description="Simple and full-featured mail server using Docker" From 11361e81ec405d87bf01061d8230f2bb65930050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Aim=C3=A9e=20Smiseth?= <51710585+SaraSmiseth@users.noreply.github.com> Date: Sun, 14 Jun 2026 09:23:55 +0200 Subject: [PATCH 50/50] Update base image to mailserver2/debian-mail-overlay:1.0.20 (#82) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index fc5ddedd..baf6bbc9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mailserver2/debian-mail-overlay:1.0.19 +FROM mailserver2/debian-mail-overlay:1.0.20 LABEL description="Simple and full-featured mail server using Docker"