├── screenshot.png ├── manifest ├── etc │ ├── cron.d │ │ └── root │ └── supervisor │ │ └── conf.d │ │ └── crond.conf ├── var │ └── www │ │ └── viewer │ │ └── dmarcts-report-viewer-config.php ├── entrypoint.sh └── usr │ └── bin │ └── dmarcts-report-parser.conf ├── .github └── workflows │ ├── contributors.yml │ └── docker-image.yml ├── examples ├── docker-compose.postgres.yml ├── env.example └── docker-compose.mysql.yml ├── Dockerfile ├── README.md └── Jenkinsfile /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gutmensch/docker-dmarc-report/HEAD/screenshot.png -------------------------------------------------------------------------------- /manifest/etc/cron.d/root: -------------------------------------------------------------------------------- 1 | 15 * * * * /usr/bin/dmarcts-report-parser.pl -i -d -r 1>>/var/log/nginx/dmarc-reports.log 2>&1 2 | 3 | -------------------------------------------------------------------------------- /manifest/etc/supervisor/conf.d/crond.conf: -------------------------------------------------------------------------------- 1 | [program:cron] 2 | command=crond -f -L /dev/stdout -c /etc/cron.d 3 | autostart=true 4 | autorestart=true 5 | priority=10 6 | stdout_events_enabled=true 7 | stderr_events_enabled=true 8 | -------------------------------------------------------------------------------- /manifest/var/www/viewer/dmarcts-report-viewer-config.php: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /.github/workflows/contributors.yml: -------------------------------------------------------------------------------- 1 | name: Contributors 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | contributors: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: wow-actions/contributors-list@v1 12 | with: 13 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 14 | svgPath: CONTRIBUTORS.svg 15 | sort: true 16 | round: true 17 | includeBots: false 18 | userNameHeight: 19 19 | # This job does not have commit powers to the `master` branch 20 | noCommit: true 21 | itemTemplate: > 22 | 23 | 24 | 25 | {{{ name }}} 26 | 27 | 28 | - name: Create Pull Request 29 | uses: peter-evans/create-pull-request@v3 30 | with: 31 | commit-message: '[chore] update contributors svg' 32 | title: '[chore] update contributors svg' 33 | delete-branch: true 34 | assignees: gutmensch 35 | reviewers: gutmensch 36 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - "v*" 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | build-and-release: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Set up QEMU 21 | uses: docker/setup-qemu-action@v2 22 | 23 | - name: Set up Docker Buildx 24 | uses: docker/setup-buildx-action@v2 25 | 26 | - name: Docker meta 27 | id: meta 28 | uses: docker/metadata-action@v4 29 | with: 30 | images: gutmensch/dmarc-report 31 | flavor: latest=true 32 | tags: | 33 | type=ref,event=branch 34 | type=ref,event=pr 35 | type=semver,pattern={{version}} 36 | 37 | - name: Login to DockerHub 38 | if: github.ref_type == 'tag' 39 | uses: docker/login-action@v2 40 | with: 41 | username: ${{ secrets.DOCKERHUB_USERNAME }} 42 | password: ${{ secrets.DOCKERHUB_TOKEN }} 43 | 44 | - name: Build and push 45 | uses: docker/build-push-action@v4 46 | with: 47 | context: . 48 | platforms: linux/amd64, linux/arm64 49 | file: Dockerfile 50 | push: ${{ github.ref_type == 'tag' }} 51 | tags: | 52 | ${{ steps.meta.outputs.tags }} 53 | gutmensch/dmarc-report:latest 54 | labels: ${{ steps.meta.outputs.labels }} 55 | -------------------------------------------------------------------------------- /examples/docker-compose.postgres.yml: -------------------------------------------------------------------------------- 1 | services: 2 | dmarc-report: 3 | image: "gutmensch/dmarc-report:latest" 4 | hostname: dmarc-report 5 | container_name: dmarc-report 6 | restart: always 7 | depends_on: 8 | db: 9 | condition: service_healthy 10 | ports: 11 | - "80:80" 12 | environment: 13 | - "REPORT_DB_HOST=${DB_HOST:-db}" 14 | - "REPORT_DB_TYPE=${DB_TYPE:-pgsql}" 15 | - "REPORT_DB_PORT=${DB_PORT:-5432}" 16 | - "REPORT_DB_NAME=${DB_NAME:-dmarc_report}" 17 | - "REPORT_DB_USER=${DB_USER:-dmarc_report}" 18 | - "REPORT_DB_PASS=${DB_PASSWORD}" 19 | - "PARSER_IMAP_SERVER=${IMAP_SERVER}" 20 | - "PARSER_IMAP_PORT=${IMAP_PORT:-993}" 21 | - "PARSER_IMAP_USER=${IMAP_USER}" 22 | - "PARSER_IMAP_PASS=${IMAP_PASSWORD}" 23 | - "PARSER_IMAP_READ_FOLDER=${IMAP_READ_FOLDER:-Inbox}" 24 | - "PARSER_IMAP_MOVE_FOLDER=${IMAP_MOVE_FOLDER:-processed}" 25 | - "PARSER_IMAP_MOVE_FOLDER_ERR=${IMAP_MOVE_FOLDER_ERR:-error}" 26 | - "PARSER_IMAP_SSL=${PARSER_IMAP_SSL}" 27 | - "PARSER_IMAP_TLS=${PARSER_IMAP_TLS}" 28 | - "PARSER_IMAP_IGNORE_ERROR=${PARSER_IMAP_IGNORE_ERROR}" 29 | - "PARSER_XML_MAXSIZE=${PARSER_XML_MAXSIZE}" 30 | 31 | db: 32 | image: postgres:latest 33 | restart: always 34 | environment: 35 | - "POSTGRES_DB=${DB_NAME:-dmarc_report}" 36 | - "POSTGRES_USER=${DB_USER:-dmarc_report}" 37 | - "POSTGRES_PASSWORD=${DB_PASSWORD}" 38 | volumes: 39 | - ./run/db:/var/lib/postgresql/data 40 | healthcheck: 41 | test: ["CMD-SHELL", "pg_isready -U dmarc_report"] 42 | interval: 10s 43 | timeout: 10s 44 | retries: 5 -------------------------------------------------------------------------------- /examples/env.example: -------------------------------------------------------------------------------- 1 | # database host address, leave empty for default host "db" 2 | DB_HOST= 3 | 4 | # the database type mysql or pgsql, leave empty for default (depending on your docker-compose.yml) 5 | DB_TYPE= 6 | 7 | # the database port (mysql 3306) (pqsql 5432), leave empty for default (depending on your docker-compose.yml) 8 | DB_PORT= 9 | 10 | # the database name, leave empty for default "dmarc_report" 11 | DB_NAME= 12 | 13 | # the database name, leave empty for default "dmarc_report" 14 | DB_USER= 15 | 16 | # mysql root password. Irrelevant if you are using postgres 17 | ROOT_DB_PASSWORD= 18 | 19 | # database password for the database user 20 | DB_PASSWORD= 21 | 22 | # the email address receiving the DMARC reports 23 | IMAP_USER= 24 | 25 | # the password for the email address receiving the DMARC reports 26 | IMAP_PASSWORD= 27 | 28 | # the server the email address is hosted on 29 | IMAP_SERVER= 30 | 31 | # optional: default is 993 (or 143) 32 | IMAP_PORT= 33 | 34 | # optional: default is "Inbox" 35 | IMAP_READ_FOLDER= 36 | 37 | # optional: default is "processed" 38 | IMAP_MOVE_FOLDER= 39 | 40 | # optional: default is "error" 41 | IMAP_MOVE_FOLDER_ERR= 42 | 43 | # Enable SSL and/or (START-)TLS. Set both to 0 to disable encryption (not recommended) 44 | PARSER_IMAP_SSL=0 45 | PARSER_IMAP_TLS=1 46 | 47 | # Ignore ERROR: message_string() issue experienced with Exchange Online. Set to 1 to enable 48 | PARSER_IMAP_IGNORE_ERROR=0 49 | 50 | # Increase the maximum size of the XML file. (default is 50000 bytes) 51 | # When the size exceeds the maximum, one could experience an error Uncaught ValueError: DOMDocument::loadXML(): 52 | # Argument #1 ($source) must not be empty. 53 | PARSER_XML_MAXSIZE=50000 54 | -------------------------------------------------------------------------------- /examples/docker-compose.mysql.yml: -------------------------------------------------------------------------------- 1 | services: 2 | dmarc-report: 3 | image: "gutmensch/dmarc-report:latest" 4 | hostname: dmarc-report 5 | container_name: dmarc-report 6 | restart: always 7 | depends_on: 8 | db: 9 | condition: service_healthy 10 | ports: 11 | - "80:80" 12 | environment: 13 | - "REPORT_DB_HOST=${DB_HOST:-db}" 14 | - "REPORT_DB_TYPE=${DB_TYPE:-mysql}" 15 | - "REPORT_DB_PORT=${DB_PORT:-3306}" 16 | - "REPORT_DB_NAME=${DB_NAME:-dmarc_report}" 17 | - "REPORT_DB_USER=${DB_USER:-dmarc_report}" 18 | - "REPORT_DB_PASS=${DB_PASSWORD}" 19 | - "PARSER_IMAP_SERVER=${IMAP_SERVER}" 20 | - "PARSER_IMAP_PORT=${IMAP_PORT:-993}" 21 | - "PARSER_IMAP_USER=${IMAP_USER}" 22 | - "PARSER_IMAP_PASS=${IMAP_PASSWORD}" 23 | - "PARSER_IMAP_READ_FOLDER=${IMAP_READ_FOLDER:-Inbox}" 24 | - "PARSER_IMAP_MOVE_FOLDER=${IMAP_MOVE_FOLDER:-processed}" 25 | - "PARSER_IMAP_MOVE_FOLDER_ERR=${IMAP_MOVE_FOLDER_ERR:-error}" 26 | - "PARSER_IMAP_SSL=${PARSER_IMAP_SSL}" 27 | - "PARSER_IMAP_TLS=${PARSER_IMAP_TLS}" 28 | - "PARSER_IMAP_IGNORE_ERROR=${PARSER_IMAP_IGNORE_ERROR}" 29 | - "PARSER_XML_MAXSIZE=${PARSER_XML_MAXSIZE}" 30 | 31 | db: 32 | image: mariadb:10 33 | command: --skip-innodb-read-only-compressed 34 | restart: always 35 | environment: 36 | - "MYSQL_ROOT_PASSWORD=${ROOT_DB_PASSWORD}" 37 | - "MYSQL_DATABASE=${DB_NAME:-dmarc_report}" 38 | - "MYSQL_USER=${DB_USER:-dmarc_report}" 39 | - "MYSQL_PASSWORD=${DB_PASSWORD}" 40 | volumes: 41 | - ./run/db:/var/lib/mysql 42 | healthcheck: 43 | test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p${ROOT_DB_PASSWORD}"] 44 | interval: 10s 45 | timeout: 10s 46 | retries: 5 47 | -------------------------------------------------------------------------------- /manifest/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # change according to alpine and php release 4 | PHP_VERSION=81 5 | 6 | # Display PHP error's or not 7 | if [[ "$ERRORS" != "1" ]] ; then 8 | sed -i -e "s/error_reporting =.*/error_reporting = E_ALL/g" /etc/php${PHP_VERSION}/php.ini 9 | sed -i -e "s/display_errors =.*/display_errors = stdout/g" /etc/php${PHP_VERSION}/php.ini 10 | fi 11 | 12 | # Disable opcache? 13 | if [[ -v NO_OPCACHE ]]; then 14 | sed -i -e "s/zend_extension=opcache.so/;zend_extension=opcache.so/g" /etc/php${PHP_VERSION}/conf.d/00_opcache.ini 15 | fi 16 | 17 | # Tweak nginx to match the workers to cpu's 18 | procs=$(cat /proc/cpuinfo | grep processor | wc -l) 19 | sed -i -e "s/worker_processes 5/worker_processes $procs/" /etc/nginx/nginx.conf 20 | 21 | # Copy important env vars for PHP-FPM to access 22 | PHP_ENV_FILE="/etc/php${PHP_VERSION}/php-fpm.d/${PHP_ENV_FILE:-env.conf}" 23 | echo '[www]' > "$PHP_ENV_FILE" 24 | echo 'user = nginx' >> "$PHP_ENV_FILE" 25 | echo 'group = www-data' >> "$PHP_ENV_FILE" 26 | echo 'listen.owner = nginx' >> "$PHP_ENV_FILE" 27 | echo 'listen.group = www-data' >> "$PHP_ENV_FILE" 28 | env | grep -e 'REPORT_DB_TYPE' -e 'REPORT_DB_HOST' -e 'REPORT_DB_PORT' -e 'REPORT_DB_NAME' -e 'REPORT_DB_USER' -e 'REPORT_DB_PASS' | sed "s/\(.*\)=\(.*\)/env[\1] = '\2'/" >> "$PHP_ENV_FILE" 29 | 30 | # compat from older image where variable was not existing 31 | grep -e ^REPORT_DB_PORT "$PHP_ENV_FILE" || echo env[REPORT_DB_PORT] = 3306 >> "$PHP_ENV_FILE" 32 | 33 | # Get and parse dmarc reports once at startup to avoid PHP errors with a new database 34 | if /usr/bin/dmarcts-report-parser.pl -i -d -r > /var/log/nginx/dmarc-reports.log 2>&1; then 35 | echo 'INFO: Dmarc reports parsed successfully' 36 | else 37 | echo 'CRIT: Dmarc reports could not be parsed. Check your IMAP and MYSQL Settings.' 38 | echo -e "DEBUG: Parsing failed with the following output:\n" 39 | cat /var/log/nginx/dmarc-reports.log 40 | exit 1 41 | fi 42 | 43 | # Start supervisord and services 44 | /usr/bin/supervisord -n -c /etc/supervisord.conf 45 | -------------------------------------------------------------------------------- /manifest/usr/bin/dmarcts-report-parser.conf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | ### configuration ############################################################## 3 | ################################################################################ 4 | 5 | # If IMAP access is not used, config options starting with $imap do not need to 6 | # be set and are ignored. 7 | 8 | $debug = $ENV{'PARSER_DEBUG'} // 0; 9 | $delete_reports = $ENV{'PARSER_DELETE_REPORTS'} // 0; 10 | 11 | $dbname = $ENV{'REPORT_DB_NAME'}; 12 | $dbuser = $ENV{'REPORT_DB_USER'}; 13 | $dbpass = $ENV{'REPORT_DB_PASS'}; 14 | $dbhost = $ENV{'REPORT_DB_HOST'}; # Set the hostname if we can't connect to the local socket. 15 | $dbport = $ENV{'REPORT_DB_PORT'} // 3306; 16 | $dbtype = $ENV{'REPORT_DB_TYPE'} eq 'pgsql' ? 'Pg' : 'mysql'; 17 | 18 | if(exists $ENV{PARSER_IMAP_SERVER_WITH_PORT} && defined $ENV{PARSER_IMAP_SERVER_WITH_PORT}) { 19 | my @server_attr = split ':', $ENV{PARSER_IMAP_SERVER_WITH_PORT}; 20 | $imapserver = $server_attr[0]; 21 | $imapport = $server_attr[1]; 22 | } else { 23 | $imapserver = $ENV{'PARSER_IMAP_SERVER'}; 24 | $imapport = $ENV{'PARSER_IMAP_PORT'}; 25 | } 26 | 27 | $imapuser = $ENV{'PARSER_IMAP_USER'}; 28 | $imappass = $ENV{'PARSER_IMAP_PASS'}; 29 | $imapssl = $ENV{'PARSER_IMAP_SSL'} // '0'; # If set to 1, remember to change server port to 993 and disable imaptls. 30 | $imaptls = $ENV{'PARSER_IMAP_TLS'} // '1'; # Enabled as the default and best-practice. 31 | $tlsverify = $ENV{'PARSER_IMAP_VERIFY'} // '0'; # Enable verify server cert as the default and best-practice. 32 | $imapignoreerror = $ENV{'PARSER_IMAP_IGNORE_ERROR'} // '0';# set it to 1 if you see an "ERROR: message_string() 33 | # expected 119613 bytes but received 81873 you may 34 | # need the IgnoreSizeErrors option" because of malfunction 35 | # imap server as MS Exchange 2007, ... 36 | $imapreadfolder = $ENV{'PARSER_IMAP_READ_FOLDER'}; 37 | 38 | # If $imapmovefolder is set, processed IMAP messages will be moved (overruled by 39 | # the --delete option!) 40 | $imapmovefolder = $ENV{'PARSER_IMAP_MOVE_FOLDER'}; 41 | $imapmovefoldererr = $ENV{'PARSER_IMAP_MOVE_FOLDER_ERR'}; 42 | 43 | # maximum size of XML files to store in database, long files can cause transaction aborts 44 | $maxsize_xml = $ENV{'PARSER_XML_MAXSIZE'} // 50000; 45 | # store XML as base64 encopded gzip in database (save space, harder usable) 46 | $compress_xml = $ENV{'PARSER_XML_COMPRESS'} // 0; 47 | 48 | # if there was an error during file processing (message does not contain XML or ZIP parts, 49 | # or a database error) the parser reports an error and does not delete the file, even if 50 | # delete_reports is set (or --delete is given). Deletion can be enforced by delete_failed, 51 | # however not for database errors. 52 | $delete_failed = $ENV{'PARSER_DELETE_FAILED'} // 0; 53 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG UPSTREAM_IMAGE=trafex/php-nginx:2.6.0 2 | 3 | FROM $UPSTREAM_IMAGE 4 | 5 | LABEL maintainer="Robert Schumann " 6 | 7 | ENV REPORT_PARSER_SOURCE="https://github.com/techsneeze/dmarcts-report-parser/archive/master.zip" \ 8 | REPORT_VIEWER_SOURCE="https://github.com/techsneeze/dmarcts-report-viewer/archive/master.zip" 9 | 10 | USER root 11 | 12 | WORKDIR / 13 | 14 | COPY ./manifest/ / 15 | 16 | RUN set -e -x \ 17 | && apk add -U \ 18 | bash \ 19 | cmake \ 20 | expat-dev \ 21 | g++ \ 22 | gpg \ 23 | gzip \ 24 | libpq \ 25 | libpq-dev \ 26 | make \ 27 | mariadb-client \ 28 | mariadb-connector-c \ 29 | mariadb-dev \ 30 | musl-obstack \ 31 | musl-obstack-dev \ 32 | openssl \ 33 | openssl-dev \ 34 | perl-dev \ 35 | perl-utils \ 36 | php81-pdo \ 37 | php81-pdo_mysql \ 38 | php81-pdo_pgsql \ 39 | tzdata \ 40 | wget \ 41 | && wget -4 -q --no-check-certificate -O parser.zip $REPORT_PARSER_SOURCE \ 42 | && wget -4 -q --no-check-certificate -O viewer.zip $REPORT_VIEWER_SOURCE \ 43 | && unzip parser.zip && cp -av dmarcts-report-parser-master/* /usr/bin/ && rm -vf parser.zip && rm -rvf dmarcts-report-parser-master \ 44 | && unzip viewer.zip && cp -av dmarcts-report-viewer-master/* /var/www/viewer/ && rm -vf viewer.zip && rm -rvf dmarcts-report-viewer-master \ 45 | && sed -i "1s/^/body { font-family: Sans-Serif; }\n/" /var/www/viewer/default.css \ 46 | && sed -i 's%.*listen [::]:8080 default_server;% listen [::]:80 default_server;%g' /etc/nginx/nginx.conf \ 47 | && sed -i 's%.*listen 8080 default_server;% listen 80 default_server;%g' /etc/nginx/nginx.conf \ 48 | && sed -i 's%.*root /var/www/html;% root /var/www/viewer;%g' /etc/nginx/nginx.conf \ 49 | && sed -i 's/.*index index.php index.html;/ index dmarcts-report-viewer.php;/g' /etc/nginx/nginx.conf \ 50 | && sed -i 's%files = /etc/supervisor.d/\*.ini%files = /etc/supervisor/conf.d/*.conf%g' /etc/supervisord.conf \ 51 | && (echo y;echo o conf allow_installing_outdated_dists yes;echo o conf prerequisites_policy follow;echo o conf commit)|cpan \ 52 | && for i in \ 53 | IO::Socket::SSL \ 54 | CPAN \ 55 | CPAN::DistnameInfo \ 56 | File::MimeInfo \ 57 | IO::Compress::Gzip \ 58 | Getopt::Long \ 59 | Mail::IMAPClient \ 60 | Mail::Mbox::MessageParser \ 61 | MIME::Base64 \ 62 | MIME::Words \ 63 | MIME::Parser \ 64 | MIME::Parser::Filer \ 65 | XML::Parser \ 66 | XML::Simple \ 67 | DBI \ 68 | # XXX: pinning to a version, which does not suffer from mariadb 10 version comparison issues 69 | # TODO: replace with DBD::mysql again, when issue is resolved 70 | # https://forum.bestpractical.com/t/mysql-dependency-error-with-mariadb-and-debian-12/38748/2 71 | DVEEDEN/DBD-mysql-4.052.tar.gz \ 72 | DBD::Pg \ 73 | Socket \ 74 | Socket6 \ 75 | PerlIO::gzip \ 76 | ; do cpan install $i; done \ 77 | && apk del mariadb-dev expat-dev openssl-dev perl-dev g++ cmake make musl-obstack-dev libpq-dev 78 | 79 | HEALTHCHECK --interval=1m --timeout=3s CMD curl --silent --fail http://127.0.0.1:80/fpm-ping 80 | 81 | EXPOSE 80 82 | 83 | CMD ["/bin/bash", "/entrypoint.sh"] 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-dmarc-report ![Docker Build](https://github.com/gutmensch/docker-dmarc-report/actions/workflows/docker-image.yml/badge.svg) [![Docker Pulls](https://img.shields.io/docker/pulls/gutmensch/dmarc-report.svg)](https://registry.hub.docker.com/u/gutmensch/dmarc-report/) 2 | 3 | This image is intended to combine a dmarc report parser (see https://github.com/techsneeze/dmarcts-report-parser by TechSneeze.com and John Bieling) with a report viewer (see https://github.com/techsneeze/dmarcts-report-viewer/ by the same people) into a runnable docker image / microservice. 4 | 5 | It fetches dmarc report mails regularly from an IMAP server, stores them into a MySQL DB and visualizes them via Webserver/PHP module. 6 | 7 | ## Howto 8 | 9 | 1. Create a \_dmarc.example.com TXT DNS record for your domain, containg an IMAP postbox, e.g. 10 | 11 | ```bash 12 | 17:18 $ dig TXT _dmarc.schumann.link +short 13 | "v=DMARC1\; p=quarantine\; fo=1\; rua=mailto:dmarc@schumann.link\; ruf=mailto:dmarc@schumann.link\; adkim=s\; aspf=s\;" 14 | ``` 15 | 16 | 1. Create a MySQL Database and a user for this service 17 | 18 | 1. Run this docker image with below mentioned env vars 19 | 20 | 1. Access port 80 on the container (or 443) or put it behind a reverse proxy to view reports 21 | 22 | ```bash 23 | docker pull gutmensch/dmarc-report 24 | docker run -e ... -ti gutmensch/dmarc-report 25 | ``` 26 | 27 | New dmarc reports will be fetched every 15 minutes past the hour, every hour. Therefore it can take up to one hour for the first report to be fetched. 28 | 29 | ## Versions for last build latest and docker image tag 1.4 30 | 31 | dmarcts report viewer: 2024-02-04 32 | 33 | dmarcts report parser: 2024-02-04 34 | 35 | CAUTION: The old gutmensch/dmarc-report:latest image (older alpine, php5, etc.) is available still as gutmensch/dmarc-report:0.5. The current latest (and 1.0) uses the latest alpine version, newer MySQL client libraries, newer OpenSSL, etc. and improves compatibilitiy with MySQL 8+. 36 | 37 | ## Frontend Screenshot 38 | 39 | ![DMARC Report Viewer](https://github.com/gutmensch/docker-dmarc-report/blob/master/screenshot.png?raw=true) 40 | 41 | ## Sample docker compose / Environment variables 42 | 43 | Make sure to create the IMAP-Folders for processed and error reports before the cron job runs! 44 | 45 | The default foldernames are are [`error`](examples/env.example) & [`processed`](examples/env.example) but they can be changed within the [`env-file`](examples/env.example). 46 | 47 | Make sure to rename the [`env.example`](examples/env.example) file to `.env` and adjust the values to your needs. 48 | 49 | You can find templates for both, [`postgreql`](examples/docker-compose.postgres.yml) 50 | and [`mysql`](examples/docker-compose.mysql.yml) 51 | db in the [`examples`](examples) directory. Just rename the setup you want to use to `docker-compose.yml`. 52 | 53 | 54 | 55 | 56 | ## Manual update 57 | 58 | If you are using the docker-compose file above, you can use this command to trigger an manual update. It will fetch the latest reports and parse them. 59 | 60 | ```bash 61 | docker compose exec dmarc-report /usr/bin/dmarcts-report-parser.pl -i -d -r=1 62 | ``` 63 | 64 | ## Optional extended configuration 65 | 66 | For further optional configuration see the docker-compose [`env-file`](examples/env.example). 67 | 68 | ## Contributors 69 | 70 | 71 | 72 | ## Stargazers over time 73 | 74 | [![Stargazers over time](https://starchart.cc/gutmensch/docker-dmarc-report.svg)](https://starchart.cc/gutmensch/docker-dmarc-report) 75 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | import org.jenkinsci.plugins.pipeline.modeldefinition.Utils 2 | import java.text.SimpleDateFormat 3 | 4 | 5 | DOCKER_IMAGE_NAME = '' 6 | DOCKER_IMAGE = '' 7 | DOCKER_ARGS = '--network=services_default' 8 | DOCKER_REGISTRY = 'registry.n-os.org:5000' 9 | 10 | 11 | properties([ 12 | disableConcurrentBuilds(), 13 | parameters([ 14 | booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: 'Do you want to run the build with tests?') 15 | ]) 16 | ]) 17 | 18 | 19 | node { 20 | try { 21 | pipeline() 22 | } 23 | catch(e) { 24 | setBuildStatus(e.toString().take(140), 'FAILURE') 25 | throw e 26 | } 27 | finally { 28 | cleanup() 29 | } 30 | } 31 | 32 | 33 | /* 34 | ****************************************************************** 35 | 36 | standard functions 37 | these functions below implement the standard docker image pipeline 38 | 39 | ****************************************************************** 40 | */ 41 | def pipeline() { 42 | 43 | stage('checkout git') { 44 | checkout scm 45 | setBuildStatus('In progress...', 'PENDING') 46 | } 47 | 48 | // https://docs.cloudbees.com/docs/admin-resources/latest/plugins/docker-workflow 49 | stage('build image') { 50 | DOCKER_IMAGE_NAME = "${DOCKER_REGISTRY}/${getDockerImage()}:${getDockerTag()}" 51 | DOCKER_IMAGE = docker.build(DOCKER_IMAGE_NAME, "--no-cache ${DOCKER_ARGS} .") 52 | } 53 | 54 | stage('run tests') { 55 | if (fileExists('./test/run.sh') && !params.SKIP_TESTS) { 56 | DOCKER_IMAGE.inside("${DOCKER_ARGS} --entrypoint=") { 57 | sh 'bash /usr/build/test/run.sh' 58 | } 59 | } 60 | else { 61 | Utils.markStageSkippedForConditional('run tests') 62 | } 63 | } 64 | 65 | stage('push image') { 66 | if (BRANCH_NAME == 'master') { 67 | DOCKER_IMAGE.push() 68 | } 69 | else { 70 | Utils.markStageSkippedForConditional('push image') 71 | } 72 | } 73 | 74 | stage('delete image') { 75 | if (BRANCH_NAME == 'master') { 76 | Utils.markStageSkippedForConditional('delete image') 77 | } 78 | else { 79 | deleteDockerImage(DOCKER_IMAGE_NAME) 80 | } 81 | setBuildStatus('Success', 'SUCCESS') 82 | } 83 | } 84 | 85 | void deleteDockerImage(image) { 86 | sh(script: "docker rmi -f ${image}") 87 | } 88 | 89 | void cleanup() { 90 | stage('schedule cleanup') { 91 | build job: '/maintenance/starter', wait: false 92 | } 93 | } 94 | 95 | String getDockerImage() { 96 | return sh(script: "echo '${JOB_NAME}' | awk -F/ '{print \$(NF-1)}' | sed 's%docker-%%'", returnStdout: true).trim() 97 | } 98 | 99 | String getDockerTag() { 100 | def shortHash = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim() 101 | def date = new Date() 102 | def sdf = new SimpleDateFormat("yyyyMMddHHmmss") 103 | // semver in TAG_ID file or reference to ARG in Dockerfile 104 | if (!fileExists('./TAG_ID')) { 105 | return "${sdf.format(date)}.${shortHash}.b${BUILD_ID}" 106 | } 107 | def tagId = sh(script: 'cat ./TAG_ID', returnStdout: true).trim() 108 | if (tagId ==~ /^[A-Z_]+$/) { 109 | return sh(script: "awk -F= '/ARG ${tagId}=/{print \$2}' Dockerfile", returnStdout: true).trim() 110 | } 111 | else { 112 | return tagId 113 | } 114 | } 115 | 116 | void setBuildStatus(message, state) { 117 | def repoUrl = sh(script: 'git config --get remote.origin.url', returnStdout: true).trim() 118 | step([ 119 | $class: "GitHubCommitStatusSetter", 120 | reposSource: [$class: "ManuallyEnteredRepositorySource", url: repoUrl], 121 | contextSource: [$class: "ManuallyEnteredCommitContextSource", context: "ci/jenkins/build-status"], 122 | errorHandlers: [[$class: "ChangingBuildStatusErrorHandler", result: "UNSTABLE"]], 123 | statusResultSource: [ $class: "ConditionalStatusResultSource", results: [[$class: "AnyBuildResult", message: message, state: state]] ] 124 | ]); 125 | } 126 | --------------------------------------------------------------------------------