├── .gitattributes ├── .dockerignore ├── root ├── etc │ ├── services.d │ │ ├── cron │ │ │ └── run │ │ ├── freshclam │ │ │ └── run │ │ ├── nginx │ │ │ └── run │ │ ├── fail2ban │ │ │ └── run │ │ └── inotify │ │ │ └── run │ ├── scripts.d │ │ ├── renew-acme.sh │ │ ├── refresh-agent-weekly.sh │ │ ├── scan-filesystem.sh │ │ └── refresh-agent.sh │ ├── cont-init.d │ │ ├── 90-finalize-conf │ │ ├── 50-clamav-config │ │ ├── 10-startup-config │ │ ├── 20-nginx-config │ │ ├── 80-template-config │ │ ├── 40-crowdsec-config │ │ ├── 30-fail2ban-config │ │ └── 60-acme-config │ └── logrotate.d │ │ ├── acme │ │ ├── nginx │ │ ├── clamav │ │ ├── fail2ban │ │ └── refresh-agent ├── default │ ├── fail2ban │ │ ├── fail2ban.local │ │ ├── filter.d │ │ │ ├── nginx-noproxy.conf │ │ │ ├── nginx-nohome.conf │ │ │ ├── nginx-deny.conf │ │ │ ├── nginx-modsecurity.conf │ │ │ └── nginx-badbots.conf │ │ ├── paths-common.local │ │ ├── README │ │ ├── action.d │ │ │ └── sendmail-common.local │ │ └── jail.local │ ├── nginx │ │ ├── conf.d │ │ │ ├── autoconf.conf │ │ │ ├── deny-tor-proxies.conf │ │ │ ├── deny-bad-ip-addresses.conf │ │ │ ├── deny-bad-referers.conf │ │ │ ├── deny-bad-user-agents.conf │ │ │ ├── geoip.conf │ │ │ ├── logging.conf │ │ │ ├── lua.conf │ │ │ ├── ddos-protection.conf │ │ │ ├── modsecurity.conf │ │ │ ├── compression.conf │ │ │ ├── security-headers.conf │ │ │ ├── proxy.conf │ │ │ └── error-pages.conf │ │ ├── sites-available │ │ │ └── default │ │ ├── README │ │ ├── sites-conf.d │ │ │ ├── geoip.conf │ │ │ ├── robots.conf │ │ │ ├── acme.conf │ │ │ ├── error-pages.conf │ │ │ ├── deny-rules.conf │ │ │ ├── openid-connect.conf │ │ │ └── ssl.conf │ │ ├── modsec.d │ │ │ ├── tools │ │ │ │ └── av-scanning │ │ │ │ │ └── runav.pl │ │ │ └── modsecurity.conf │ │ └── nginx.conf │ ├── clamav │ │ ├── README │ │ ├── freshclam.conf │ │ └── clamd.conf │ └── www │ │ ├── README │ │ ├── index.html │ │ └── error.html └── usr │ └── local │ └── lib │ └── lua │ └── crowdsec_nginx.lua ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ └── BuildImage.yml ├── install ├── etc │ ├── crontabs │ │ └── root │ └── nginx │ │ └── nginx.conf ├── install-base-image.sh ├── install-crowdsec.sh ├── install-modsecurity.sh ├── install.sh ├── install-nginx.sh └── install-lua.sh ├── Dockerfile └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.sh text eol=lf -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | .github 4 | README.md 5 | LICENSE 6 | .vscode -------------------------------------------------------------------------------- /root/etc/services.d/cron/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | exec /usr/sbin/crond -f -S -l 5 4 | -------------------------------------------------------------------------------- /root/etc/services.d/freshclam/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | exec freshclam -d >/dev/null -------------------------------------------------------------------------------- /root/etc/services.d/nginx/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | exec /usr/sbin/nginx -c /config/nginx/nginx.conf 4 | -------------------------------------------------------------------------------- /root/default/fail2ban/fail2ban.local: -------------------------------------------------------------------------------- 1 | [Definition] 2 | 3 | logtarget = /config/logs/fail2ban/fail2ban.log 4 | dbfile = /config/fail2ban/fail2ban.sqlite3 5 | -------------------------------------------------------------------------------- /root/etc/services.d/fail2ban/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | if [[ ${FAIL2BAN_ENABLED} != "disabled" ]]; then 4 | exec fail2ban-client -x -f start 5 | fi 6 | -------------------------------------------------------------------------------- /root/default/fail2ban/filter.d/nginx-noproxy.conf: -------------------------------------------------------------------------------- 1 | # fail2ban filter to prevent using this as open proxy 2 | 3 | 4 | [Definition] 5 | 6 | failregex = ^ -.*GET http.* 7 | 8 | ignoreregex = -------------------------------------------------------------------------------- /root/default/fail2ban/paths-common.local: -------------------------------------------------------------------------------- 1 | # 2 | # Common paths of nginx logs 3 | # 4 | 5 | [DEFAULT] 6 | 7 | nginx_error_log = /config/logs/nginx/*error*.log 8 | 9 | nginx_access_log = /config/logs/nginx/*access*.log -------------------------------------------------------------------------------- /root/default/fail2ban/filter.d/nginx-nohome.conf: -------------------------------------------------------------------------------- 1 | # fail2ban filter to prevent browsing unix home directories from the webserver 2 | 3 | 4 | [Definition] 5 | 6 | failregex = ^ -.*GET .*/~.* 7 | 8 | ignoreregex = -------------------------------------------------------------------------------- /root/etc/scripts.d/renew-acme.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | # Generate renew lock 4 | touch /tmp/acme-renew.lock 5 | sleep 1 6 | 7 | # Renew certificates 8 | chmod +x /etc/cont-init.d/60-acme-config 9 | /etc/cont-init.d/60-acme-config 10 | -------------------------------------------------------------------------------- /root/default/nginx/conf.d/autoconf.conf: -------------------------------------------------------------------------------- 1 | # Do not modify this file, it contains mapping tables which will be generated automatically based on your environment variables. 2 | # If you want to disable some of the mappings change them in the related header value and not inside the autoconf.conf file. 3 | 4 | -------------------------------------------------------------------------------- /root/default/clamav/README: -------------------------------------------------------------------------------- 1 | 2 | This directory includes the ClamAV configuration and virus definitions 3 | 4 | All changes on the files in this directory require a restart of the container in order to get active. 5 | 6 | Be careful and make sure you know what you do before modifying the configuration files as the service is used for the ModSecurity file scan -------------------------------------------------------------------------------- /root/usr/local/lib/lua/crowdsec_nginx.lua: -------------------------------------------------------------------------------- 1 | -- This module takes cae about the crowdsec initialization in case crowdsec is enabled 2 | local cs = require "crowdsec.CrowdSec" 3 | local ok, err = cs.init("/usr/local/lib/lua/crowdsec/crowdsec.conf") 4 | if ok == nil then 5 | ngx.log(ngx.ERR, "[Crowdsec] " .. err) 6 | error() 7 | end 8 | ngx.log(ngx.NOTICE, "[Crowdsec] Initialisation done") -------------------------------------------------------------------------------- /root/default/fail2ban/filter.d/nginx-deny.conf: -------------------------------------------------------------------------------- 1 | # fail2ban filter configuration for nginx 2 | 3 | 4 | [Definition] 5 | 6 | failregex = ^ \[error\] \d+#\d+: \*\d+ (access forbidden by rule), client: , server: \S*, request: "\S+ \S+ HTTP\/\d+\.\d+", host: "\S+"(?:, referrer: "\S+")?\s*$ 7 | 8 | ignoreregex = 9 | 10 | datepattern = {^LN-BEG} 11 | 12 | # DEV NOTES: 13 | # 14 | # Author: Will L (driz@linuxserver.io) 15 | -------------------------------------------------------------------------------- /root/etc/cont-init.d/90-finalize-conf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | # fix potentional permission issues 4 | chown -R secproxy:secproxy /config 5 | chmod -R 0644 /etc/logrotate.d 6 | chmod -R +wr /config/logs 7 | mkdir -p /var/lib/nginx/cache 8 | chown -R secproxy:secproxy /var/lib/nginx/cache 9 | chmod 677 /var/lib/nginx/cache/ 10 | chown clamav:clamav /run/clamav 11 | chown clamav:clamav /var/lib/clamav 12 | chown clamav:clamav /var/log/clamav/*.* 13 | 14 | -------------------------------------------------------------------------------- /root/default/fail2ban/filter.d/nginx-modsecurity.conf: -------------------------------------------------------------------------------- 1 | # fail2ban filter for nginx modsecurity connector 2 | 3 | 4 | [Definition] 5 | 6 | failregex = ^ \[error\].*.\[client ] ModSecurity:\s+(?:\[(?:\w+ \"[^\"]*\"|[^\]]*)\]\s*)*Access denied with code [45]\d\d 7 | 8 | ignoreregex = 9 | 10 | datepattern = {^LN-BEG} 11 | 12 | # DEV Notes: 13 | # List of all 4XX errors ModSecurity generates during blocking action 14 | # 15 | # Author: Florian Michel 16 | # Date: 26.04.2021 -------------------------------------------------------------------------------- /root/default/www/README: -------------------------------------------------------------------------------- 1 | 2 | This directory includes default web and error pages which are included in the default page of nginx. 3 | 4 | You can replace this files if you want other default pages on your web server. 5 | Please do not remove this pages as they will be recreated on a container restart. 6 | 7 | If you add additional pages here, they will be available on the default web page. 8 | For custom web pages you will probably need to include them with different directives you will probably need to add them inside the server directive. -------------------------------------------------------------------------------- /root/default/fail2ban/README: -------------------------------------------------------------------------------- 1 | 2 | This directory includes the fail2ban configuration 3 | 4 | All changes on the files in this directory require a restart of the container in order to get active. 5 | 6 | You can add custom filters and actions to the folders "action.d" and "filter.d". They will be included if you enable the related filter/action in the "jail.local" file. 7 | Do not edit the .conf files inside the actions.d and filter.d directory. Make changes only on .local files with simmilar name to the .conf files. 8 | Please do not add files to the root of this folder, as they will be ignored during the restart and will not bew copied to the fail2ban folder -------------------------------------------------------------------------------- /root/default/nginx/sites-available/default: -------------------------------------------------------------------------------- 1 | 2 | # redirect traffic to https 3 | server { 4 | listen 80; 5 | listen [::]:80; 6 | server_name _; 7 | 8 | return 301 https://$host$request_uri; 9 | } 10 | 11 | # Default SSL server 12 | server { 13 | listen 443 ssl http2; 14 | listen [::]:443 ssl http2; 15 | server_name _; 16 | 17 | root /config/www; 18 | index index.html; 19 | 20 | # include site related configurations like ssl, error pages, robots deny list etc 21 | include /config/nginx/sites-conf.d/*.conf; 22 | 23 | # Default location in case no location is specified 24 | location / { 25 | try_files $uri $uri/ /index.html =404; 26 | } 27 | } -------------------------------------------------------------------------------- /root/default/nginx/README: -------------------------------------------------------------------------------- 1 | 2 | This directory includes the nginx configuration. 3 | 4 | All changes on files in this folder will get active immediately. 5 | There is a file watcher (inotify) which will allow nginx to perform a hot reload if your changes are valid. 6 | If changes are not valid nginx will not load them. To troubleshoot them you can open the docker logs ot `/config/logs/nginx/reload.log`, all details will be visible there. 7 | 8 | If you add new files/folders in the root folder make sure to add them in the related config files so nginx can load them. 9 | If you add new files in the existing folders, they will be included by default because the folders are already included in the configguration. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /install/etc/crontabs/root: -------------------------------------------------------------------------------- 1 | # do daily/weekly/monthly maintenance 2 | # min hour day month weekday command 3 | */15 * * * * run-parts /etc/periodic/15min 4 | 0 * * * * run-parts /etc/periodic/hourly 5 | 0 2 * * * run-parts /etc/periodic/daily 6 | 0 3 * * 6 run-parts /etc/periodic/weekly 7 | 0 5 1 * * run-parts /etc/periodic/monthly 8 | 30 2 * * * /etc/scripts.d/renew-acme.sh 9 | 0 */3 * * * /etc/scripts.d/refresh-agent.sh 10 | 0 1 * * 6 /etc/scripts.d/refresh-agent-weekly.sh 11 | 0 2 * * 6 /etc/scripts.d/scan-filesystem.sh 12 | -------------------------------------------------------------------------------- /root/default/nginx/conf.d/deny-tor-proxies.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains all known active TOR proxies to block them on this webserver as TOR ips are known to be used for attacks 3 | 4 | # it will be available in all sites if you include one of the following two examples inside your server block 5 | # The first example is the best as it loads all "*.conf" files from the directory 6 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 7 | # Make sure to remove the leading "#" from the specific line 8 | 9 | # Examples for server block 10 | # include /config/nginx/sites-conf.d/*.conf; 11 | # include /config/nginx/sites-conf.d/filename.conf; 12 | 13 | 14 | # Below is a list of all known TOR endpoints which might be active currently -------------------------------------------------------------------------------- /root/default/nginx/conf.d/deny-bad-ip-addresses.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains ip adresses with bad behavior that were listed on firehol within the last 30 days 3 | 4 | # it will be available in all sites if you include one of the following two examples inside your server block 5 | # The first example is the best as it loads all "*.conf" files from the directory 6 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 7 | # Make sure to remove the leading "#" from the specific line 8 | 9 | # Examples for server block 10 | # include /config/nginx/sites-conf.d/*.conf; 11 | # include /config/nginx/sites-conf.d/filename.conf; 12 | 13 | 14 | # Below is a list of all bad ip addresses listed on the firehol abuser list within the last 30 days -------------------------------------------------------------------------------- /root/default/nginx/sites-conf.d/geoip.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains the server block related setttings of the geoip blocking/allow mapping 3 | 4 | # it will be available in all sites if you include one of the following two examples inside your server block 5 | # The first example is the best as it loads all "*.conf" files from the directory 6 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 7 | # Make sure to remove the leading "#" from the specific line 8 | 9 | # Examples for server block 10 | # include /config/nginx/sites-conf.d/*.conf; 11 | # include /config/nginx/sites-conf.d/filename.conf; 12 | 13 | # If the geoip mapping returns no for the current client ip, block the request and return 444 14 | if ($allowed_country = no) { 15 | return 444; 16 | } -------------------------------------------------------------------------------- /root/default/nginx/sites-conf.d/robots.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains a location block for robots 3 | 4 | # it will be available in all sites if you include one of the following two examples inside your server block 5 | # The first example is the best as it loads all "*.conf" files from the directory 6 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 7 | # Make sure to remove the leading "#" from the specific line 8 | 9 | # Examples for server block 10 | # include /config/nginx/sites-conf.d/*.conf; 11 | # include /config/nginx/sites-conf.d/filename.conf; 12 | 13 | 14 | # Location block for robots, to tell them to ignore this page, if they ignore this we block them ;) 15 | location = /robots.txt { 16 | auth_basic off; 17 | root /config/www; 18 | } -------------------------------------------------------------------------------- /install/install-base-image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Enable early exit in case of errors 4 | set -e 5 | set -o pipefail 6 | 7 | 8 | # Download S6 Overlay files 9 | if [[ ${ARCH} = "x86_64" ]]; then 10 | wget --quiet https://github.com/just-containers/s6-overlay/releases/latest/download/s6-overlay-amd64-installer -O /tmp/s6-overlay-installer 11 | else 12 | wget --quiet https://github.com/just-containers/s6-overlay/releases/latest/download/s6-overlay-arm-installer -O /tmp/s6-overlay-installer 13 | fi 14 | 15 | # Install S6 overlay 16 | echo "**** Install S6 overlay ****" 17 | chmod +x /tmp/s6-overlay-installer 18 | /tmp/s6-overlay-installer / 19 | rm /tmp/s6-overlay-installer 20 | 21 | 22 | # Create user 23 | echo "**** create user and make folders ****" 24 | groupmod -g 1000 users 25 | useradd -u 911 -U -d /config -s /bin/false secproxy 26 | usermod -G users secproxy 27 | mkdir -p \ 28 | /config \ 29 | /default -------------------------------------------------------------------------------- /root/default/nginx/conf.d/deny-bad-referers.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains all known bad bad referers. The list is updated automatically so do not modify within this file. 3 | 4 | # it will be available in all sites if you include one of the following two examples inside your server block 5 | # The first example is the best as it loads all "*.conf" files from the directory 6 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 7 | # Make sure to remove the leading "#" from the specific line 8 | 9 | # Examples for server block 10 | # include /config/nginx/sites-conf.d/*.conf; 11 | # include /config/nginx/sites-conf.d/filename.conf; 12 | 13 | 14 | # Below is a list of all known bad referers 15 | map $http_user_agent $bad_referer { 16 | 17 | # Default will allow all referers which are not known 18 | default 0; 19 | 20 | # Referers below will be blocked 21 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report to help us improvethis image 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Current environment** 14 | - Architecture: amd64 15 | - Container Version: '....' 16 | - Docker/Kubernetes Version: '....' 17 | 18 | **To Reproduce** 19 | Steps to reproduce the behavior: 20 | 1. Go to '...' 21 | 2. Change configuration to '....' 22 | 3. Restart container '....' 23 | 4. See error in '....' 24 | 25 | **Expected behavior** 26 | A clear and concise description of what you expected to happen. 27 | 28 | **Screenshots** 29 | If applicable, add screenshots to help explain your problem. 30 | 31 | **Log files** 32 | Add log files and console output related to the bug. 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /root/etc/services.d/inotify/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | # Create log directory 4 | LOG_FILE=/config/logs/nginx/reload.log 5 | 6 | # Listens for file changes on nginx config files 7 | inotifywait -rq --event close_write,modify,moved_from,moved_to,create,delete /config/nginx | 8 | while read -r path action file; do 9 | if [[ "$file" =~ .*conf$ ]] || [[ "$file" =~ .*types$ ]] || [[ "$file" =~ .*pem$ ]] || [[ "$path" =~ .*/sites-available/$ ]]; then 10 | if /usr/sbin/nginx -c /config/nginx/nginx.conf -t 2>&1 | tee -a $LOG_FILE; then 11 | echo "$(date "+%F %T") Changes to nginx config detected and validated, reloading nginx" | tee -a $LOG_FILE 12 | /usr/sbin/nginx -c /config/nginx/nginx.conf -s reload 2>&1 | tee -a $LOG_FILE 13 | else 14 | echo "$(date "+%F %T") Changes to nginx config detected but validation failed, skipping nginx reload. Please fix the configuration." | tee -a $LOG_FILE 15 | fi 16 | fi 17 | done 18 | -------------------------------------------------------------------------------- /root/default/nginx/conf.d/deny-bad-user-agents.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains all known bad user agents which might try to scan your webpage. The list is updated automatically so do not modify within this file. 3 | 4 | # it will be available in all sites if you include one of the following two examples inside your server block 5 | # The first example is the best as it loads all "*.conf" files from the directory 6 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 7 | # Make sure to remove the leading "#" from the specific line 8 | 9 | # Examples for server block 10 | # include /config/nginx/sites-conf.d/*.conf; 11 | # include /config/nginx/sites-conf.d/filename.conf; 12 | 13 | 14 | # Below is a list of all known bad user agents which might try to scan your webpage 15 | map $http_user_agent $bad_user_agent { 16 | 17 | # Default will allow all bots which are not known 18 | default 0; 19 | 20 | # User agents below will be blocked 21 | } -------------------------------------------------------------------------------- /root/etc/logrotate.d/acme: -------------------------------------------------------------------------------- 1 | /config/logs/acme.sh/*.log { 2 | 3 | # rotate log files daily, weekly... 4 | weekly 5 | 6 | # keep X retentions versions 7 | rotate 52 8 | 9 | # uncomment this if you want your log files compressed 10 | compress 11 | 12 | # Postpone compression of the previous log file to the next rotation cycle 13 | delaycompress 14 | 15 | # create new (empty) log files after rotating old ones 16 | #create 17 | 18 | # If the log file is missing, go on to the next one without issuing an error message 19 | missingok 20 | 21 | # Do not archive old versions of log files with date extension 22 | nodateext 23 | 24 | # Do not rotate the log if it is empty 25 | notifempty 26 | 27 | # If sharedscript is specified, the scripts are only run once, no matter how many logs match the wildcarded pattern 28 | sharedscripts 29 | 30 | # Content to execute after script execution 31 | #postrotate 32 | #endscript 33 | 34 | su secproxy secproxy 35 | } -------------------------------------------------------------------------------- /root/etc/logrotate.d/nginx: -------------------------------------------------------------------------------- 1 | /config/logs/nginx/*.log { 2 | 3 | # rotate log files daily, weekly... 4 | weekly 5 | 6 | # keep X retentions versions 7 | rotate 4 8 | 9 | # uncomment this if you want your log files compressed 10 | compress 11 | 12 | # Postpone compression of the previous log file to the next rotation cycle 13 | #delaycompress 14 | 15 | # create new (empty) log files after rotating old ones 16 | create 17 | 18 | # If the log file is missing, go on to the next one without issuing an error message 19 | missingok 20 | 21 | # Do not archive old versions of log files with date extension 22 | nodateext 23 | 24 | # Do not rotate the log if it is empty 25 | notifempty 26 | 27 | # If sharedscript is specified, the scripts are only run once, no matter how many logs match the wildcarded pattern 28 | sharedscripts 29 | 30 | # Content to execute after script execution 31 | #postrotate 32 | #endscript 33 | 34 | su secproxy secproxy 35 | } -------------------------------------------------------------------------------- /root/etc/logrotate.d/clamav: -------------------------------------------------------------------------------- 1 | /config/logs/clamav/*.log { 2 | 3 | # rotate log files daily, weekly... 4 | weekly 5 | 6 | # keep X retentions versions 7 | rotate 4 8 | 9 | # uncomment this if you want your log files compressed 10 | #compress 11 | 12 | # Postpone compression of the previous log file to the next rotation cycle 13 | #delaycompress 14 | 15 | # create new (empty) log files after rotating old ones 16 | #create 17 | 18 | # If the log file is missing, go on to the next one without issuing an error message 19 | missingok 20 | 21 | # Do not archive old versions of log files with date extension 22 | nodateext 23 | 24 | # Do not rotate the log if it is empty 25 | notifempty 26 | 27 | # If sharedscript is specified, the scripts are only run once, no matter how many logs match the wildcarded pattern 28 | sharedscripts 29 | 30 | # Content to execute after script execution 31 | #postrotate 32 | #endscript 33 | 34 | su secproxy secproxy 35 | } -------------------------------------------------------------------------------- /root/default/nginx/sites-conf.d/acme.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains a location block for the acme Let's Encrypt challenge, this is required for all server blocks 3 | 4 | # it will be available in all sites if you include one of the following two examples inside your server block 5 | # The first example is the best as it loads all "*.conf" files from the directory 6 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 7 | # Make sure to remove the leading "#" from the specific line 8 | 9 | # Examples for server block 10 | # include /config/nginx/sites-conf.d/*.conf; 11 | # include /config/nginx/sites-conf.d/filename.conf; 12 | 13 | 14 | # Location block for Let's Encrypt acme challenge, required for the certificate creation process 15 | location /.well-known/acme-challenge/ { 16 | set $oidc_enabled "false"; 17 | allow all; 18 | default_type text/plain; 19 | alias /config/www/acme_root/.well-known/acme-challenge/; 20 | autoindex on; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /install/etc/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | # Default nginx configuration to validate new configurations before sending to productive endpoint 3 | 4 | #user nobody; 5 | worker_processes 1; 6 | 7 | events { 8 | worker_connections 1024; 9 | } 10 | 11 | http { 12 | include mime.types; 13 | default_type application/octet-stream; 14 | sendfile on; 15 | keepalive_timeout 65; 16 | 17 | # Include config directory 18 | include /etc/nginx/conf.d/*.conf; 19 | 20 | # Disable logging for this temporary instance 21 | access_log off; 22 | error_log off; 23 | 24 | server { 25 | 26 | # Server to validate new updated configurations before loading to productive 27 | listen 127.0.0.1:80; 28 | server_name localhost; 29 | 30 | # Include config directory 31 | include /etc/nginx/sites-conf.d/*.conf; 32 | 33 | # Default location block 34 | location / { 35 | root html; 36 | index index.html index.htm; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /root/default/nginx/modsec.d/tools/av-scanning/runav.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # runav.pl 4 | # Copyright (c) 2004-2011 Trustwave 5 | # 6 | # This script is an interface between ModSecurity and its 7 | # ability to intercept files being uploaded through the 8 | # web server, and ClamAV 9 | 10 | 11 | $CLAMSCAN = "/usr/bin/clamscan"; 12 | 13 | if ($#ARGV != 0) { 14 | print "Usage: modsec-clamscan.pl \n"; 15 | exit; 16 | } 17 | 18 | my ($FILE) = shift @ARGV; 19 | 20 | $cmd = "$CLAMSCAN --stdout --disable-summary $FILE"; 21 | $input = `$cmd`; 22 | $input =~ m/^(.+)/; 23 | $error_message = $1; 24 | 25 | $output = "0 Unable to parse clamscan output [$1]"; 26 | 27 | if ($error_message =~ m/: Empty file\.?$/) { 28 | $output = "1 empty file"; 29 | } 30 | elsif ($error_message =~ m/: (.+) ERROR$/) { 31 | $output = "0 clamscan: $1"; 32 | } 33 | elsif ($error_message =~ m/: (.+) FOUND$/) { 34 | $output = "0 clamscan: $1"; 35 | } 36 | elsif ($error_message =~ m/: OK$/) { 37 | $output = "1 clamscan: OK"; 38 | } 39 | 40 | print "$output\n"; 41 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # Main image 3 | FROM alpine:3.14.3 4 | 5 | # Software versions to use 6 | ARG NGINX_VERSION=1.21.5 7 | ARG MODSECURITY_VERSION=3.0.6 8 | ARG OWASP_VERSION=v3.4/dev 9 | 10 | # Variable to specify if running on GitHub Action or localy (prevent GitHub action resource issues) 11 | ARG RUNS_ON_GITHUB=false 12 | 13 | LABEL maintainer="flo-mic" \ 14 | description="Secure-Proxy based on nginx with modsecurity, fail2ban, clamav and much more." 15 | 16 | # environment variables 17 | ENV ARCH="x86_64" \ 18 | MIRROR=http://dl-cdn.alpinelinux.org/alpine \ 19 | PS1="$(whoami)@$(hostname):$(pwd)\\$ " \ 20 | HOME="/root" \ 21 | TERM="xterm" 22 | 23 | #Copy Install scripts 24 | COPY install/ /tmp/secproxy-installer/ 25 | 26 | # Install image components 27 | RUN ./tmp/secproxy-installer/install.sh && rm -rf /tmp/* 28 | 29 | # Copy/replace root files 30 | COPY root/ / 31 | 32 | # Specify mount volumes 33 | VOLUME /config 34 | 35 | # Expose needed ports 36 | EXPOSE 80 443 37 | 38 | # Entrypoint of S6 overlay 39 | ENTRYPOINT [ "/init" ] 40 | 41 | -------------------------------------------------------------------------------- /root/etc/logrotate.d/fail2ban: -------------------------------------------------------------------------------- 1 | /config/logs/fail2ban/fail2ban.log { 2 | 3 | # rotate log files daily, weekly... 4 | weekly 5 | 6 | # keep X retentions versions 7 | rotate 4 8 | 9 | # uncomment this if you want your log files compressed 10 | compress 11 | 12 | # Postpone compression of the previous log file to the next rotation cycle 13 | delaycompress 14 | 15 | # create new (empty) log files after rotating old ones 16 | #create 17 | 18 | # If the log file is missing, go on to the next one without issuing an error message 19 | missingok 20 | 21 | # Do not archive old versions of log files with date extension 22 | nodateext 23 | 24 | # Do not rotate the log if it is empty 25 | #notifempty 26 | 27 | # If sharedscript is specified, the scripts are only run once, no matter how many logs match the wildcarded pattern 28 | #sharedscripts 29 | 30 | # Content to execute after script execution 31 | postrotate 32 | /usr/bin/fail2ban-client flushlogs 1>/dev/null || true 33 | endscript 34 | 35 | su secproxy secproxy 36 | } -------------------------------------------------------------------------------- /root/etc/logrotate.d/refresh-agent: -------------------------------------------------------------------------------- 1 | /config/logs/refresh-agent/*.log { 2 | 3 | # rotate log files daily, weekly... 4 | weekly 5 | 6 | # keep X retentions versions 7 | rotate 4 8 | 9 | # uncomment this if you want your log files compressed 10 | compress 11 | 12 | # Postpone compression of the previous log file to the next rotation cycle 13 | delaycompress 14 | 15 | # create new (empty) log files after rotating old ones 16 | #create 17 | 18 | # If the log file is missing, go on to the next one without issuing an error message 19 | missingok 20 | 21 | # Do not archive old versions of log files with date extension 22 | nodateext 23 | 24 | # Do not rotate the log if it is empty 25 | #notifempty 26 | 27 | # If sharedscript is specified, the scripts are only run once, no matter how many logs match the wildcarded pattern 28 | #sharedscripts 29 | 30 | # Content to execute after script execution 31 | postrotate 32 | /usr/bin/fail2ban-client flushlogs 1>/dev/null || true 33 | endscript 34 | 35 | su secproxy secproxy 36 | } -------------------------------------------------------------------------------- /root/etc/cont-init.d/50-clamav-config: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | # Create clamav socket directory if not exists 4 | mkdir -p /run/clamav 5 | chown clamav:clamav /run/clamav 6 | 7 | # Create clamav log directory 8 | mkdir -p /config/logs/clamav 9 | touch /config/logs/clamav/freshclam.log 10 | touch /config/logs/clamav/clamav.log 11 | 12 | # Create symlink for clamav log in case a deamon uses the default directory 13 | rm -rf /var/log/clamav 14 | ln -s /config/logs/clamav /var/log/clamav 15 | chown clamav:clamav /var/log/clamav/*.* 16 | sleep 1 17 | 18 | # Create symlink for clamav config files and AV definitions 19 | mkdir -p /config/clamav/definitions 20 | rm -rf /etc/clamav/clamd.conf 21 | rm -rf /etc/clamav/freshclam.conf 22 | rm -rf /var/lib/clamav 23 | ln -s /config/clamav/clamd.conf /etc/clamav/clamd.conf 24 | ln -s /config/clamav/freshclam.conf /etc/clamav/freshclam.conf 25 | ln -s /config/clamav/definitions /var/lib/clamav 26 | chown clamav:clamav /var/lib/clamav 27 | 28 | # Force clamav definition update if no definitions exist (first start) 29 | if [ ! -f /config/clamav/definitions/main.cld ]; then 30 | echo "**** Clamav definitions missing, downloading them. This will some time... ****" 31 | freshclam 32 | fi 33 | -------------------------------------------------------------------------------- /install/install-crowdsec.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Enable early exit in case of errors 4 | set -e 5 | set -o pipefail 6 | 7 | # Move to work dir 8 | cd /tmp 9 | 10 | echo "Installing crowdsec bouncers" 11 | 12 | 13 | # Install lua cs bouncer 14 | git clone https://github.com/crowdsecurity/lua-cs-bouncer /tmp/lua_cs_bouncer 15 | cd /tmp/lua_cs_bouncer 16 | mkdir -p /usr/local/lib/lua/crowdsec 17 | cp lib/*.lua /usr/local/lib/lua/crowdsec 18 | cp template.conf /usr/local/lib/lua/crowdsec/crowdsec.conf 19 | 20 | # Correct lua path issues with current lua setup 21 | sed -i 's/require "lrucache"/require "resty.lrucache"/' /usr/local/lib/lua/crowdsec/CrowdSec.lua 22 | sed -i 's/require "config"/require "crowdsec.config"/' /usr/local/lib/lua/crowdsec/CrowdSec.lua 23 | cd /tmp 24 | 25 | 26 | # Install nginx bouncer 27 | git clone https://github.com/crowdsecurity/cs-nginx-bouncer /tmp/nginx_cs_bouncer 28 | mkdir -p /default/nginx/conf.d 29 | cd /tmp/nginx_cs_bouncer 30 | cp nginx/access.lua /usr/local/lib/lua/crowdsec/access.lua 31 | 32 | # Correct lua path and local declaration issues with current lua setup 33 | sed -i 's/ok, err = require "CrowdSec"/local ok, err = require "crowdsec.CrowdSec"/' /usr/local/lib/lua/crowdsec/access.lua 34 | 35 | cd /tmp 36 | -------------------------------------------------------------------------------- /root/default/nginx/conf.d/geoip.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains the mapping of the geoip database to allowed/blocked countries 3 | 4 | # it will be available in all sites if you include one of the following two examples inside your server block 5 | # The first example is the best as it loads all "*.conf" files from the directory 6 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 7 | # Make sure to remove the leading "#" from the specific line 8 | 9 | # Examples for server block 10 | # include /config/nginx/sites-conf.d/*.conf; 11 | # include /config/nginx/sites-conf.d/filename.conf; 12 | 13 | # GeoIP database file 14 | geoip2 /usr/share/GeoIP/geoip.mmdb { 15 | auto_reload 5m; 16 | $geoip2_metadata_country_build metadata build_epoch; 17 | $geoip2_data_country_code country iso_code; 18 | } 19 | 20 | # GeoIP allow/blocking list 21 | map $geoip2_data_country_code $allowed_country { 22 | 23 | # Default action to choose if does not come from a country listed below. Default is yes to allow the request 24 | default yes; 25 | 26 | # Uncomment one of the below lines or add your own ones to block/allow a country. 27 | # You need the two letter country code and specify yes (allow) or no (block) 28 | #CN no; 29 | #RU no; 30 | #US no; 31 | } -------------------------------------------------------------------------------- /root/default/fail2ban/action.d/sendmail-common.local: -------------------------------------------------------------------------------- 1 | 2 | # Custom sendmail actions, will not be visible for end users to avoid irritations 3 | 4 | [Definition] 5 | 6 | actionstop = 7 | actionstart = 8 | 9 | actionban = printf %%b "Subject: [Fail2Ban] : BANNED IP ! 10 | Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"` 11 | From: <> 12 | To: \n 13 | Hello,\n 14 | The jail has banned ip after attempts against .\n 15 | Here is some info about the IP: https://db-ip.com/ \n 16 | Lines containing IP : \n 17 | `grep '' ` \n 18 | Regards, 19 | Secure-Proxy" | 20 | 21 | actionunban = printf %%b "Subject: [Fail2Ban] : UNBANNED IP 22 | Date: `LC_ALL=C date +"%%a, %%d %%h %%Y %%T %%z"` 23 | From: <> 24 | To: \n 25 | Hello, \n 26 | Fail2ban has unbanned ip https://db-ip.com/ successfully. \n 27 | Regards, 28 | Secure-Proxy" | 29 | 30 | [Init] 31 | 32 | # Your system mail command 33 | # 34 | mailcmd = /usr/sbin/sendmail -t -v -H 'exec openssl s_client -quiet -connect -starttls smtp' -au -ap -f 35 | -------------------------------------------------------------------------------- /root/etc/scripts.d/refresh-agent-weekly.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | # Make required folders if not already exist 4 | mkdir -p /config/logs/refresh-agent 5 | mkdir -p /tmp/refresh-agent 6 | 7 | LOG_FILE=/config/logs/refresh-agent/refresh-agent.log 8 | 9 | echo "**** Refreshing GeoIP databases and crowdsec hub configurations... ****" >> tee -a $LOG_FILE 10 | 11 | 12 | # Refresh current geoip-country-lite database 13 | echo "$(date "+%F %T") Updating GeoIP database" >> $LOG_FILE 14 | curl -sL "https://download.db-ip.com/free/dbip-country-lite-$(date +%Y-%m).mmdb.gz" \ 15 | -o /tmp/refresh-agent/geoip.mmdb.gz 2>&1 | tee -a $LOG_FILE 16 | 17 | # Check if GeoIP db for this month was already released, if not download version of previous month 18 | GEOIP_DB_SIZE=$(du -sb "/tmp/refresh-agent/geoip.mmdb.gz" | awk '{ print $1 }') 19 | if [[ $GEOIP_DB_SIZE -le 2 ]]; then 20 | echo "GeoIP DB for \"$(date +%Y-%m)\" not released. Unsing version of previous month." >> $LOG_FILE 21 | last_release=$(date -d "$date -1 months" +"%Y-%m") 22 | curl -sL "https://download.db-ip.com/free/dbip-country-lite-$last_release.mmdb.gz" \ 23 | -o /tmp/refresh-agent/geoip.mmdb.gz 2>&1 | tee -a $LOG_FILE 24 | fi 25 | gunzip -f /tmp/refresh-agent/geoip.mmdb.gz 26 | mkdir -p /usr/share/GeoIP 27 | mv -f /tmp/refresh-agent/geoip.mmdb /usr/share/GeoIP/geoip.mmdb 28 | 29 | 30 | # Remove temporary files 31 | rm -r /tmp/refresh-agent 32 | -------------------------------------------------------------------------------- /root/default/nginx/conf.d/logging.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains global logging settings 3 | 4 | # It is available in all sites by default as this is already loaded in the http directive. 5 | # If you do not want this configuration to be active, we recommend to modify the http directive in your nginx.conf file. 6 | # A change on the http directive affects all pages so be carefull with this and test it on a seperate instance first. 7 | # To change this you will need to remove the line "include /config/nginx/conf.d/*.conf;" from nginx.conf. 8 | # Make sure to add all required files manually to the http directive before doing this or you break the functionality of all pages. 9 | # You can add the individual files by adding the following to your nginx.conf file. 10 | # Make sure to replace "filename.conf" with the realname and remove "#" at the beginning to make this active. 11 | 12 | # Examples to add only individual configs to the http directive 13 | # include /config/nginx/conf.d/filename.conf; 14 | 15 | 16 | # Configures default error logger. 17 | error_log /config/logs/nginx/error.log warn; 18 | 19 | # Sets the path, format, and configuration for a buffered log write. 20 | access_log /config/logs/nginx/access.log; 21 | 22 | 23 | # Specifies the main log format. 24 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 25 | '$status $body_bytes_sent "$http_referer" ' 26 | '"$http_user_agent" "$http_x_forwarded_for"'; -------------------------------------------------------------------------------- /root/default/nginx/conf.d/lua.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains the mapping of the geoip database to allowed/blocked countries 3 | 4 | # it will be available in all sites if you include one of the following two examples inside your server block 5 | # The first example is the best as it loads all "*.conf" files from the directory 6 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 7 | # Make sure to remove the leading "#" from the specific line 8 | 9 | # Examples for server block 10 | # include /config/nginx/sites-conf.d/*.conf; 11 | # include /config/nginx/sites-conf.d/filename.conf; 12 | 13 | # Lua package paths, importand to specify for lua resty core module 14 | lua_package_path "/usr/local/lib/lua/?.lua;;"; 15 | 16 | # Define SSL verify depth, usefull if your certificate was issued by an intermediate certificate 17 | lua_ssl_verify_depth 5; 18 | 19 | # Trusted certificates by lua, defaulty to the system ca-certificates 20 | lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; 21 | 22 | # Init lua with resty core module for crowdsec and modsecurity file scan (clamav) 23 | init_by_lua_block { 24 | require "resty.core" 25 | collectgarbage("collect") -- just to collect any garbage 26 | 27 | local crowdsec_ngx_script=io.open("/usr/local/lib/lua/crowdsec_nginx.lua","r") 28 | if crowdsec_ngx_script~=nil then 29 | io.close(crowdsec_ngx_script) 30 | require "crowdsec_nginx" 31 | end 32 | } 33 | -------------------------------------------------------------------------------- /root/default/nginx/sites-conf.d/error-pages.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains custom error pages for a server block 3 | 4 | # it will be available in all sites if you include one of the following two examples inside your server block 5 | # The first example is the best as it loads all "*.conf" files from the directory 6 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 7 | # Make sure to remove the leading "#" from the specific line 8 | 9 | # Examples for server block 10 | # include /config/nginx/sites-conf.d/*.conf; 11 | # include /config/nginx/sites-conf.d/filename.conf; 12 | 13 | # If http request was send to https port try redirecting internaly 14 | error_page 497 https://$server_name:$server_port$request_uri; 15 | 16 | # Error codes which use a custom error page 17 | error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 425 426 428 429 431 451 500 501 502 503 504 505 506 507 508 510 511 @error; 18 | 19 | # location of the custom error page and related security settings 20 | location @error { 21 | ssi on; 22 | internal; 23 | auth_basic off; 24 | root /config/www; 25 | allow all; 26 | modsecurity_rules 'SecRule REQUEST_URI "@beginsWith /" "id:1,pass,phase:2,log,ctl:ruleEngine=DetectionOnly"'; 27 | set $oidc_enabled "false"; 28 | try_files /error.html =404; 29 | break; 30 | } 31 | -------------------------------------------------------------------------------- /install/install-modsecurity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Enable early exit in case of errors 4 | set -e 5 | set -o pipefail 6 | 7 | # Move to work dir 8 | cd /tmp 9 | 10 | # Clone and install ssdeep package 11 | echo "Install ssdeep package" 12 | SSDEEP_VERSION=$(curl https://api.github.com/repos/ssdeep-project/ssdeep/releases/latest -s | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' | sed 's/release-//') 13 | wget --quiet https://github.com/ssdeep-project/ssdeep/releases/download/release-${SSDEEP_VERSION}/ssdeep-${SSDEEP_VERSION}.tar.gz 14 | tar -xzf ssdeep-${SSDEEP_VERSION}.tar.gz 15 | cd ssdeep-${SSDEEP_VERSION} 16 | ./configure 17 | make -j${CPU_CORES} 18 | make install 19 | cd /tmp 20 | 21 | 22 | # Clone ModSecurity 23 | echo "Install ModSecurity libraries" 24 | git clone -b "v${MODSECURITY_VERSION}" --depth 1 --quiet https://github.com/SpiderLabs/ModSecurity 25 | git -C /tmp/ModSecurity submodule update --init --recursive --quiet 26 | 27 | 28 | # Install Modsecurity 29 | cd "/tmp/ModSecurity" 30 | ./build.sh 31 | ./configure --with-lmdb --disable-doxygen-doc 32 | make -j${CPU_CORES} 33 | make install 34 | cd /tmp 35 | 36 | 37 | # Get OWASP Core rule set 38 | echo "Get OWASP core rule set" 39 | git clone -b "${OWASP_VERSION}" --depth 1 --quiet https://github.com/coreruleset/coreruleset.git /tmp/owasp-modsecurity-crs 40 | mkdir -p /default/nginx/modsec.d 41 | cp /tmp/owasp-modsecurity-crs/crs-setup.conf.example /default/nginx/modsec.d/crs-setup.conf 42 | cp -r /tmp/owasp-modsecurity-crs/rules /default/nginx/modsec.d/rules/ 43 | -------------------------------------------------------------------------------- /.github/workflows/BuildImage.yml: -------------------------------------------------------------------------------- 1 | # This Workflow will build a docker ocntainer an dpublis it on the github container registry 2 | name: Build image and deploy to contaienr registry 3 | 4 | on: 5 | # Run on each commit to main branch 6 | push: 7 | branches: [ main ] 8 | # Allow run on demand 9 | workflow_dispatch: 10 | 11 | env: 12 | ENDPOINT: "flo-mic/secure-proxy" 13 | VERSION: "latest" 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 21 | - uses: actions/checkout@v2 22 | 23 | - name: Build image 24 | run: | 25 | docker build --no-cache --build-arg RUNS_ON_GITHUB=true -t ${{ github.sha }} . 26 | 27 | - name: Tag image 28 | run: | 29 | docker tag ${{ github.sha }} ${ENDPOINT}:${VERSION} 30 | docker tag ${{ github.sha }} ${ENDPOINT}:${VERSION}-${{ github.sha }} 31 | docker tag ${{ github.sha }} ghcr.io/${ENDPOINT}:${VERSION} 32 | docker tag ${{ github.sha }} ghcr.io/${ENDPOINT}:${VERSION}-${{ github.sha }} 33 | 34 | - name: Login to GitHub Container Registry 35 | run: | 36 | echo "${{ secrets.GH_TOKEN }}" | docker login ghcr.io -u ${{ secrets.GH_USER }} --password-stdin 37 | 38 | - name: Push tags to GitHub Container Registry 39 | run: | 40 | docker push ghcr.io/${ENDPOINT}:${VERSION}-${{ github.sha }} 41 | docker push ghcr.io/${ENDPOINT}:${VERSION} 42 | 43 | -------------------------------------------------------------------------------- /root/default/nginx/sites-conf.d/deny-rules.conf: -------------------------------------------------------------------------------- 1 | # This configuration contains custom deny rules to block common attacks 2 | 3 | # it will be available in all sites if you include one of the following two examples inside your server block 4 | # The first example is the best as it loads all "*.conf" files from the directory 5 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 6 | # Make sure to remove the leading "#" from the specific line 7 | 8 | # Examples for server block 9 | # include /config/nginx/sites-conf.d/*.conf; 10 | # include /config/nginx/sites-conf.d/filename.conf; 11 | 12 | # Block bad user agents 13 | if ($bad_user_agent = '1') { 14 | return 444; 15 | } 16 | 17 | # Block bad referers 18 | if ($bad_referer = '1') { 19 | return 444; 20 | } 21 | 22 | # Deny access hidden files such as .htaccess, .htpasswd etc. but allow well-known locations 23 | location ~ /\.(?!well-known).* { 24 | return 444; 25 | } 26 | 27 | # Block access to git files if you use something like gitlab 28 | location ~ /\.git { 29 | return 444; 30 | } 31 | 32 | # Protect sensitive nginx files against bad misconfigurations of web root 33 | location ~* /(README|README.md|nginx.conf|ssl.conf|security-headers.conf|logging.conf|proxy.conf) { 34 | return 444; 35 | } 36 | 37 | # Block known attack locations 38 | location ~* .(globals|encode|localhost|loopback|xmlrpc|revslider|roundcube|smtp|http\:|soap|w00tw00t|display_errors|set_time_limit|allow_url_include.*disable_functions.*open_basedir|set_magic_quotes_runtime|webconfig.txt.php|file_put_contentssever_root|wlwmanifest) { 39 | return 444; 40 | } 41 | -------------------------------------------------------------------------------- /root/default/nginx/conf.d/ddos-protection.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains a basic ddos protection with limit request zones 3 | 4 | # it will be available in all sites if you include one of the following two examples inside your server block 5 | # The first example is the best as it loads all "*.conf" files from the directory 6 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 7 | # Make sure to remove the leading "#" from the specific line 8 | 9 | # Examples for server block 10 | # include /config/nginx/sites-conf.d/*.conf; 11 | # include /config/nginx/sites-conf.d/filename.conf; 12 | 13 | # Map ips to prevent ddos protection for local ip addresses 14 | geo $limit_source { 15 | default 1; 16 | 10.0.0.0/8 0; 17 | 172.0.0.0/16 0; 18 | 192.0.0.0/16 0; 19 | } 20 | 21 | # Map ip adresses and their classification to limit keys 22 | map $limit_source $limit_key { 23 | 0 ""; 24 | 1 $binary_remote_addr; 25 | } 26 | 27 | # Create a limit request zone for each client ip to limit the amount of requests per client 28 | limit_req_zone $limit_key zone=flood:50m rate=200r/s; 29 | 30 | # Create a limit connection zone for address to limit the amount of connections per client 31 | limit_conn_zone $limit_key zone=addr:50m; 32 | 33 | # Create overall limits for all requests or an address 34 | limit_req zone=flood burst=200 nodelay; 35 | 36 | # Create overall limits for all connections of an address 37 | limit_conn addr 200; 38 | 39 | # Close client header connections that are writing data too infrequently 40 | client_header_timeout 5s; 41 | 42 | # Close client body connections that are writing data too infrequently 43 | client_body_timeout 5s; 44 | -------------------------------------------------------------------------------- /root/etc/cont-init.d/10-startup-config: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | # Check for custom user group id 4 | PUID=${PUID:-911} 5 | PGID=${PGID:-911} 6 | 7 | # Update user and group id of secproxy user 8 | groupmod -o -g "$PGID" secproxy >/dev/null 9 | usermod -o -u "$PUID" secproxy >/dev/null 10 | 11 | echo '-------------------------------------' 12 | echo "User uid: $(id -u secproxy) 13 | User gid: $(id -g secproxy) 14 | -------------------------------------" 15 | 16 | # Set permission for secproxy user 17 | chown secproxy:secproxy /config 18 | chown secproxy:secproxy /default 19 | 20 | 21 | # make sure all environment variables are lower case and properly formated 22 | UPDATE_CONFIGURATION=$(echo "${UPDATE_CONFIGURATION,,}") 23 | UPDATE_CONFIGURATION=$(echo "${UPDATE_CONFIGURATION}" | sed "s/\"//g") 24 | UPDATE_SKIP_FILES=$(echo "${UPDATE_SKIP_FILES,,}") 25 | UPDATE_SKIP_FILES=$(echo "${UPDATE_SKIP_FILES}" | sed "s/\"//g") 26 | 27 | # Remove existing autoconf file 28 | rm -f /config/nginx/conf.d/autoconf.conf 29 | 30 | # Copy template files if not exist 31 | for i in $(find /default -type f) 32 | do 33 | CONFIG_PATH="$(echo $i | sed -e "s/^\/default/\/config/")" 34 | CONFIG_DIR="$(echo "$(dirname "${CONFIG_PATH}")")" 35 | 36 | # Create directory if not exists 37 | mkdir -p $CONFIG_DIR 38 | 39 | # Check if automatic update was configured 40 | if [[ ${UPDATE_CONFIGURATION} = "enabled" && ! "${UPDATE_SKIP_FILES[@]}" =~ "${CONFIG_PATH}" ]]; then 41 | # Do not overwrite files in sites-available/* 42 | if [[ ${CONFIG_PATH} != "/config/nginx/sites-available/"* ]]; then 43 | cp $i $CONFIG_PATH 44 | fi 45 | elif [[ ! -f $CONFIG_PATH ]]; then 46 | # Copy only new files if automatic update was disabled 47 | cp $i $CONFIG_PATH 48 | fi 49 | done 50 | sleep 1 -------------------------------------------------------------------------------- /root/etc/cont-init.d/20-nginx-config: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | # Create log folder 4 | mkdir -p /config/logs/nginx 5 | 6 | # Create modsecurity log 7 | touch /config/logs/nginx/modsec_audit.log 8 | 9 | # Link log folder to default location 10 | rm -rf /var/log/nginx 11 | ln -s /config/logs/nginx /var/log/nginx 12 | 13 | # Download dhparam key from https://2ton.com.au/dhtool/ 14 | if [[ ! -f "/config/nginx/dhparam4096.pem" ]] || ! grep -q 'PARAMETERS' "/config/nginx/dhparam4096.pem"; then 15 | echo "**** downloading fresh generated 4096 dhparam key ****" 16 | echo "**** this process can not guarantee a unique key ****" 17 | echo "**** for more security create a your own dhparam ****" 18 | curl -s https://2ton.com.au/dhparam/4096 > /config/nginx/dhparam4096.pem 19 | sleep 1 20 | fi 21 | if ! grep -q 'PARAMETERS' "/config/nginx/dhparam4096.pem"; then 22 | # Check again for dhparam to avoid long running generation 23 | sleep 1 24 | if ! grep -q 'PARAMETERS' "/config/nginx/dhparam4096.pem"; then 25 | echo "Generating dhparams.pem. This will take a long time. Do not stop the container until this process is completed." 26 | openssl dhparam -out /config/nginx/dhparam4096.pem 4096 27 | fi 28 | fi 29 | 30 | # Generate nginx log files 31 | if [[ ! -f /config/logs/nginx/error.log ]]; then 32 | touch /config/logs/nginx/error.log 33 | fi 34 | if [[ ! -f /config/logs/nginx/access.log ]]; then 35 | touch /config/logs/nginx/access.log 36 | fi 37 | 38 | # Make av scanner script executable 39 | chmod +x /config/nginx/modsec.d/tools/av-scanning/runav.pl 40 | 41 | # Download/update bad bot, blocking and serval other lists 42 | chmod +x /etc/scripts.d/refresh-agent.sh 43 | chmod +x /etc/scripts.d/refresh-agent-weekly.sh 44 | /etc/scripts.d/refresh-agent.sh 45 | /etc/scripts.d/refresh-agent-weekly.sh 46 | sleep 1 47 | -------------------------------------------------------------------------------- /root/etc/cont-init.d/80-template-config: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | # Copy template files again if not exists to cache also updated files from init scripts 4 | for i in $(find /default -type f) 5 | do 6 | CONFIG_PATH="$(echo $i | sed -e "s/^\/default/\/config/")" 7 | CONFIG_DIR="$(echo "$(dirname "${CONFIG_PATH}")")" 8 | 9 | # Create directory if not exists 10 | mkdir -p $CONFIG_DIR 11 | 12 | # Copy file if not exists 13 | if [[ ! -f $CONFIG_PATH ]]; then 14 | cp $i $CONFIG_PATH 15 | fi 16 | done 17 | sleep 1 18 | 19 | # Global ignore list for template updates as they might be updated from an refresh script on regular basis anyway 20 | TEMPLATE_IGNORE_ARRAY=( 21 | "/default/nginx/conf.d/deny-bad-ip-addresses.conf" 22 | "/default/nginx/conf.d/deny-tor-proxies.conf" 23 | "/default/nginx/conf.d/deny-bad-referers.conf" 24 | "/default/nginx/conf.d/deny-bad-user-agents.conf" 25 | "/default/nginx/conf.d/autoconf.conf" 26 | ) 27 | 28 | # Check for configuration updates that need to be performed from the user manually to prevent any breaking changes 29 | for i in $(find /default/ -type f) 30 | do 31 | #Only compare files when they are not in the ignore list 32 | if [[ ! "${TEMPLATE_IGNORE_ARRAY[@]}" =~ "${i}" ]]; then 33 | CONFIG_PATH="$(echo $i | sed -e "s/^\/default/\/config/")" 34 | FILE_EQUAL="$(cmp -s ${CONFIG_PATH} ${i}; echo $?)" 35 | if [[ $FILE_EQUAL -ne 0 ]]; then 36 | confs_changed="${CONFIG_PATH}\n${confs_changed}" 37 | fi 38 | fi 39 | done 40 | 41 | # Inform about available updates 42 | if [ -n "$confs_changed" ]; then 43 | echo "**** Changes in default configurations detected ****" 44 | echo "**** Remove the following files to get defaults ****" 45 | echo "**** Save your custom settings before deletion ****" 46 | echo -e "${confs_changed}" 47 | fi -------------------------------------------------------------------------------- /root/default/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | # /etc/nginx/nginx.conf 3 | 4 | # Define user for nxing 5 | user secproxy; 6 | 7 | # Define worker process for nginx 8 | pid /run/nginx.pid; 9 | 10 | # Set number of worker processes automatically based on number of CPU cores. 11 | worker_processes 4; 12 | 13 | # Allow running in detached mode due to running in docker as multi service 14 | daemon off; 15 | 16 | # Enables the use of JIT for regular expressions to speed-up their processing. 17 | pcre_jit on; 18 | 19 | events { 20 | # The maximum number of simultaneous connections that can be opened by 21 | # a worker process. 22 | worker_connections 1024; 23 | } 24 | 25 | http { 26 | # Includes mapping of file name extensions to MIME types of responses 27 | # and defines the default type. 28 | include /config/nginx/mime.types; 29 | default_type application/octet-stream; 30 | 31 | # Include custom configs from conf.d 32 | include /config/nginx/conf.d/*.conf; 33 | 34 | # Name servers used to resolve names of upstream servers into addresses, uses docker dns resolver 35 | resolver 127.0.0.11 valid=30s ipv6=off; # Docker DNS Server 36 | 37 | # Increase current variable hash table size, importand for map tables of blocking lists 38 | variables_hash_bucket_size 2048; 39 | 40 | # Increase max variable hash table size, importand for map tables of blocking lists 41 | variables_hash_max_size 4096; 42 | 43 | # Disable check of maximum accepted body size of a client request 44 | client_max_body_size 0; 45 | 46 | # Sendfile copies data between one FD and other from within the kernel (improves performance) 47 | sendfile on; 48 | 49 | # Causes nginx to attempt to send its HTTP response head in one packet, instead of partial frames 50 | tcp_nopush on; 51 | 52 | # Includes virtual hosts configs. 53 | include /config/nginx/sites-available/*; 54 | 55 | } -------------------------------------------------------------------------------- /root/default/nginx/conf.d/modsecurity.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains the default modsecurity configuration 3 | 4 | # It is available in all sites by default as this is already loaded in the http directive. 5 | # If you do not want this configuration to be active, we recommend to modify the http directive in your nginx.conf file. 6 | # A change on the http directive affects all pages so be carefull with this and test it on a seperate instance first. 7 | # To change this you will need to remove the line "include /config/nginx/conf.d/*.conf;" from nginx.conf. 8 | # Make sure to add all required files manually to the http directive before doing this or you break the functionality of all pages. 9 | # You can add the individual files by adding the following to your nginx.conf file. 10 | # Make sure to replace "filename.conf" with the realname and remove "#" at the beginning to make this active. 11 | 12 | # Examples to add only individual configs to the http directive 13 | # include /config/nginx/conf.d/filename.conf; 14 | 15 | 16 | # Main configuration file of ModSecurity. 17 | # It takes care that all needed configuration files are properly loaded. 18 | 19 | 20 | # Enable ModSecurity Web Application Firewall 21 | modsecurity on; 22 | modsecurity_rules ' 23 | 24 | # Load Modsecurity main configuration 25 | Include /config/nginx/modsec.d/modsecurity.conf 26 | 27 | # Load OWASP core rule set main config 28 | Include /config/nginx/modsec.d/crs-setup.conf 29 | 30 | # Load OWASP core rule set 31 | Include /config/nginx/modsec.d/rules/*.conf 32 | 33 | # Custom Modsecurity AV scanner rule 34 | SecRule FILES_TMPNAMES "@inspectFile /config/nginx/modsec.d/tools/av-scanning/runav.pl" \ 35 | "id:400001, \ 36 | phase:2, \ 37 | t:none, \ 38 | deny, \ 39 | log, \ 40 | msg:Infected File upload detected, \ 41 | tag:MALICIOUS_SOFTWARE/VIRUS" 42 | '; -------------------------------------------------------------------------------- /root/default/nginx/sites-conf.d/openid-connect.conf: -------------------------------------------------------------------------------- 1 | # This configuration contains a lua script to perform openid-connect authentications for SSO 2 | 3 | # it will be available in all sites if you include one of the following two examples inside your server block 4 | # The first example is the best as it loads all "*.conf" files from the directory 5 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 6 | # Make sure to remove the leading "#" from the specific line 7 | 8 | # Examples for server block 9 | # include /config/nginx/sites-conf.d/*.conf; 10 | # include /config/nginx/sites-conf.d/filename.conf; 11 | 12 | # Preset variable oidc to avoid errors due to uninitialized variabe 13 | set $oidc_enabled "false"; 14 | 15 | # Lua rule for OpenID Connect authentication 16 | access_by_lua ' 17 | 18 | if ngx.var.oidc_enabled == "true" then 19 | local opts = { 20 | redirect_uri = ngx.var.scheme .. "://" .. ngx.var.server_name .. "/redirect_uri", 21 | accept_none_alg = true, 22 | discovery = ngx.var.oidc_discovery_url, 23 | client_id = ngx.var.oidc_client_id, 24 | client_secret = ngx.var.oidc_client_secret, 25 | ssl_verify = "yes", 26 | logout_path = ngx.var.oidc_logout_path, 27 | post_logout_redirect_uri = ngx.var.oidc_post_logout_redirect_uri, 28 | revoke_tokens_on_logout = true, 29 | redirect_after_logout_with_id_token_hint = false, 30 | scope = "openid email profile", 31 | session_contents = {id_token=true}, 32 | renew_access_token_on_expiry = true 33 | } 34 | -- call introspect for OAuth 2.0 Bearer Access Token validation 35 | local oidc = require("resty.openidc") 36 | local res, err, _, session = oidc.authenticate(opts) 37 | 38 | if err then 39 | ngx.status = 403 40 | ngx.say(err) 41 | ngx.exit(ngx.HTTP_FORBIDDEN) 42 | end 43 | session:hide() 44 | end 45 | '; -------------------------------------------------------------------------------- /root/default/nginx/sites-conf.d/ssl.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains ssl settings for a server block, it loads also the letsencrypt certificate 3 | 4 | # it will be available in all sites if you include one of the following two examples inside your server block 5 | # The first example is the best as it loads all "*.conf" files from the directory 6 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 7 | # Make sure to remove the leading "#" from the specific line 8 | 9 | # Examples for server block 10 | # include /config/nginx/sites-conf.d/*.conf; 11 | # include /config/nginx/sites-conf.d/filename.conf; 12 | 13 | 14 | # Enables the specified protocols. Default is TLSv1.3 and TLSv1.2. 15 | ssl_protocols TLSv1.2 TLSv1.3; 16 | 17 | # Supported SSL Chippers for a new connection 18 | ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; 19 | 20 | # Specifies that our cipher suits should be preferred over client ciphers. Default is 'on'. 21 | ssl_prefer_server_ciphers on; 22 | 23 | # Certificates 24 | ssl_certificate $certfile; 25 | ssl_certificate_key $keyfile; 26 | 27 | # verify chain of trust of OCSP response using Root CA and Intermediate certs 28 | ssl_trusted_certificate /config/acme.sh/ca.cer; 29 | 30 | # Path of the file with Diffie-Hellman parameters for EDH ciphers. 31 | # TIP: Generate with: `openssl dhparam -out /config/nginx/dhparam4096.pem 4096` 32 | ssl_dhparam /config/nginx/dhparam4096.pem; 33 | 34 | # Specifies a time during which a client may reuse the session parameters. 35 | # Default is '5m'. 36 | ssl_session_timeout 1d; 37 | 38 | # Disable TLS session tickets (they are insecure). Default is 'on'. 39 | ssl_session_tickets off; 40 | 41 | # Session chache time 42 | ssl_session_cache shared:MozSSL:10m; # about 40000 sessions 43 | 44 | # OCSP stapling 45 | ssl_stapling on; 46 | ssl_stapling_verify on; 47 | 48 | 49 | -------------------------------------------------------------------------------- /root/etc/cont-init.d/40-crowdsec-config: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | # make sure all environment variables are lower case 4 | CROWDSEC_URL=$(echo "${CROWDSEC_URL,,}") 5 | CROWDSEC_API_TOKEN=$(echo "${CROWDSEC_API_TOKEN,,}") 6 | CROWDSEC_API_TOKEN_FILE=$(echo "${CROWDSEC_API_TOKEN_FILE,,}") 7 | 8 | # Importand lua scrip paths 9 | LUA_SCRIPT=/usr/local/lib/lua/crowdsec_nginx.lua 10 | LUA_SCRIPT_DISABLED=/usr/local/lib/lua/crowdsec_nginx.lua.disabled 11 | 12 | # If crowdsec url and configuration is not available skip config 13 | if [[ -z $CROWDSEC_URL ]]; then 14 | # Disable crowdsec Lua file 15 | [ -f $LUA_SCRIPT ] && mv $LUA_SCRIPT $LUA_SCRIPT_DISABLED 16 | exit 0 17 | elif [[ -z $CROWDSEC_API_TOKEN ]] && [[ -z $CROWDSEC_API_TOKEN_FILE ]]; then 18 | # Disable crowdsec Lua file 19 | [ -f $LUA_SCRIPT ] && mv $LUA_SCRIPT $LUA_SCRIPT_DISABLED 20 | exit 0 21 | fi 22 | 23 | # Enable crowdsec lua file if crowdsec should be used 24 | [ -f $LUA_SCRIPT_DISABLED ] && mv $LUA_SCRIPT_DISABLED $LUA_SCRIPT 25 | 26 | # Load Lua crowdsec file 27 | AUTOCONF_FILE=/config/nginx/conf.d/autoconf.conf 28 | echo "# Load required lua crowdsec module" >> $AUTOCONF_FILE 29 | echo "access_by_lua_file '/usr/local/lib/lua/crowdsec/access.lua';" >> $AUTOCONF_FILE 30 | echo "" >> $AUTOCONF_FILE 31 | 32 | # Configure Crowdsec api url 33 | CROWDSEC_URL=$(echo $CROWDSEC_URL | sed -e 's/\//\\\//g') 34 | sed -i "s/^API_URL=.*/API_URL=${CROWDSEC_URL}/" /usr/local/lib/lua/crowdsec/crowdsec.conf 35 | 36 | 37 | # Get apikey 38 | if [[ -n $CROWDSEC_API_TOKEN_FILE ]]; then 39 | if [ -s "$CROWDSEC_API_TOKEN_FILE" ]; then 40 | $CROWDSEC_API_TOKEN="$(head -n 1 $CROWDSEC_API_TOKEN_FILE)" 41 | else 42 | echo "ERROR: Crowdsec API Token file does not exist or is empty. Can not enable crowdsec." 43 | fi; 44 | fi 45 | 46 | # Check if api token is available 47 | if [[ -n $CROWDSEC_API_TOKEN ]]; then 48 | 49 | # Remove leading and ending (") from vartiable 50 | CROWDSEC_API_TOKEN=$(echo "${CROWDSEC_API_TOKEN//\"}") 51 | 52 | # Add new api key for lua bouncer 53 | sed -i "s/^API_KEY=.*/API_KEY=${CROWDSEC_API_TOKEN}/" /usr/local/lib/lua/crowdsec/crowdsec.conf 54 | fi -------------------------------------------------------------------------------- /root/default/nginx/conf.d/compression.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains compression settings for the global http block 3 | 4 | # It is available in all sites by default as this is already loaded in the http directive. 5 | # If you do not want this configuration to be active, we recommend to modify the http directive in your nginx.conf file. 6 | # A change on the http directive affects all pages so be carefull with this and test it on a seperate instance first. 7 | # To change this you will need to remove the line "include /config/nginx/conf.d/*.conf;" from nginx.conf. 8 | # Make sure to add all required files manually to the http directive before doing this or you break the functionality of all pages. 9 | # You can add the individual files by adding the following to your nginx.conf file. 10 | # Make sure to replace "filename.conf" with the realname and remove "#" at the beginning to make this active. 11 | 12 | # Examples to add only individual configs to the http directive 13 | # include /config/nginx/conf.d/filename.conf; 14 | 15 | 16 | # Enable gzipping of responses. 17 | gzip on; 18 | 19 | # gzip also proxied request 20 | gzip_proxied any; 21 | 22 | # request types to gzip 23 | gzip_types text/plain text/xml text/css application/x-javascript; 24 | 25 | # Set the Vary HTTP header as defined in the RFC 2616. Default is 'off'. 26 | gzip_vary on; 27 | 28 | # Disable gzip on e.g User-Auth headers 29 | gzip_disable “MSIE [1-6]\.(?!.*SV1)”; 30 | 31 | #Enable brotlin for faster compression and improved performance 32 | brotli on; 33 | 34 | # Sets the compression level for on the fly compression 35 | brotli_comp_level 6; 36 | 37 | # Enables on the fly compression for the following mime types 38 | brotli_types application/atom+xml application/javascript application/json application/rss+xml 39 | application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype 40 | application/x-font-ttf application/x-javascript application/xhtml+xml application/xml 41 | font/eot font/opentype font/otf font/truetype image/svg+xml image/vnd.microsoft.icon 42 | image/x-icon image/x-win-bitmap text/css text/javascript text/plain text/xml; 43 | 44 | # Check of the existence of pre compressed brotli files 45 | brotli_static on; -------------------------------------------------------------------------------- /root/default/fail2ban/filter.d/nginx-badbots.conf: -------------------------------------------------------------------------------- 1 | # Fail2Ban configuration file 2 | # 3 | # Regexp to catch known spambots and software alike. 4 | 5 | 6 | [Definition] 7 | 8 | badbotscustom = EmailCollector|WebEMailExtrac|TrackBack/1\.02|sogou music spider 9 | badbots = Atomic_Email_Hunter/4\.0|atSpider/1\.0|autoemailspider|bwh3_user_agent|China Local Browse 2\.6|ContactBot/0\.2|ContentSmartz|DataCha0s/2\.0|DBrowse 1\.4b|DBrowse 1\.4d|Demo Bot DOT 16b|Demo Bot Z 16b|DSurf15a 01|DSurf15a 71|DSurf15a 81|DSurf15a VA|EBrowse 1\.4b|Educate Search VxB|EmailSiphon|EmailSpider|EmailWolf 1\.00|ESurf15a 15|ExtractorPro|Franklin Locator 1\.8|FSurf15a 01|Full Web Bot 0416B|Full Web Bot 0516B|Full Web Bot 2816B|Guestbook Auto Submitter|Industry Program 1\.0\.x|ISC Systems iRc Search 2\.1|IUPUI Research Bot v 1\.9a|LARBIN-EXPERIMENTAL \(efp@gmx\.net\)|LetsCrawl\.com/1\.0 \+http\://letscrawl\.com/|Lincoln State Web Browser|LMQueueBot/0\.2|LWP\:\:Simple/5\.803|Mac Finder 1\.0\.xx|MFC Foundation Class Library 4\.0|Microsoft URL Control - 6\.00\.8xxx|Missauga Locate 1\.0\.0|Missigua Locator 1\.9|Missouri College Browse|Mizzu Labs 2\.2|Mo College 1\.9|MVAClient|Mozilla/2\.0 \(compatible; NEWT ActiveX; Win32\)|Mozilla/3\.0 \(compatible; Indy Library\)|Mozilla/3\.0 \(compatible; scan4mail \(advanced version\) http\://www\.peterspages\.net/?scan4mail\)|Mozilla/4\.0 \(compatible; Advanced Email Extractor v2\.xx\)|Mozilla/4\.0 \(compatible; Iplexx Spider/1\.0 http\://www\.iplexx\.at\)|Mozilla/4\.0 \(compatible; MSIE 5\.0; Windows NT; DigExt; DTS Agent|Mozilla/4\.0 efp@gmx\.net|Mozilla/5\.0 \(Version\: xxxx Type\:xx\)|NameOfAgent \(CMS Spider\)|NASA Search 1\.0|Nsauditor/1\.x|PBrowse 1\.4b|PEval 1\.4b|Poirot|Port Huron Labs|Production Bot 0116B|Production Bot 2016B|Production Bot DOT 3016B|Program Shareware 1\.0\.2|PSurf15a 11|PSurf15a 51|PSurf15a VA|psycheclone|RSurf15a 41|RSurf15a 51|RSurf15a 81|searchbot admin@google\.com|ShablastBot 1\.0|snap\.com beta crawler v0|Snapbot/1\.0|Snapbot/1\.0 \(Snap Shots, \+http\://www\.snap\.com\)|sogou develop spider|Sogou Orion spider/3\.0\(\+http\://www\.sogou\.com/docs/help/webmasters\.htm#07\)|sogou spider|Sogou web spider/3\.0\(\+http\://www\.sogou\.com/docs/help/webmasters\.htm#07\)|sohu agent|SSurf15a 11 |TSurf15a 11|Under the Rainbow 2\.2|User-Agent\: Mozilla/4\.0 \(compatible; MSIE 6\.0; Windows NT 5\.1\)|VadixBot|WebVulnCrawl\.unknown/1\.0 libwww-perl/5\.803|Wells Search II|WEP Search 00 10 | 11 | failregex = ^ -.*"(GET|POST|HEAD).*HTTP.*"(?:%(badbots)s|%(badbotscustom)s)"$ 12 | 13 | ignoreregex = 14 | 15 | # DEV Notes: 16 | # List of bad bots fetched from http://www.user-agents.org 17 | # Generated on Thu Nov 7 14:23:35 PST 2013 by files/gen_badbots. 18 | # 19 | # Author: Yaroslav Halchenko 20 | -------------------------------------------------------------------------------- /root/default/fail2ban/jail.local: -------------------------------------------------------------------------------- 1 | # fail2ban jail configuration 2 | # All changes on this file or on any other fail2ban files require a restart of the container 3 | 4 | ## 5 | # Global settings 6 | ## 7 | 8 | [DEFAULT] 9 | 10 | # Use "iptables-allports" to avoid any issues on special platforms. 11 | banaction = iptables-allports 12 | 13 | # "bantime" is the number of seconds that a host is banned. 14 | bantime = 600 15 | 16 | # A host is banned if it has generated "maxretry" during the last "findtime" (seconds). 17 | findtime = 600 18 | 19 | # "maxretry" is the number of failures before a host get banned. 20 | maxretry = 5 21 | 22 | # "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban 23 | # will not ban a host which matches an address in this list. Several addresses 24 | ignoreip = 127.0.0.1/8 ::1 25 | 26 | 27 | ## 28 | # Mail action and conenction settings 29 | # DO NOT MODIFY THIS MANUAL, THIS IS DONE AUTOMATICALLY 30 | ## 31 | 32 | #action = %(action_mw)s[from=, password=, destination=, sendername=, smtpserver=] 33 | #sender = root@localhost 34 | #destemail = root@localhost 35 | action_mw = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] 36 | %(mta)s-common[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"] 37 | 38 | ## 39 | # Mail action settings end, you can modify all settings below again 40 | ## 41 | 42 | 43 | ## 44 | # JAILS 45 | ## 46 | 47 | [ssh] 48 | enabled = false 49 | 50 | 51 | [nginx-http-auth] 52 | enabled = true 53 | filter = nginx-http-auth 54 | port = http,https 55 | logpath = %(nginx_error_log)s 56 | 57 | 58 | [nginx-badbots] 59 | enabled = true 60 | port = http,https 61 | filter = nginx-badbots 62 | logpath = %(nginx_access_log)s 63 | maxretry = 2 64 | 65 | 66 | [nginx-botsearch] 67 | enabled = true 68 | port = http,https 69 | filter = nginx-botsearch 70 | logpath = %(nginx_access_log)s 71 | 72 | 73 | [nginx-deny] 74 | enabled = true 75 | port = http,https 76 | filter = nginx-deny 77 | logpath = %(nginx_error_log)s 78 | 79 | 80 | [nginx-modsecurity] 81 | enabled = true 82 | port = http,https 83 | filter = nginx-modsecurity 84 | logpath = %(nginx_error_log)s 85 | 86 | 87 | [nginx-nohome] 88 | enabled = true 89 | port = http,https 90 | filter = nginx-nohome 91 | logpath = %(nginx_access_log)s 92 | maxretry = 2 93 | 94 | 95 | [nginx-noproxy] 96 | enabled = true 97 | port = http,https 98 | filter = nginx-noproxy 99 | logpath = %(nginx_access_log)s 100 | maxretry = 2 101 | 102 | 103 | [nginx-limit-req] 104 | enabled = true 105 | filter = nginx-limit-req 106 | logpath = %(nginx_error_log)s 107 | findtime = 600 108 | bantime = 7200 109 | maxretry = 10 -------------------------------------------------------------------------------- /root/default/nginx/conf.d/security-headers.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration contains security headers for all server blocks 3 | 4 | # it will be available in all sites if you include one of the following two examples inside your server block 5 | # The first example is the best as it loads all "*.conf" files from the directory 6 | # The second line only loads this specific config file, replace "filename.conf" with the filename of this file 7 | # Make sure to remove the leading "#" from the specific line 8 | 9 | # Examples for server block 10 | # include /config/nginx/sites-conf.d/*.conf; 11 | # include /config/nginx/sites-conf.d/filename.conf; 12 | 13 | 14 | # Helper to send Strict Transport Security header only on ssl connections 15 | map $frontend_use_ssl $strict_transport_security_value { 16 | off ''; 17 | on 'max-age=63072000; includeSubDomains; preload'; 18 | } 19 | 20 | # Don't tell nginx version to the clients. Default is 'on'. 21 | server_tokens off; 22 | 23 | # Dont tell server detals (nginx) to the clients 24 | more_clear_headers Server; 25 | 26 | # HSTS, to avoid man in the middle attacks and force a ssl conenction 27 | more_clear_headers "Strict-Transport-Security"; 28 | add_header Strict-Transport-Security $strict_transport_security_value always; 29 | 30 | # Add Permission policy to prevent malicious access to end user devices or location tracking 31 | add_header Permissions-Policy "accelerometer=(), ambient-light-sensor=(), autoplay=(), camera=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), usb=(), vr=()"; 32 | 33 | # Prevent search engin scrawling 34 | more_clear_headers "X-Robots-Tag"; 35 | add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"; 36 | 37 | # Prevent MIME type sniffing 38 | more_clear_headers "X-Content-Type-Options"; 39 | add_header X-Content-Type-Options "nosniff" always; 40 | 41 | # Prevent iframe integration on external pages 42 | more_clear_headers "X-Frame-Options"; 43 | add_header X-Frame-Options "SAMEORIGIN" always; 44 | 45 | # Block pages from loading when they detect reflected XSS attacks. 46 | more_clear_headers "X-XSS-Protection"; 47 | add_header X-XSS-Protection "1; mode=block"; 48 | 49 | # Set referrer-policy to none to avoid any url spoofing 50 | more_clear_headers "Referrer-Policy"; 51 | add_header Referrer-Policy 'no-referrer'; 52 | 53 | # Remove hidden headers from response 54 | more_clear_headers 'X-Hidden-*'; 55 | 56 | # Remove X-Powered-By header to remove details about running environmnets and theire versions from backend servers 57 | more_clear_headers X-Powered-By; 58 | 59 | # Remove X-Page-Speed header to remove details about running Page-Speed 60 | more_clear_headers X-Page-Speed; 61 | 62 | # Remove AspNet-Version header to remove details about running AspNet environment 63 | more_clear_headers X-AspNet-Version; 64 | 65 | # Remove AspNetMvc-Version header to remove details about running AspNet environment 66 | more_clear_headers X-AspNetMvc-Version; 67 | 68 | # Remove x-Varnish header to remove details about Varnish 69 | more_clear_headers X-Varnish; -------------------------------------------------------------------------------- /install/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Enable early exit in case of errors 4 | set -e 5 | set -o pipefail 6 | 7 | # Get available cpu cores to improve compile time 8 | if [[ $RUNS_ON_GITHUB = false ]]; then 9 | CPU_CORES=$(nproc --all) 10 | else 11 | CPU_CORES=1 12 | fi 13 | export CPU_CORES 14 | 15 | # Install build dependencies 16 | echo "**** install build packages ****" 17 | apk add --no-cache --virtual=build-dependencies \ 18 | autoconf \ 19 | automake \ 20 | byacc \ 21 | cargo \ 22 | flex \ 23 | g++ \ 24 | gcc \ 25 | gd-dev \ 26 | git \ 27 | libc-dev \ 28 | libffi-dev \ 29 | libtool \ 30 | libxslt-dev \ 31 | linux-headers \ 32 | make \ 33 | openssl-dev \ 34 | pcre-dev \ 35 | perl-dev \ 36 | tar \ 37 | wget \ 38 | yajl-dev \ 39 | zlib-dev 40 | 41 | 42 | # Install runtime packages 43 | echo "**** install runtime packages ****" 44 | apk add --no-cache --upgrade \ 45 | apache2-utils \ 46 | bash \ 47 | ca-certificates \ 48 | clamav \ 49 | clamav-libunrar \ 50 | coreutils \ 51 | curl-dev \ 52 | fail2ban \ 53 | geoip-dev \ 54 | gnupg \ 55 | inotify-tools \ 56 | libmaxminddb-dev \ 57 | libstdc++ \ 58 | libxml2-dev \ 59 | lmdb-dev \ 60 | memcached \ 61 | nano \ 62 | openssl \ 63 | tzdata \ 64 | shadow \ 65 | yajl 66 | 67 | 68 | # Install base image components 69 | ./tmp/secproxy-installer/install-base-image.sh 70 | 71 | 72 | # Install Lua 73 | ./tmp/secproxy-installer/install-lua.sh 74 | 75 | 76 | # Install Modsecurity 77 | ./tmp/secproxy-installer/install-modsecurity.sh 78 | 79 | 80 | # Install Nginx 81 | ./tmp/secproxy-installer/install-nginx.sh 82 | 83 | 84 | # Install crowdsec 85 | ./tmp/secproxy-installer/install-crowdsec.sh 86 | 87 | 88 | # Install ACME.sh client 89 | git clone https://github.com/acmesh-official/acme.sh.git /.acme.sh 90 | mkdir -p /default/www/acme_root/.well-known/acme-challenge 91 | chown -R root:secproxy /default/www/acme_root 92 | 93 | 94 | # Prepare fail2ban and move to default as template 95 | echo "Prepare fail2ban config" 96 | rm /etc/fail2ban/jail.d/alpine-ssh.conf 97 | mkdir -p /default/fail2ban 98 | mv /etc/fail2ban/action.d /default/fail2ban/ 99 | mv /etc/fail2ban/filter.d /default/fail2ban/ 100 | # Replace default iptable action to "DROP" instead of "REJECT ..." as this cause errors on old iptable versions 101 | sed -i 's/^blocktype = .*$/blocktype = DROP/g' /default/fail2ban/action.d/iptables-common.conf 102 | 103 | 104 | # Apply custom cron config 105 | echo "Import custom crontabs" 106 | mkdir -p /etc/crontabs 107 | crontab -u root /tmp/secproxy-installer/etc/crontabs/root 108 | 109 | 110 | # Cleanup before deploying 111 | echo "**** clean build files ****" 112 | apk del --purge \ 113 | build-dependencies 114 | rm -rf \ 115 | /root/.cache \ 116 | /root/.cargo \ 117 | /tmp/* \ 118 | /var/cache/apk/* 119 | for myfile in *.pyc *.pyo; do \ 120 | find /usr/lib/python3.* -iname "${myfile}" -exec rm -f '{}' + \ 121 | ; done -------------------------------------------------------------------------------- /root/etc/cont-init.d/30-fail2ban-config: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | # Create additional folders 4 | mkdir -p /config/logs/fail2ban 5 | mkdir -p /var/run/fail2ban 6 | 7 | # make sure all environment variables are lower case and properly formated 8 | FAIL2BAN_MAIL_REPORT=$(echo "${FAIL2BAN_MAIL_REPORT,,}") 9 | FAIL2BAN_MAIL_REPORT=$(echo "${FAIL2BAN_MAIL_REPORT}" | sed "s/\"//g") 10 | 11 | # Update default action filter files as they should not contain user specific configuration 12 | cp /default/fail2ban/action.d/*.conf /config/fail2ban/action.d/ 13 | cp /default/fail2ban/filter.d/*.conf /config/fail2ban/filter.d/ 14 | 15 | 16 | # Replace fail2ban config in /etc directory with user config 17 | if [[ -d /etc/fail2ban/filter.d ]]; then 18 | rm -rf /etc/fail2ban/filter.d 19 | fi 20 | if [[ -d /etc/fail2ban/action.d ]]; then 21 | rm -rf /etc/fail2ban/action.d 22 | fi 23 | cp -R /config/fail2ban/filter.d /etc/fail2ban/ 24 | cp -R /config/fail2ban/action.d /etc/fail2ban/ 25 | cp /config/fail2ban/fail2ban.local /etc/fail2ban/fail2ban.local 26 | cp /config/fail2ban/jail.local /etc/fail2ban/jail.local 27 | cp /config/fail2ban/paths-common.local /etc/fail2ban/paths-common.local 28 | 29 | # Generate fail2ban log files 30 | if [[ ! -f /config/logs/fail2ban/fail2ban.log ]]; then 31 | touch /config/logs/fail2ban/fail2ban.log 32 | fi 33 | 34 | # Configure mail settings if available 35 | if [[ ${FAIL2BAN_MAIL_REPORT} != "disabled" ]] && [[ -n $SMTP_SERVER ]] && [[ -n $SMTP_SENDER_MAIL ]] && [[ -n $SMTP_SENDER_NAME ]] && [[ -n $SMTP_RECEIVER ]]; then 36 | echo "**** Enabling fail2ban mail notifications ****" 37 | 38 | ORIGINAL_MAIL_ACTION="#action = %(action_mw)s\[from=, password=, destination=, sendername=, smtpserver=\]" 39 | MAIL_ACTION="action = %(action_mw)s\[from=${SMTP_SENDER_MAIL}, password=, destination=${SMTP_RECEIVER}, sendername=${SMTP_SENDER_NAME}, smtpserver=${SMTP_SERVER}\]" 40 | 41 | # Check if smtp was configured with password file 42 | if [[ -n $SMTP_PASSWORD_FILE ]]; then 43 | if [ -s "$SMTP_PASSWORD_FILE" ]; then 44 | SMTP_PASSWORD="$(head -n 1 $SMTP_PASSWORD_FILE)" 45 | else 46 | echo "ERROR: SMTP password file does not exist or is empty. Can not enable fail2ban mail notifications. Please check your smtp server variables" 47 | fi; 48 | fi 49 | 50 | # Check if smtp password is available 51 | if [[ -n $SMTP_PASSWORD ]]; then 52 | 53 | # Duplicate (%) as sendmail will interpret this as command if there is a single (%) 54 | SMTP_PASSWORD=$(sed -e "s/%/%%/g" <<< $SMTP_PASSWORD) 55 | 56 | # Add password to mail action 57 | MAIL_ACTION=$(sed -e "s//$SMTP_PASSWORD/g" <<< $MAIL_ACTION) 58 | 59 | # Write smtp config to config file 60 | sed -i "s/${ORIGINAL_MAIL_ACTION}/${MAIL_ACTION}/" /etc/fail2ban/jail.local 61 | sed -i "s/#sender = root@localhost/sender = ${SMTP_SENDER_MAIL}/" /etc/fail2ban/jail.local 62 | sed -i "s/#destemail = root@localhost/destemail = ${SMTP_RECEIVER}/" /etc/fail2ban/jail.local 63 | else 64 | echo "ERROR: SMTP Server details choosen but no password available. Can not enable fail2ban mail notifications. Please check your smtp server variables." 65 | fi 66 | fi 67 | 68 | # Clear SMTP_PASSWORD from variables 69 | SMTP_PASSWORD="" 70 | MAIL_ACTION="" -------------------------------------------------------------------------------- /root/default/nginx/conf.d/proxy.conf: -------------------------------------------------------------------------------- 1 | 2 | # This configuration includes some default headers which are usefull for all proxy servers 3 | 4 | # It is available in all sites by default as this is already loaded in the http directive. 5 | # If you do not want this configuration to be active, we recommend to modify the http directive in your nginx.conf file. 6 | # A change on the http directive affects all pages so be carefull with this and test it on a seperate instance first. 7 | # To change this you will need to remove the line "include /config/nginx/conf.d/*.conf;" from nginx.conf. 8 | # Make sure to add all required files manually to the http directive before doing this or you break the functionality of all pages. 9 | # You can add the individual files by adding the following to your nginx.conf file. 10 | # Make sure to replace "filename.conf" with the realname and remove "#" at the beginning to make this active. 11 | 12 | # Examples to add only individual configs to the http directive 13 | # include /config/nginx/conf.d/filename.conf; 14 | 15 | 16 | # Timeout if the real server is dead 17 | proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; 18 | 19 | # Proxy Connection Settings 20 | proxy_connect_timeout 240; 21 | 22 | # Enable proxy buffering 23 | proxy_buffering on; 24 | 25 | # Enables buffering of a client request body to proxy server 26 | proxy_request_buffering on; 27 | 28 | # Buffers used for reading a response from the proxied server 29 | proxy_buffers 32 64k; 30 | 31 | # Buffer size for reading a response from the proxied server 32 | proxy_buffer_size 64k; 33 | 34 | # Bucket size for hash tables used by the proxy_hide_header and proxy_set_header directives 35 | proxy_headers_hash_bucket_size 128; 36 | 37 | # Sets the maximum size of headers hash tables 38 | proxy_headers_hash_max_size 1024; 39 | 40 | # Sets the HTTP protocol version for proxying 41 | proxy_http_version 1.1; 42 | 43 | # Defines a timeout for reading a response from the proxied server 44 | proxy_read_timeout 240; 45 | 46 | # Sets the text that should be changed in the “Location” and “Refresh” header 47 | proxy_redirect http:// $scheme://; 48 | 49 | # Sets a timeout for transmitting a request to the proxied server 50 | proxy_send_timeout 240; 51 | 52 | # Proxy ssl name to forward, used for dedicated TLS sessions per host 53 | proxy_ssl_name $proxy_host; 54 | 55 | # Passing the server name through TLS Server Name Indication extension when establishing a connection with the proxied HTTPS server. 56 | proxy_ssl_server_name on; 57 | 58 | # Proxy Cache and Cookie Settings 59 | proxy_cache_bypass $cookie_session; 60 | 61 | # enable proxy cache for faster responses 62 | proxy_cache_path cache/ keys_zone=auth_cache:10m; 63 | 64 | #proxy_cookie_path / "/; Secure"; # enable at your own risk, may break certain apps 65 | proxy_no_cache $cookie_session; 66 | 67 | # Helper variable for proxying websockets. 68 | map $http_upgrade $connection_upgrade { 69 | default upgrade; 70 | '' close; 71 | } 72 | 73 | # Helper to check if SSL is used with current scheme and should be forwarded to the backend as well 74 | map $scheme $frontend_use_ssl { 75 | default off; 76 | 'https' on; 77 | 'wss' on; 78 | 'smtps' on; 79 | 'pops' on; 80 | } 81 | 82 | # Set useful and common proxy headers which help the application to identify the real client and protocol for this connection 83 | proxy_set_header Host $host; 84 | proxy_set_header Connection $connection_upgrade; 85 | proxy_set_header Early-Data $ssl_early_data; 86 | proxy_set_header Upgrade $http_upgrade; 87 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 88 | proxy_set_header X-Forwarded-Host $host; 89 | proxy_set_header X-Forwarded-Server $host; 90 | proxy_set_header X-Forwarded-Proto $scheme; 91 | proxy_set_header X-Forwarded-Ssl $frontend_use_ssl; 92 | proxy_set_header X-Real-IP $remote_addr; 93 | -------------------------------------------------------------------------------- /root/default/www/index.html: -------------------------------------------------------------------------------- 1 | 137 | 138 | 139 | Welcome to Secure-Proxy 140 | 141 | 142 | 143 | 144 |
145 |

Secure-Proxy

146 |

A secure proxy server for your applications

147 |

This image is designed for security!

148 |

It is used as reverse proxy and can handle static websites.

149 |
150 |

Integrated Lets Encrypt, Web Application Firewall, fail2ban, ClamAV and a lot more.

151 |

Protects your applications against Bots, Crawlers, Attackers and Spam waves.

152 |
153 | 154 | -------------------------------------------------------------------------------- /root/etc/scripts.d/scan-filesystem.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | # Required files and folders 4 | LOG_FILE=/config/logs/clamav/clamscan.log 5 | QUARANTINE_DIRECTORY=/var/clamav/quarantine 6 | 7 | # Make required folders if not already exist 8 | mkdir -p /config/logs/clamav 9 | mkdir -p $QUARANTINE_DIRECTORY 10 | 11 | echo "**** ClamAV scanning system for malicious files.... ****" | tee -a $LOG_FILE 12 | 13 | # make sure action variable is lower case and properly formated 14 | CLAMAV_SYSTEM_SCAN=$(echo "${CLAMAV_SYSTEM_SCAN,,}") 15 | CLAMAV_ACTION=$(echo "${CLAMAV_ACTION,,}") 16 | CLAMAV_SYSTEM_SCAN=$(echo "${CLAMAV_SYSTEM_SCAN}" | sed "s/\"//g") 17 | 18 | # Exist script if file system scan was disabled 19 | if [[ $CLAMAV_SYSTEM_SCAN = "disabled" ]]; then 20 | exit 0 21 | fi 22 | 23 | # Get folders to scan 24 | if [ -n "$CLAMAV_SCAN_DIR" ]; then 25 | SCAN_FOLDERS="" 26 | for i in $(echo $CLAMAV_SCAN_DIR | sed "s/ / /g") 27 | do 28 | # Test if directory exists 29 | if [ -d "$i" ]; then 30 | # Add directory to scan list 31 | SCAN_FOLDERS="${SCAN_FOLDERS} ${i}" 32 | fi 33 | done 34 | else 35 | SCAN_FOLDERS="/" 36 | fi 37 | 38 | # Get folders to ignore 39 | IGNORE_FOLDERS="\/config\/clamav\/definitions\/*|\/sys\/kernel\/*|\/sys\/devices\/*|\/sys\/module\/*|\/sys\/bus\/*|\/sys\/fs\/*|\/sys\/class\/*|\/sys\/power\/*" 40 | if [ -n "$CLAMAV_IGNORE_DIR" ]; then 41 | for i in $(echo $CLAMAV_IGNORE_DIR | sed "s/ / /g") 42 | do 43 | # Test if directory exists 44 | if [ -d "$i" ]; then 45 | # Remove last character '/' if exists 46 | if [[ "$i" == */ ]]; then 47 | i=${i%?} 48 | fi 49 | # Add expression for all files and subfolders 50 | i="${i}/*" 51 | # Make regex from directory path 52 | i=$(echo $i | sed -r 's/\//\\\//g') 53 | # Append regex to ignore list 54 | IGNORE_FOLDERS="${IGNORE_FOLDERS}|${i}" 55 | # Control will enter here if $DIRECTORY exists. 56 | fi 57 | done 58 | fi 59 | 60 | # Check which scan action should be used 61 | SCAN_ACTION="-r -i" 62 | if [[ "$CLAMAV_ACTION" == "delete" ]]; then 63 | SCAN_ACTION=" --remove" 64 | elif [[ "$CLAMAV_ACTION" == "move" ]]; then 65 | SCAN_ACTION=" --move=${$QUARANTINE_DIRECTORY}" 66 | MOVE_MAIL_MESSAGE="Moved files are located in the directory \"${QUARANTINE_DIRECTORY}\" for quarantine reasons. Please check and remove/restore the files as needed." 67 | fi 68 | 69 | # Scan file system for any malicious files 70 | SCAN_RESULT=$(clamscan $SCAN_ACTION --log=$LOG_FILE --exclude-dir="${IGNORE_FOLDERS}" $SCAN_FOLDERS) 71 | 72 | # Check if infections found 73 | INFECTED_FILES=$(echo $SCAN_RESULT | sed -e "s/.*Infected.files: //" | sed "s/\s.*$//") 74 | if [[ $INFECTED_FILES > 0 ]]; then 75 | RESULT_SUBJECT="Infected files found during file system scan!" 76 | else 77 | RESULT_SUBJECT="File system scan performed without any abnormalities." 78 | fi 79 | 80 | #Check if mail schould be send 81 | if [[ $CLAMAV_MAIL_REPORT == 2 ]] || [[ $CLAMAV_MAIL_REPORT == 1 && $INFECTED_FILES > 0 ]] || [[ -z "$CLAMAV_MAIL_REPORT" && $INFECTED_FILES > 0 ]]; then 82 | 83 | # Check if smtp was configured with password file 84 | if [[ -n $SMTP_PASSWORD_FILE ]]; then 85 | if [ -s "$SMTP_PASSWORD_FILE" ]; then 86 | $SMTP_PASSWORD="$(head -n 1 $SMTP_PASSWORD_FILE)" 87 | else 88 | echo "ERROR: SMTP password file does not exist or is empty. Can not enable fail2ban mail notifications. Please check your smtp server variables" 89 | fi; 90 | fi 91 | # Check if smtp password is available 92 | if [[ -n $SMTP_PASSWORD ]]; then 93 | 94 | # Send mail 95 | echo "Subject: [ClamAV]: ${RESULT_SUBJECT} 96 | Date: `LC_ALL=C date +"%a, %d %h %Y %T %z"` 97 | From: ${SMTP_SENDER_NAME} <${SMTP_SENDER_MAIL}> 98 | To: ${SMTP_RECEIVER} 99 | 100 | Hello, 101 | 102 | the automatic ClamAV file system scan found \"${INFECTED_FILES}\" infected files. For any infected file the action \"${CLAMAV_ACTION}\" was performed automatically as configured for this container. ${MOVE_MAIL_MESSAGE} 103 | 104 | ${SCAN_RESULT} 105 | 106 | Regards, 107 | Secure-Proxy" | /usr/sbin/sendmail -t -v -H 'exec openssl s_client -quiet -connect ${SMTP_SERVER} -starttls smtp' -au${SMTP_SENDER_MAIL} -ap${SMTP_PASSWORD} -f ${SMTP_SENDER_MAIL} ${SMTP_RECEIVER} 108 | 109 | fi 110 | fi 111 | -------------------------------------------------------------------------------- /install/install-nginx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Enable early exit in case of errors 4 | set -e 5 | set -o pipefail 6 | 7 | # Move to work dir 8 | cd /tmp 9 | 10 | 11 | # Clone nginx 12 | echo "Get nginx source files" 13 | wget --quiet https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz -O /tmp/nginx-$NGINX_VERSION.tar.gz 14 | tar -xzf nginx-$NGINX_VERSION.tar.gz 15 | 16 | 17 | # Clone nginx brotli module 18 | echo "Get nginx brotli module" 19 | git clone https://github.com/google/ngx_brotli /tmp/nginx_brotli_module 20 | cd /tmp/nginx_brotli_module 21 | git checkout $(git describe --tags `git rev-list --tags --max-count=1`) 22 | cd /tmp 23 | 24 | 25 | # Clone nginx http-headers-more module 26 | echo "Get nginx http-headers-more module" 27 | git clone https://github.com/openresty/headers-more-nginx-module /tmp/nginx_http_headers_more_module 28 | cd /tmp/nginx_http_headers_more_module 29 | git checkout $(git describe --tags `git rev-list --tags --max-count=1`) 30 | cd /tmp 31 | 32 | 33 | # Clone nginx dev tools 34 | echo "Get nginx dev tools" 35 | git clone https://github.com/vision5/ngx_devel_kit /tmp/nginx_devel_kit 36 | cd /tmp/nginx_devel_kit 37 | git checkout $(git describe --tags `git rev-list --tags --max-count=1`) 38 | cd /tmp 39 | 40 | 41 | # Clone nginx geoip module 42 | echo "Get nginx geoip module" 43 | git clone https://github.com/leev/ngx_http_geoip2_module /tmp/nginx_geoip2_module 44 | 45 | 46 | # Clone Lua nginx module 47 | echo "Get nginx lua module" 48 | git clone https://github.com/openresty/lua-nginx-module /tmp/nginx_lua_module 49 | cd /tmp/nginx_lua_module 50 | git checkout $(git describe --tags `git rev-list --tags --max-count=1`) 51 | cd /tmp 52 | 53 | 54 | # Clone Lua stream nginx module 55 | echo "Get nginx lua stream module" 56 | git clone https://github.com/openresty/stream-lua-nginx-module /tmp/nginx_lua_stream_module 57 | cd /tmp/nginx_lua_stream_module 58 | git checkout $(git describe --tags `git rev-list --tags --max-count=1`) 59 | cd /tmp 60 | 61 | 62 | # Clone ModSecurity nginx connector 63 | echo "Get ModSecurity nginx connector" 64 | git clone -b master --depth 1 --quiet https://github.com/SpiderLabs/ModSecurity-nginx.git /tmp/nginx_modsecurity_module 65 | 66 | 67 | # tell nginx's build system where to find LuaJIT: 68 | OPENRESTY_PATH=$(find /usr/local/include/ -maxdepth 1 -type d -name 'luajit-*' -print -quit) 69 | export LUAJIT_LIB=/usr/local/lib/ 70 | export LUAJIT_INC=${OPENRESTY_PATH}/ 71 | 72 | 73 | # Compiling nginx with modules 74 | echo 'Compile Nginx' 75 | cd "/tmp/nginx-$NGINX_VERSION" 76 | ./configure \ 77 | --user=secproxy \ 78 | --group=secproxy \ 79 | --prefix=/var/lib/nginx \ 80 | --sbin-path=/usr/sbin/nginx \ 81 | --modules-path=/usr/lib/nginx/modules \ 82 | --conf-path=/etc/nginx/nginx.conf \ 83 | --pid-path=/run/nginx.pid \ 84 | --lock-path=/run/nginx.lock \ 85 | --with-threads \ 86 | --with-file-aio \ 87 | --with-http_ssl_module \ 88 | --with-http_v2_module \ 89 | --with-http_realip_module \ 90 | --with-http_addition_module \ 91 | --with-http_geoip_module \ 92 | --with-http_sub_module \ 93 | --with-http_gunzip_module \ 94 | --with-http_gzip_static_module \ 95 | --with-http_auth_request_module \ 96 | --with-http_random_index_module \ 97 | --with-http_secure_link_module \ 98 | --with-http_slice_module \ 99 | --with-http_stub_status_module \ 100 | --with-ld-opt="-Wl,-rpath,${OPENRESTY_PATH}" \ 101 | --with-mail=dynamic \ 102 | --with-mail_ssl_module \ 103 | --with-stream \ 104 | --with-stream_ssl_module \ 105 | --with-stream_realip_module \ 106 | --with-stream_geoip_module \ 107 | --with-stream_ssl_preread_module \ 108 | --add-module=../nginx_brotli_module \ 109 | --add-module=../nginx_devel_kit \ 110 | --add-module=../nginx_geoip2_module \ 111 | --add-module=../nginx_http_headers_more_module \ 112 | --add-module=../nginx_lua_module \ 113 | --add-module=../nginx_lua_stream_module \ 114 | --add-module=../nginx_modsecurity_module 115 | make -j${CPU_CORES} 116 | make install 117 | 118 | # copy mime.types to default location 119 | mkdir -p /default/nginx 120 | cp /etc/nginx/mime.types /default/nginx/mime.types 121 | 122 | # Copy nginx config to use for config validation and make required folders 123 | cp /tmp/secproxy-installer/etc/nginx/nginx.conf /etc/nginx/nginx.conf 124 | mkdir -p /etc/nginx/conf.d 125 | mkdir -p /etc/nginx/sites-conf.d 126 | 127 | cd /tmp -------------------------------------------------------------------------------- /install/install-lua.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Enable early exit in case of errors 4 | set -e 5 | set -o pipefail 6 | 7 | # Move to work dir 8 | cd /tmp 9 | 10 | # Add lua-dev as build dependency (only for lua setup, not globally) 11 | apk add --no-cache --virtual=lua-build-dependencies lua-dev 12 | 13 | # Clone and install LuaJIT 14 | echo "Install LuaJIT package" 15 | git clone https://github.com/openresty/luajit2 /tmp/openresty_luait 16 | cd /tmp/openresty_luait 17 | git checkout $(git describe --tags `git rev-list --tags --max-count=1`) 18 | make -j${CPU_CORES} 19 | make install 20 | cd /tmp 21 | 22 | 23 | # Clone and install lua resty core module 24 | echo "Install install lua resty core module" 25 | git clone https://github.com/openresty/lua-resty-core /tmp/lua_resty_core 26 | cd /tmp/lua_resty_core 27 | git checkout $(git describe --tags `git rev-list --tags --max-count=1`) 28 | make -j${CPU_CORES} 29 | make install 30 | cd /tmp 31 | 32 | 33 | # Clone and install lua resty lrucache module 34 | echo "Install lua resty lrucache module" 35 | git clone https://github.com/openresty/lua-resty-lrucache /tmp/lua_resty_lrucache 36 | cd /tmp/lua_resty_lrucache 37 | git checkout $(git describe --tags `git rev-list --tags --max-count=1`) 38 | make -j${CPU_CORES} 39 | make install 40 | cd /tmp 41 | 42 | 43 | # Clone and install lua logging module 44 | echo "Install lua logging module" 45 | git clone https://github.com/lunarmodules/lualogging /tmp/lua_logging 46 | cp -r /tmp/lua_logging/src/* /usr/local/lib/lua 47 | 48 | 49 | # Clone and install lua socket module 50 | echo "Install lua socket module" 51 | git clone https://github.com/diegonehab/luasocket /tmp/lua_socket 52 | cd /tmp/lua_socket 53 | make -j${CPU_CORES} 54 | make CDIR_linux=lib/lua/5.1 LDIR_linux=lib/lua install 55 | cd /tmp 56 | 57 | 58 | # Clone and install lua sec module 59 | echo "Install lua sec module" 60 | git clone https://github.com/brunoos/luasec.git /tmp/lua_sec 61 | cd /tmp/lua_sec 62 | mkdir -p /usr/local/lib/lua/5.1 63 | make linux -j${CPU_CORES} 64 | make LUACPATH=/usr/local/lib/lua/5.1 LUAPATH=/usr/local/lib/lua install 65 | cd /tmp 66 | 67 | 68 | # Clone and Install lua cjson module 69 | git clone https://github.com/openresty/lua-cjson /tmp/lua_cjson 70 | cd /tmp/lua_cjson 71 | make -j${CPU_CORES} 72 | make install 73 | cd /tmp 74 | cp -r /tmp/lua_cjson/lua/* /usr/local/lib/lua/ 75 | 76 | 77 | # Clone and Install lua resty string module 78 | git clone https://github.com/openresty/lua-resty-string /tmp/lua_string 79 | cd /tmp/lua_string 80 | make -j${CPU_CORES} 81 | make install 82 | cd /tmp 83 | 84 | 85 | # Clone and Install lua resty openssl module 86 | git clone https://github.com/fffonion/lua-resty-openssl /tmp/lua_openssl 87 | cp -r /tmp/lua_openssl/lib/resty/* /usr/local/lib/lua/resty/ 88 | 89 | 90 | # Clone and Install lua resty jwt module 91 | git clone https://github.com/cdbattags/lua-resty-jwt /tmp/lua_jwt 92 | cp /tmp/lua_jwt/lib/resty/* /usr/local/lib/lua/resty/ 93 | 94 | 95 | # Clone and Install lua resty hmac module 96 | git clone https://github.com/jkeys089/lua-resty-hmac /tmp/lua_hmac 97 | cp /tmp/lua_hmac/lib/resty/* /usr/local/lib/lua/resty/ 98 | 99 | 100 | # Clone and Install lua resty openidc module 101 | git clone https://github.com/zmartzone/lua-resty-openidc /tmp/lua_openidc 102 | cp /tmp/lua_openidc/lib/resty/* /usr/local/lib/lua/resty/ 103 | 104 | 105 | # Clone and Install lua resty http module 106 | echo "Install lua http module" 107 | RSTRING_VERSION=$(curl https://api.github.com/repos/ledgetech/lua-resty-http/releases/latest -s | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' | sed 's/release-//') 108 | wget --quiet https://github.com/ledgetech/lua-resty-http/archive/refs/tags/${RSTRING_VERSION}.tar.gz 109 | tar -xzf ${RSTRING_VERSION}.tar.gz 110 | RSTRING_VERSION="lua-resty-http-${RSTRING_VERSION:1}" 111 | cp /tmp/${RSTRING_VERSION}/lib/resty/* /usr/local/lib/lua/resty/ 112 | 113 | 114 | # Clone and Install lua resty session module 115 | echo "Install lua httsession module" 116 | RSESSION_VERSION=$(curl https://api.github.com/repos/bungle/lua-resty-session/releases/latest -s | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' | sed 's/release-//') 117 | wget --quiet https://github.com/bungle/lua-resty-session/archive/refs/tags/${RSESSION_VERSION}.tar.gz 118 | tar -xzf ${RSESSION_VERSION}.tar.gz 119 | RSESSION_VERSION="lua-resty-session-${RSESSION_VERSION:1}" 120 | cp -r /tmp/${RSESSION_VERSION}/lib/resty/* /usr/local/lib/lua/resty/ 121 | 122 | 123 | # Remove lua without purging 124 | apk del lua-build-dependencies 125 | -------------------------------------------------------------------------------- /root/etc/scripts.d/refresh-agent.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | # Function to validate a updated config file before loading into production 4 | validate_config(){ 5 | 6 | # Copy configuration file to temporary nginx 7 | cp ${1} /etc/nginx/conf.d/ 8 | 9 | # test conf file against nginx config 10 | if /usr/sbin/nginx -c /etc/nginx/nginx.conf -t 2> $LOG_FILE; then 11 | rm /etc/nginx/conf.d/* 12 | return 0; 13 | else 14 | echo "$(date "+%F %T") Validation of configuration file ${1} failed, skipping this file for reload." | tee -a $LOG_FILE 15 | rm /etc/nginx/conf.d/* 16 | return 1; 17 | fi 18 | } 19 | 20 | 21 | # Make required folders if not already exist 22 | mkdir -p /config/logs/refresh-agent 23 | mkdir -p /tmp/refresh-agent 24 | 25 | LOG_FILE=/config/logs/refresh-agent/refresh-agent.log 26 | 27 | 28 | # Download current robots list to webroot as there is no compare needed 29 | FILE=/config/www/robots.txt 30 | if [[ ! -f "$FILE" || $(find "${FILE}" -mtime +1 -print) ]]; then 31 | echo "$(date "+%F %T") Updating ${FILE}." >> $LOG_FILE 32 | curl -sL https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/robots.txt/robots.txt \ 33 | -o $FILE 2>&1 | tee -a $LOG_FILE 34 | fi 35 | 36 | 37 | # download list of active tor adresses 38 | FILE=/config/nginx/conf.d/deny-tor-proxies.conf 39 | if [[ ! -f "$FILE" || $(find "${FILE}" -mtime +1 -print) ]]; then 40 | cp /default/nginx/conf.d/deny-tor-proxies.conf /tmp/refresh-agent/deny-tor-proxies.conf 41 | curl -sL https://check.torproject.org/exit-addresses | grep ExitAddress | cut -d ' ' -f 2 | sed "s/^/deny /g; s/$/;/g" >> /tmp/refresh-agent/deny-tor-proxies.conf 42 | if ! cmp /tmp/refresh-agent/deny-tor-proxies.conf $FILE >/dev/null 2>&1 43 | then 44 | if validate_config /tmp/refresh-agent/deny-tor-proxies.conf; then 45 | echo "$(date "+%F %T") Updating known tor proxy adresses to ${FILE} as this has changed." >> $LOG_FILE 46 | mv -f /tmp/refresh-agent/deny-tor-proxies.conf $FILE 47 | fi 48 | fi 49 | fi 50 | 51 | 52 | # download list of bad ip addresses 53 | FILE=/config/nginx/conf.d/deny-bad-ip-addresses.conf 54 | if [[ ! -f "$FILE" || $(find "${FILE}" -mtime +1 -print) ]]; then 55 | cp /default/nginx/conf.d/deny-bad-ip-addresses.conf /tmp/refresh-agent/deny-bad-ip-addresses.conf 56 | curl -sL https://iplists.firehol.org/files/firehol_abusers_30d.netset | grep "^[^#]" | sed "s/^/deny /g; s/$/;/g" >> /tmp/refresh-agent/deny-bad-ip-addresses.conf 57 | if ! cmp /tmp/refresh-agent/deny-bad-ip-addresses.conf $FILE >/dev/null 2>&1 58 | then 59 | if validate_config /tmp/refresh-agent/deny-bad-ip-addresses.conf; then 60 | echo "$(date "+%F %T") Updating known bad ip address list from firehol to ${FILE} as this has changed." >> $LOG_FILE 61 | mv -f /tmp/refresh-agent/deny-bad-ip-addresses.conf $FILE 62 | fi 63 | fi 64 | fi 65 | 66 | 67 | # download list of bad ip addresses 68 | FILE=/config/nginx/conf.d/deny-bad-ip-addresses.conf 69 | if [[ ! -f "$FILE" || $(find "${FILE}" -mtime +1 -print) ]]; then 70 | cp /default/nginx/conf.d/deny-bad-ip-addresses.conf /tmp/refresh-agent/deny-bad-ip-addresses.conf 71 | curl -sL https://iplists.firehol.org/files/firehol_abusers_30d.netset | grep "^[^#]" | sed "s/^/deny /g; s/$/;/g" >> /tmp/refresh-agent/deny-bad-ip-addresses.conf 72 | if ! cmp /tmp/refresh-agent/deny-bad-ip-addresses.conf $FILE >/dev/null 2>&1 73 | then 74 | if validate_config /tmp/refresh-agent/deny-bad-ip-addresses.conf; then 75 | echo "$(date "+%F %T") Updating known bad ip address list from firehol to ${FILE} as this has changed." >> $LOG_FILE 76 | mv -f /tmp/refresh-agent/deny-bad-ip-addresses.conf $FILE 77 | fi 78 | fi 79 | fi 80 | 81 | 82 | # download lists of bad user agents from ultimate bad bot blocker 83 | FILE=/config/nginx/conf.d/deny-bad-user-agents.conf 84 | if [[ ! -f "$FILE" || $(find "${FILE}" -mtime +1 -print) ]]; then 85 | cp /default/nginx/conf.d/deny-bad-user-agents.conf /tmp/refresh-agent/deny-bad-user-agents.conf 86 | sed -i '$ d' /tmp/refresh-agent/deny-bad-user-agents.conf 87 | curl -sL https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-user-agents.list | sed 's/^/\t"~*(?:\\b)/g; s/$/(?:\\b)"\t1;/g' >> /tmp/refresh-agent/deny-bad-user-agents.conf 88 | echo "}" >> /tmp/refresh-agent/deny-bad-user-agents.conf 89 | if ! cmp /tmp/refresh-agent/deny-bad-user-agents.conf $FILE >/dev/null 2>&1 90 | then 91 | if validate_config /tmp/refresh-agent/deny-bad-user-agents.conf; then 92 | echo "$(date "+%F %T") Updating known bad user agent list to ${FILE} as this has changed." >> $LOG_FILE 93 | mv -f /tmp/refresh-agent/deny-bad-user-agents.conf $FILE 94 | fi 95 | fi 96 | fi 97 | 98 | 99 | # download lists of bad referers from ultimate bad bot blocker 100 | FILE=/config/nginx/conf.d/deny-bad-referers.conf 101 | if [[ ! -f "$FILE" || $(find "${FILE}" -mtime +1 -print) ]]; then 102 | cp /default/nginx/conf.d/deny-bad-referers.conf /tmp/refresh-agent/deny-bad-referers.conf 103 | sed -i '$ d' /tmp/refresh-agent/deny-bad-referers.conf 104 | curl -sL https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-referrers.list | sed 's/\-/\\-/g; s/\./\\./g; s/^/\t"~*(?:\\b)/g; s/$/(?:\\b)"\t1;/g' >> /tmp/refresh-agent/deny-bad-referers.conf 105 | echo "}" >> /tmp/refresh-agent/deny-bad-referers.conf 106 | if ! cmp /tmp/refresh-agent/deny-bad-referers.conf $FILE >/dev/null 2>&1 107 | then 108 | if validate_config /tmp/refresh-agent/deny-bad-referers.conf; then 109 | echo "$(date "+%F %T") Updating known bad user agent list to ${FILE} as this has changed." >> $LOG_FILE 110 | mv -f /tmp/refresh-agent/deny-bad-referers.conf $FILE 111 | fi 112 | fi 113 | fi -------------------------------------------------------------------------------- /root/default/www/error.html: -------------------------------------------------------------------------------- 1 | 169 | 170 | 171 | <!--# echo var="status" default="Error" --> 172 | 173 | 174 | 175 | 176 | 177 | 178 |
179 | 180 |

We are updating our website

181 |

This is only for a few seconds, you will be redirected.

182 | 183 |

184 |

185 |

186 |

We are sorry for the inconvenience.

187 | Go back 188 | 189 |
190 | 191 | 192 | -------------------------------------------------------------------------------- /root/default/nginx/conf.d/error-pages.conf: -------------------------------------------------------------------------------- 1 | 2 | # This config is required for the custom error pages, it maps a error code to the related message and description. 3 | #When you disable this make sure to also disable "/config/nginx/sites-conf.d/error-pages.conf" 4 | 5 | # It is available in all sites by default as this is already loaded in the http directive. 6 | # If you do not want this configuration to be active, we recommend to modify the http directive in your nginx.conf file. 7 | # A change on the http directive affects all pages so be carefull with this and test it on a seperate instance first. 8 | # To change this you will need to remove the line "include /config/nginx/conf.d/*.conf;" from nginx.conf. 9 | # Make sure to add all required files manually to the http directive before doing this or you break the functionality of all pages. 10 | # You can add the individual files by adding the following to your nginx.conf file. 11 | # Make sure to replace "filename.conf" with the realname and remove "#" at the beginning to make this active. 12 | 13 | # Examples to add only individual configs to the http directive 14 | # include /config/nginx/conf.d/filename.conf; 15 | 16 | # Providing a error text to the html error code 17 | map $status $status_text { 18 | 400 'Bad Request'; 19 | 401 'Unauthorized'; 20 | 402 'Payment Required'; 21 | 403 'Forbidden'; 22 | 404 'Not Found'; 23 | 405 'Method Not Allowed'; 24 | 406 'Not Acceptable'; 25 | 407 'Proxy Authentication Required'; 26 | 408 'Request Timeout'; 27 | 409 'Conflict'; 28 | 410 'Gone'; 29 | 411 'Length Required'; 30 | 412 'Precondition Failed'; 31 | 413 'Payload Too Large'; 32 | 414 'URI Too Long'; 33 | 415 'Unsupported Media Type'; 34 | 416 'Range Not Satisfiable'; 35 | 417 'Expectation Failed'; 36 | 418 'I\'m a teapot'; 37 | 421 'Misdirected Request'; 38 | 422 'Unprocessable Entity'; 39 | 423 'Locked'; 40 | 424 'Failed Dependency'; 41 | 425 'Too Early'; 42 | 426 'Upgrade Required'; 43 | 428 'Precondition Required'; 44 | 429 'Too Many Requests'; 45 | 431 'Request Header Fields Too Large'; 46 | 451 'Unavailable For Legal Reasons'; 47 | 500 'Internal Server Error'; 48 | 501 'Not Implemented'; 49 | 502 'Bad Gateway'; 50 | 503 'Service Unavailable'; 51 | 504 'Gateway Timeout'; 52 | 505 'HTTP Version Not Supported'; 53 | 506 'Variant Also Negotiates'; 54 | 507 'Insufficient Storage'; 55 | 508 'Loop Detected'; 56 | 510 'Not Extended'; 57 | 511 'Network Authentication Required'; 58 | default 'Something is wrong'; 59 | } 60 | 61 | # Mapping html error code to a readable description 62 | map $status $status_description { 63 | 400 'The server cannot process the request due to a client error.'; 64 | 401 'The request has not been applied because it lacks valid authentication credentials for the target resource.'; 65 | 402 'The server requests a payment, this is quite unusal. Please get in contact with the system administrator of this server.'; 66 | 403 'The server refused to authorize the request.'; 67 | 404 'The requested file or page cannot be found at this address.'; 68 | 405 'The method received in the request-line is not supported by the target resource.'; 69 | 406 'The target resource does not have a current representation that would be acceptable to the user agent.'; 70 | 407 'The client needs to authenticate itself in order to use a proxy.'; 71 | 408 'The server did not receive a complete request message within the time that it was prepared to wait.'; 72 | 409 'The request could not be completed due to a conflict with the current state of the target resource.'; 73 | 410 'The access to the target resource is no longer available at the origin server.'; 74 | 411 'The server refuses to accept the request without a defined Content-Length.'; 75 | 412 'One or more conditions given in the request header fields evaluated to false when tested on the server.'; 76 | 413 'The server is refusing to process a request because the request payload is larger than the server is willing to process.'; 77 | 414 'The server is refusing to service the request because the request-target is longer than the server is willing to interpret.'; 78 | 415 'The origin server is refusing to service the request because the payload is in a format not supported by this method on the target resource.'; 79 | 416 'The set of ranges requested has been rejected due to invalid ranges.'; 80 | 417 'The expectation given in the request\'s expect header field could not be met by the inbound server.'; 81 | 418 'I cannot brew your coffee because I\'m a teapot.'; 82 | 421 'The server is not configured to produce responses for the combination of scheme and authority that are included in the request URI.'; 83 | 422 'The server was unable to process the contained instructions.'; 84 | 423 'The source or destination resource of a method is locked.'; 85 | 424 'The method could not be performed on the resource because the requested action depended on another action failed.'; 86 | 425 'The server is unwilling to risk processing a request that might be replayed.'; 87 | 426 'The server refuses to perform the request using the current protocol.'; 88 | 428 'The origin server requires the request to be conditional.'; 89 | 429 'The user has sent too many requests in a given amount of time.'; 90 | 431 'The server is unwilling to process the request because its header fields are too large.'; 91 | 451 'The server is denying access to the resource as a consequence of a legal demand.'; 92 | 500 'The server encountered an unexpected condition that prevented it from fulfilling the request.'; 93 | 501 'The server does not support the functionality required to fulfill the request.'; 94 | 502 'The server, while acting as a gateway or proxy, received an invalid response from an inbound server.'; 95 | 503 'The server is currently down for maintenance.'; 96 | 504 'The server, while acting as a gateway or proxy, did not receive a timely response from an upstream server.'; 97 | 505 'The server does not support the major version of HTTP that was used in the request message.'; 98 | 506 'The server has an internal configuration error.'; 99 | 507 'The server is unable to store the representation needed to successfully complete the request.'; 100 | 508 'The server terminated an operation because it encountered an infinite loop while processing a request.'; 101 | 510 'The policy for accessing the resource has not been met in the request.'; 102 | 511 'The client needs to authenticate to gain network access.'; 103 | default 'We couldn\'t identify the reason for this problem, please refresh the page or try it later again.'; 104 | } -------------------------------------------------------------------------------- /root/default/clamav/freshclam.conf: -------------------------------------------------------------------------------- 1 | ## 2 | ## Example config file for freshclam 3 | ## Please read the freshclam.conf(5) manual before editing this file. 4 | ## 5 | 6 | # Path to the database directory. 7 | # WARNING: It must match clamd.conf's directive! 8 | # Default: hardcoded (depends on installation options) 9 | #DatabaseDirectory /var/lib/clamav 10 | 11 | # Path to the log file (make sure it has proper permissions) 12 | # Default: disabled 13 | UpdateLogFile /var/log/clamav/freshclam.log 14 | 15 | # Maximum size of the log file. 16 | # Value of 0 disables the limit. 17 | # You may use 'M' or 'm' for megabytes (1M = 1m = 1048576 bytes) 18 | # and 'K' or 'k' for kilobytes (1K = 1k = 1024 bytes). 19 | # in bytes just don't use modifiers. If LogFileMaxSize is enabled, 20 | # log rotation (the LogRotate option) will always be enabled. 21 | # Default: 1M 22 | #LogFileMaxSize 2M 23 | 24 | # Log time with each message. 25 | # Default: no 26 | #LogTime yes 27 | 28 | # Enable verbose logging. 29 | # Default: no 30 | #LogVerbose yes 31 | 32 | # Use system logger (can work together with UpdateLogFile). 33 | # Default: no 34 | #LogSyslog yes 35 | 36 | # Specify the type of syslog messages - please refer to 'man syslog' 37 | # for facility names. 38 | # Default: LOG_LOCAL6 39 | #LogFacility LOG_MAIL 40 | 41 | # Enable log rotation. Always enabled when LogFileMaxSize is enabled. 42 | # Default: no 43 | #LogRotate yes 44 | 45 | # This option allows you to save the process identifier of the daemon 46 | # This file will be owned by root, as long as freshclam was started by root. 47 | # It is recommended that the directory where this file is stored is 48 | # also owned by root to keep other users from tampering with it. 49 | # Default: disabled 50 | PidFile /run/clamav/freshclam.pid 51 | 52 | # By default when started freshclam drops privileges and switches to the 53 | # "clamav" user. This directive allows you to change the database owner. 54 | # Default: clamav (may depend on installation options) 55 | DatabaseOwner clamav 56 | 57 | # Use DNS to verify virus database version. FreshClam uses DNS TXT records 58 | # to verify database and software versions. With this directive you can change 59 | # the database verification domain. 60 | # WARNING: Do not touch it unless you're configuring freshclam to use your 61 | # own database verification domain. 62 | # Default: current.cvd.clamav.net 63 | #DNSDatabaseInfo current.cvd.clamav.net 64 | 65 | # database.clamav.net is now the primary domain name to be used world-wide. 66 | # Now that CloudFlare is being used as our Content Delivery Network (CDN), 67 | # this one domain name works world-wide to direct freshclam to the closest 68 | # geographic endpoint. 69 | # If the old db.XY.clamav.net domains are set, freshclam will automatically 70 | # use database.clamav.net instead. 71 | DatabaseMirror database.clamav.net 72 | 73 | # How many attempts to make before giving up. 74 | # Default: 3 (per mirror) 75 | #MaxAttempts 5 76 | 77 | # With this option you can control scripted updates. It's highly recommended 78 | # to keep it enabled. 79 | # Default: yes 80 | ScriptedUpdates yes 81 | 82 | # By default freshclam will keep the local databases (.cld) uncompressed to 83 | # make their handling faster. With this option you can enable the compression; 84 | # the change will take effect with the next database update. 85 | # Default: no 86 | #CompressLocalDatabase no 87 | 88 | # With this option you can provide custom sources for database files. 89 | # This option can be used multiple times. Support for: 90 | # http(s)://, ftp(s)://, or file:// 91 | # Default: no custom URLs 92 | DatabaseCustomURL http://www.rfxn.com/downloads/rfxn.ndb 93 | DatabaseCustomURL http://www.rfxn.com/downloads/rfxn.hdb 94 | DatabaseCustomURL http://www.rfxn.com/downloads/rfxn.yara 95 | 96 | # This option allows you to easily point freshclam to private mirrors. 97 | # If PrivateMirror is set, freshclam does not attempt to use DNS 98 | # to determine whether its databases are out-of-date, instead it will 99 | # use the If-Modified-Since request or directly check the headers of the 100 | # remote database files. For each database, freshclam first attempts 101 | # to download the CLD file. If that fails, it tries to download the 102 | # CVD file. This option overrides DatabaseMirror, DNSDatabaseInfo 103 | # and ScriptedUpdates. It can be used multiple times to provide 104 | # fall-back mirrors. 105 | # Default: disabled 106 | #PrivateMirror mirror1.example.com 107 | #PrivateMirror mirror2.example.com 108 | 109 | # Number of database checks per day. 110 | # Default: 12 (every two hours) 111 | #Checks 24 112 | 113 | # Proxy settings 114 | # The HTTPProxyServer may be prefixed with [scheme]:// to specify which kind 115 | # of proxy is used. 116 | # http:// HTTP Proxy. Default when no scheme or proxy type is specified. 117 | # https:// HTTPS Proxy. (Added in 7.52.0 for OpenSSL, GnuTLS and NSS) 118 | # socks4:// SOCKS4 Proxy. 119 | # socks4a:// SOCKS4a Proxy. Proxy resolves URL hostname. 120 | # socks5:// SOCKS5 Proxy. 121 | # socks5h:// SOCKS5 Proxy. Proxy resolves URL hostname. 122 | # Default: disabled 123 | #HTTPProxyServer https://proxy.example.com 124 | #HTTPProxyPort 1234 125 | #HTTPProxyUsername myusername 126 | #HTTPProxyPassword mypass 127 | 128 | # If your servers are behind a firewall/proxy which applies User-Agent 129 | # filtering you can use this option to force the use of a different 130 | # User-Agent header. 131 | # Default: clamav/version_number 132 | #HTTPUserAgent SomeUserAgentIdString 133 | 134 | # Use aaa.bbb.ccc.ddd as client address for downloading databases. Useful for 135 | # multi-homed systems. 136 | # Default: Use OS'es default outgoing IP address. 137 | #LocalIPAddress aaa.bbb.ccc.ddd 138 | 139 | # Send the RELOAD command to clamd. 140 | # Default: no 141 | #NotifyClamd /etc/clamav/clamd.conf 142 | 143 | # Run command after successful database update. 144 | # Use EXIT_1 to return 1 after successful database update. 145 | # Default: disabled 146 | #OnUpdateExecute command 147 | 148 | # Run command when database update process fails. 149 | # Default: disabled 150 | #OnErrorExecute command 151 | 152 | # Run command when freshclam reports outdated version. 153 | # In the command string %v will be replaced by the new version number. 154 | # Default: disabled 155 | #OnOutdatedExecute command 156 | 157 | # Don't fork into background. 158 | # Default: no 159 | Foreground yes 160 | 161 | # Enable debug messages in libclamav. 162 | # Default: no 163 | #Debug yes 164 | 165 | # Timeout in seconds when connecting to database server. 166 | # Default: 30 167 | #ConnectTimeout 60 168 | 169 | # Maximum time in seconds for each download operation. 0 means no timeout. 170 | # Default: 0 171 | #ReceiveTimeout 1800 172 | 173 | # With this option enabled, freshclam will attempt to load new databases into 174 | # memory to make sure they are properly handled by libclamav before replacing 175 | # the old ones. 176 | # Tip: This feature uses a lot of RAM. If your system has limited RAM and you 177 | # are actively running ClamD or ClamScan during the update, then you may need 178 | # to set `TestDatabases no`. 179 | # Default: yes 180 | #TestDatabases no 181 | 182 | # This option enables downloading of bytecode.cvd, which includes additional 183 | # detection mechanisms and improvements to the ClamAV engine. 184 | # Default: yes 185 | #Bytecode no 186 | 187 | # Include an optional signature databases (opt-in). 188 | # This option can be used multiple times. 189 | #ExtraDatabase dbname1 190 | #ExtraDatabase dbname2 191 | 192 | # Exclude a standard signature database (opt-out). 193 | # This option can be used multiple times. 194 | #ExcludeDatabase dbname1 195 | #ExcludeDatabase dbname2 196 | -------------------------------------------------------------------------------- /root/etc/cont-init.d/60-acme-config: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bash 2 | 3 | # Create folders 4 | mkdir -p /config/acme.sh 5 | mkdir -p /config/acme.sh/default 6 | mkdir -p /config/acme.sh/backup 7 | mkdir -p /config/logs/acme.sh 8 | mkdir -p /config/www/acme_root 9 | sleep 1 10 | 11 | 12 | # Fix permissions for acme root folder 13 | chown -R root:secproxy /config/www/acme_root 14 | chmod +x /etc/scripts.d/renew-acme.sh 15 | 16 | # Get all existing certificates 17 | CERTIFICATES_TO_DELETE=$(ls -d /config/acme.sh/*_ecc | sed "s/\/config\/acme.sh\///g") 18 | 19 | # Check if script was triggered by startup or renew cron job 20 | if [[ -f /tmp/acme-renew.lock ]]; then 21 | CRON_JOB="true" 22 | rm -f /tmp/acme-renew.lock 23 | else 24 | CRON_JOB="false" 25 | 26 | # Backup autoconf as we need to replace during certificate issuing 27 | AUTOCONF_FILE=/config/nginx/conf.d/autoconf.conf 28 | mv $AUTOCONF_FILE $AUTOCONF_FILE.bak 29 | echo "map \$host \$certfile { 30 | default /config/acme.sh/defaut/fullchain.cer; 31 | } 32 | map \$host \$keyfile { 33 | default /config/acme.sh/defaut/privkey.cer; 34 | }" > $AUTOCONF_FILE 35 | sleep 1 36 | fi 37 | 38 | 39 | # Check if default self signed certificate exists 40 | if [[ -f "/config/acme.sh/default/privkey.key" ]]; then 41 | # Check if default certificate needs to be renewed 42 | if openssl x509 -in /config/acme.sh/default/fullchain.cer -noout -checkend 86400 >/dev/null; then 43 | echo "" > /dev/null 44 | else 45 | echo "Generating new default certificate file" 46 | openssl req -new -x509 -days 365 -nodes \ 47 | -out /config/acme.sh/default/fullchain.cer \ 48 | -keyout /config/acme.sh/default/privkey.key \ 49 | -subj "/C=DE/CN=flo-mic-secure-proxy.com" > /dev/null 2>&1 50 | sleep 1 51 | fi 52 | else 53 | echo "Generating default certificate file" 54 | openssl req -new -x509 -days 365 -nodes \ 55 | -out /config/acme.sh/default/fullchain.cer \ 56 | -keyout /config/acme.sh/default/privkey.key \ 57 | -subj "/C=DE/CN=flo-mic-secure-proxy.com" > /dev/null 2>&1 58 | sleep 1 59 | fi 60 | 61 | 62 | # Check if CA trusted key is available 63 | if [[ ! -f "/config/acme.sh/ca.cer" ]]; then 64 | cp /config/acme.sh/default/fullchain.cer /config/acme.sh/ca.cer 65 | sleep 1 66 | fi 67 | 68 | 69 | # Check if variable "Staging" changed 70 | STAGING_CHANGED="false" 71 | if [[ -f /config/acme.sh/acme_staging.conf ]]; then 72 | source /config/acme.sh/acme_staging.conf 73 | 74 | # Check if state of staging environment variable changed 75 | if [[ ! $ACME_STAGING == $CERT_STAGING ]]; then 76 | echo "**** ACME config has changed, prepare new certificate ****" 77 | STAGING_CHANGED="true" 78 | fi 79 | else 80 | STAGING_CHANGED="true" 81 | echo "**** ACME config has changed, prepare new certificate ****" 82 | fi 83 | echo "ACME_STAGING=${CERT_STAGING}" > /config/acme.sh/acme_staging.conf 84 | 85 | # Get all domains for certificate issuing 86 | CERT_FILES=$(env | grep "CERT_FILE_.*" | tr " " "," | tr ",," "," | cut -d'=' -f2) 87 | 88 | # check if CERT_DOMAIN was provided and format all parameters 89 | if [[ $(echo $CERT_FILES | wc -w) == 0 ]]; then 90 | echo "ERROR: No domain provided, can not continue with acme challenge! Make sure the \"CERT_FILE_XXX\" parameter is provided." 91 | sleep infinity 92 | fi 93 | 94 | 95 | # Generate arrays to store the path mapping tables 96 | CERT_PATHS=() 97 | KEY_PATHS=() 98 | 99 | # Validate or generate certificates for all listed domains 100 | for CERT_FILE in $CERT_FILES; do 101 | 102 | # make sure all environment variables are lower case 103 | CERT_FILE=$(echo "${CERT_FILE,,}") 104 | CERT_STAGING=$(echo "${CERT_STAGING,,}") 105 | 106 | # Format and sort Domains 107 | FORCE_RENEWAL=$STAGING_CHANGED 108 | DOMAINS=$(echo $CERT_FILE | tr "," " " ) 109 | DOMAINS=($DOMAINS) 110 | DOMAINS=($(printf '%s\n' "${DOMAINS[@]}"|sort)) 111 | 112 | # Generating mapping table for cert 113 | for DOMAIN in ${DOMAINS[@]} 114 | do 115 | # Remove cert from removal list as it is still needed 116 | CERTIFICATES_TO_DELETE=$(echo $CERTIFICATES_TO_DELETE | sed "s/^${DOMAIN}_ecc//g" | sed "s/ ${DOMAIN}_ecc//g" | sed "s/ / /g") 117 | 118 | # Fill arrays for certificate mapping tables 119 | CERT_PATHS+=(${DOMAIN}=/config/acme.sh/${DOMAINS[0]}_ecc/${DOMAINS[0]}_chain.cer) 120 | KEY_PATHS+=(${DOMAIN}=/config/acme.sh/${DOMAINS[0]}_ecc/${DOMAINS[0]}.key) 121 | done 122 | 123 | # Check if certificate exists 124 | if [[ ! -f /config/acme.sh/${DOMAINS[0]}_ecc/fullchain.cer ]]; then 125 | FORCE_RENEWAL="true" 126 | fi 127 | 128 | # Check currend DNS names of certificate 129 | CURRENT_DNS_NAMES=$(openssl x509 -noout -text -in /config/acme.sh/${DOMAINS[0]}_ecc/${DOMAINS[0]}.cer | grep DNS: | sed 's/DNS://g' | sed 's/ //g') 130 | if [[ ! $CURRENT_DNS_NAMES == $CERT_FILE ]]; then 131 | FORCE_RENEWAL="true" 132 | fi 133 | 134 | # build domain string 135 | REQUEST_DOMAINS="" 136 | for DOMAIN in ${DOMAINS[@]} 137 | do 138 | REQUEST_DOMAINS="${REQUEST_DOMAINS} -d ${DOMAIN}" 139 | done 140 | 141 | # If new certificate needs to be generated, generate it 142 | if [[ $FORCE_RENEWAL == "true" ]]; then 143 | 144 | echo "**** Requesting new certificate from LetsEncrypt ****" 145 | 146 | # Start nginx for acme if no nginx instance is running 147 | if ! pgrep -f "[n]ginx:" > /dev/null; then 148 | echo "**** Starting temporary nginx instance for acme challenge ****" 149 | nohup ./etc/services.d/nginx/run & 150 | sleep 1 151 | nginx -c /config/nginx/nginx.conf -s reload 152 | fi 153 | 154 | # Move existing certificate in backup folder if a public key is present 155 | if [[ -f /config/acme.sh/${DOMAINS[0]}/${DOMAINS[0]}.cer ]]; then 156 | mv /config/acme.sh/${DOMAINS[0]}_ecc /config/acme.sh/backup/${DOMAINS[0]}_ecc 157 | sleep 1 158 | 159 | fi 160 | rm -rf /config/acme.sh/backup/${DOMAINS[0]}_ecc 161 | sleep 1 162 | 163 | # Echo affected domains 164 | for DOMAIN in ${DOMAINS[@]} 165 | do 166 | echo "Domain: $DOMAIN" 167 | done 168 | 169 | # Check if staging environment should be used 170 | shopt -s nocasematch 171 | if [[ "${CERT_STAGING}" == "true" ]]; then 172 | echo "Staging: true" 173 | STAGING="--staging --server letsencrypt_test" 174 | else 175 | echo "Staging: false" 176 | STAGING="--server letsencrypt" 177 | fi 178 | 179 | # Request certificate 180 | ./.acme.sh/acme.sh $STAGING \ 181 | --issue $REQUEST_DOMAINS \ 182 | --home /config/acme.sh/ \ 183 | --cert-home /config/acme.sh/ \ 184 | --config-home /config/acme.sh/ \ 185 | --webroot /config/www/acme_root \ 186 | --keylength ec-384 \ 187 | --ecc \ 188 | --force \ 189 | --ca-file /config/acme.sh/ca.cer \ 190 | --fullchain-file /config/acme.sh/${DOMAINS[0]}_ecc/${DOMAINS[0]}_chain.cer \ 191 | --log /config/logs/acme.sh/acme.log 192 | 193 | else 194 | # Check if certificate should be renewed within the next 30 days 195 | if openssl x509 -in /config/acme.sh/${DOMAINS[0]}_ecc/${DOMAINS[0]}.cer -noout -checkend 2592000 >/dev/null; then 196 | echo "" > /dev/null 197 | else 198 | echo "**** ACME will renew the certificate \"${DOMAINS[0]}\" now. ****" 199 | # Echo affected domains 200 | for DOMAIN in ${DOMAINS[@]} 201 | do 202 | echo "Domain: $DOMAIN" 203 | done 204 | 205 | # Request renewal 206 | ./.acme.sh/acme.sh --renew $REQUEST_DOMAINS \ 207 | --ecc \ 208 | --home /config/acme.sh/ \ 209 | --cert-home /config/acme.sh/ \ 210 | --config-home /config/acme.sh/ \ 211 | --log /config/logs/acme.sh/acme.log \ 212 | --ca-file /config/acme.sh/ca.cer \ 213 | --fullchain-file /config/acme.sh/${DOMAINS[0]}_ecc/${DOMAINS[0]}_chain.cer \ 214 | --reloadcmd "nginx -c /config/nginx/nginx.conf -s reload" 215 | sleep 1 216 | fi 217 | fi 218 | done 219 | 220 | # Kill nginx to avoid issues with s6 service if this is not a renew triggered by cron 221 | if [[ $CRON_JOB == "false" ]]; then 222 | if pgrep -f "[n]ginx:" > /dev/null; then 223 | echo "**** Stopping temporary nginx instance ****" 224 | pkill -f [n]ginx: 225 | sleep 1 226 | fi 227 | if pgrep -f "[n]ginx:" > /dev/null; then 228 | echo "Nginx still active, sending SIGKILL" 229 | pkill -9 -f [n]ginx: 230 | sleep 1 231 | fi 232 | fi 233 | 234 | # Update autoconfig file if this was a container restart 235 | if [[ $CRON_JOB == "false" ]]; then 236 | 237 | # Get autoconf back from backup 238 | mv $AUTOCONF_FILE.bak $AUTOCONF_FILE 239 | 240 | # Build mapping table for certificate public key 241 | echo "# Map the host to a certificate public key path" >> $AUTOCONF_FILE 242 | echo "map \$host \$certfile {" >> $AUTOCONF_FILE 243 | for d in ${CERT_PATHS[@]} 244 | do 245 | # Check if file exists 246 | CER_FILE=$(echo $d | sed 's/^.*=//') 247 | KEY_FILE=$(echo $CER_FILE | sed 's/_chain.cer/.key/g') 248 | if [[ -f $CER_FILE && -f $KEY_FILE ]]; then 249 | echo " $d;" | sed 's/=/ /g' >> $AUTOCONF_FILE 250 | fi 251 | done 252 | echo " default /config/acme.sh/default/fullchain.cer;" >> $AUTOCONF_FILE 253 | echo "}" >> $AUTOCONF_FILE 254 | echo "" >> $AUTOCONF_FILE 255 | 256 | 257 | # Build mapping table for certificate private key 258 | echo "# Map the host to a certificate private key path" >> $AUTOCONF_FILE 259 | echo "map \$host \$keyfile {" >> $AUTOCONF_FILE 260 | for d in ${KEY_PATHS[@]} 261 | do 262 | # Check if file exists 263 | KEY_FILE=$(echo $d | sed 's/^.*=//') 264 | CER_FILE=$(echo $KEY_FILE | sed 's/.key/_chain.cer/g') 265 | if [[ -f $CER_FILE && -f $KEY_FILE ]]; then 266 | echo " $d;" | sed 's/=/ /g' >> $AUTOCONF_FILE 267 | fi 268 | done 269 | echo " default /config/acme.sh/default/privkey.key;" >> $AUTOCONF_FILE 270 | echo "}" >> $AUTOCONF_FILE 271 | echo "" >> $AUTOCONF_FILE 272 | 273 | 274 | # Delete certificates not more used 275 | for i in $CERTIFICATES_TO_DELETE 276 | do 277 | # Backup certificate if a public key is present 278 | CER=$(echo ${i} | sed "s/_ecc//g") 279 | if [[ -f /config/acme.sh/${i}/${CER}.cer ]]; then 280 | mv /config/acme.sh/${i} /config/acme.sh/backup/${i} 281 | sleep 1 282 | fi 283 | rm -rf /config/acme.sh/backup/${i} 284 | sleep 1 285 | done 286 | fi -------------------------------------------------------------------------------- /root/default/nginx/modsec.d/modsecurity.conf: -------------------------------------------------------------------------------- 1 | # -- Rule engine initialization ---------------------------------------------- 2 | 3 | # Enable ModSecurity, attaching it to every transaction. Use detection 4 | # only to start with, because that minimises the chances of post-installation 5 | # disruption. 6 | # 7 | #SecRuleEngine DetectionOnly 8 | SecRuleEngine On 9 | 10 | 11 | # -- Request body handling --------------------------------------------------- 12 | 13 | # Allow ModSecurity to access request bodies. If you don't, ModSecurity 14 | # won't be able to see any POST parameters, which opens a large security 15 | # hole for attackers to exploit. 16 | # 17 | SecRequestBodyAccess On 18 | 19 | 20 | # Enable XML request body parser. 21 | # Initiate XML Processor in case of xml content-type 22 | # 23 | SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\+|/)|text/)xml" \ 24 | "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" 25 | 26 | # Enable JSON request body parser. 27 | # Initiate JSON Processor in case of JSON content-type; change accordingly 28 | # if your application does not use 'application/json' 29 | # 30 | SecRule REQUEST_HEADERS:Content-Type "application/json" \ 31 | "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" 32 | 33 | # Sample rule to enable JSON request body parser for more subtypes. 34 | # Uncomment or adapt this rule if you want to engage the JSON 35 | # Processor for "+json" subtypes 36 | # 37 | #SecRule REQUEST_HEADERS:Content-Type "^application/.+[+]json$" \ 38 | # "id:'200006',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" 39 | 40 | # Maximum request body size we will accept for buffering. If you support 41 | # file uploads then the value given on the first line has to be as large 42 | # as the largest file you are willing to accept. The second value refers 43 | # to the size of data, with files excluded. You want to keep that value as 44 | # low as practical. 45 | # 46 | SecRequestBodyLimit 13107200 47 | SecRequestBodyNoFilesLimit 131072 48 | 49 | # What to do if the request body size is above our configured limit. 50 | # Keep in mind that this setting will automatically be set to ProcessPartial 51 | # when SecRuleEngine is set to DetectionOnly mode in order to minimize 52 | # disruptions when initially deploying ModSecurity. 53 | # 54 | SecRequestBodyLimitAction Reject 55 | 56 | # Verify that we've correctly processed the request body. 57 | # As a rule of thumb, when failing to process a request body 58 | # you should reject the request (when deployed in blocking mode) 59 | # or log a high-severity alert (when deployed in detection-only mode). 60 | # 61 | SecRule REQBODY_ERROR "!@eq 0" \ 62 | "id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2" 63 | 64 | # By default be strict with what we accept in the multipart/form-data 65 | # request body. If the rule below proves to be too strict for your 66 | # environment consider changing it to detection-only. You are encouraged 67 | # _not_ to remove it altogether. 68 | # 69 | SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ 70 | "id:'200003',phase:2,t:none,log,deny,status:400, \ 71 | msg:'Multipart request body failed strict validation: \ 72 | PE %{REQBODY_PROCESSOR_ERROR}, \ 73 | BQ %{MULTIPART_BOUNDARY_QUOTED}, \ 74 | BW %{MULTIPART_BOUNDARY_WHITESPACE}, \ 75 | DB %{MULTIPART_DATA_BEFORE}, \ 76 | DA %{MULTIPART_DATA_AFTER}, \ 77 | HF %{MULTIPART_HEADER_FOLDING}, \ 78 | LF %{MULTIPART_LF_LINE}, \ 79 | SM %{MULTIPART_MISSING_SEMICOLON}, \ 80 | IQ %{MULTIPART_INVALID_QUOTING}, \ 81 | IP %{MULTIPART_INVALID_PART}, \ 82 | IH %{MULTIPART_INVALID_HEADER_FOLDING}, \ 83 | FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'" 84 | 85 | # Did we see anything that might be a boundary? 86 | # 87 | # Here is a short description about the ModSecurity Multipart parser: the 88 | # parser returns with value 0, if all "boundary-like" line matches with 89 | # the boundary string which given in MIME header. In any other cases it returns 90 | # with different value, eg. 1 or 2. 91 | # 92 | # The RFC 1341 descript the multipart content-type and its syntax must contains 93 | # only three mandatory lines (above the content): 94 | # * Content-Type: multipart/mixed; boundary=BOUNDARY_STRING 95 | # * --BOUNDARY_STRING 96 | # * --BOUNDARY_STRING-- 97 | # 98 | # First line indicates, that this is a multipart content, second shows that 99 | # here starts a part of the multipart content, third shows the end of content. 100 | # 101 | # If there are any other lines, which starts with "--", then it should be 102 | # another boundary id - or not. 103 | # 104 | # After 3.0.3, there are two kinds of types of boundary errors: strict and permissive. 105 | # 106 | # If multipart content contains the three necessary lines with correct order, but 107 | # there are one or more lines with "--", then parser returns with value 2 (non-zero). 108 | # 109 | # If some of the necessary lines (usually the start or end) misses, or the order 110 | # is wrong, then parser returns with value 1 (also a non-zero). 111 | # 112 | # You can choose, which one is what you need. The example below contains the 113 | # 'strict' mode, which means if there are any lines with start of "--", then 114 | # ModSecurity blocked the content. But the next, commented example contains 115 | # the 'permissive' mode, then you check only if the necessary lines exists in 116 | # correct order. Whit this, you can enable to upload PEM files (eg "----BEGIN.."), 117 | # or other text files, which contains eg. HTTP headers. 118 | # 119 | # The difference is only the operator - in strict mode (first) the content blocked 120 | # in case of any non-zero value. In permissive mode (second, commented) the 121 | # content blocked only if the value is explicit 1. If it 0 or 2, the content will 122 | # allowed. 123 | # 124 | 125 | # 126 | # See #1747 and #1924 for further information on the possible values for 127 | # MULTIPART_UNMATCHED_BOUNDARY. 128 | # 129 | SecRule MULTIPART_UNMATCHED_BOUNDARY "@eq 1" \ 130 | "id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'" 131 | 132 | 133 | # PCRE Tuning 134 | # We want to avoid a potential RegEx DoS condition 135 | # 136 | SecPcreMatchLimit 1000 137 | SecPcreMatchLimitRecursion 1000 138 | 139 | # Some internal errors will set flags in TX and we will need to look for these. 140 | # All of these are prefixed with "MSC_". The following flags currently exist: 141 | # 142 | # MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded. 143 | # 144 | SecRule TX:/^MSC_/ "!@streq 0" \ 145 | "id:'200005',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'" 146 | 147 | 148 | # -- Response body handling -------------------------------------------------- 149 | 150 | # Allow ModSecurity to access response bodies. 151 | # You should have this directive enabled in order to identify errors 152 | # and data leakage issues. 153 | # 154 | # Do keep in mind that enabling this directive does increases both 155 | # memory consumption and response latency. 156 | # 157 | SecResponseBodyAccess On 158 | 159 | # Which response MIME types do you want to inspect? You should adjust the 160 | # configuration below to catch documents but avoid static files 161 | # (e.g., images and archives). 162 | # 163 | SecResponseBodyMimeType text/plain text/html text/xml 164 | 165 | # Buffer response bodies of up to 512 KB in length. 166 | SecResponseBodyLimit 524288 167 | 168 | # What happens when we encounter a response body larger than the configured 169 | # limit? By default, we process what we have and let the rest through. 170 | # That's somewhat less secure, but does not break any legitimate pages. 171 | # 172 | SecResponseBodyLimitAction ProcessPartial 173 | 174 | 175 | # -- Filesystem configuration ------------------------------------------------ 176 | 177 | # The location where ModSecurity stores temporary files (for example, when 178 | # it needs to handle a file upload that is larger than the configured limit). 179 | # 180 | # This default setting is chosen due to all systems have /tmp available however, 181 | # this is less than ideal. It is recommended that you specify a location that's private. 182 | # 183 | SecTmpDir /tmp/ 184 | 185 | # The location where ModSecurity will keep its persistent data. This default setting 186 | # is chosen due to all systems have /tmp available however, it 187 | # too should be updated to a place that other users can't access. 188 | # 189 | SecDataDir /tmp/ 190 | 191 | 192 | # -- File uploads handling configuration ------------------------------------- 193 | 194 | # The location where ModSecurity stores intercepted uploaded files. This 195 | # location must be private to ModSecurity. You don't want other users on 196 | # the server to access the files, do you? 197 | # 198 | #SecUploadDir /opt/modsecurity/var/upload/ 199 | 200 | # By default, only keep the files that were determined to be unusual 201 | # in some way (by an external inspection script). For this to work you 202 | # will also need at least one file inspection rule. 203 | # 204 | SecUploadKeepFiles On 205 | 206 | # Uploaded files are by default created with permissions that do not allow 207 | # any other user to access them. You may need to relax that if you want to 208 | # interface ModSecurity to an external program (e.g., an anti-virus). 209 | # 210 | #SecUploadFileMode 0600 211 | 212 | 213 | # -- Debug log configuration ------------------------------------------------- 214 | 215 | # The default debug log configuration is to duplicate the error, warning 216 | # and notice messages from the error log. 217 | # 218 | #SecDebugLog /config/logs/nginx/debug.log 219 | #SecDebugLogLevel 3 220 | 221 | 222 | # -- Audit log configuration ------------------------------------------------- 223 | 224 | # Log the transactions that are marked by a rule, as well as those that 225 | # trigger a server error (determined by a 5xx or 4xx, excluding 404, 226 | # level response status codes). 227 | # 228 | SecAuditEngine Off 229 | SecAuditLogRelevantStatus "^(?:5|4(?!04))" 230 | 231 | # Log everything we know about a transaction. 232 | SecAuditLogParts ABIJDEFHZ 233 | 234 | # Use a single file for logging. This is much easier to look at, but 235 | # assumes that you will use the audit log only ocassionally. 236 | # 237 | SecAuditLogType Serial 238 | SecAuditLog /config/logs/nginx/modsec_audit.log 239 | 240 | # Specify the path for concurrent audit logging. 241 | #SecAuditLogStorageDir /opt/modsecurity/var/audit/ 242 | 243 | 244 | # -- Miscellaneous ----------------------------------------------------------- 245 | 246 | # Use the most commonly used application/x-www-form-urlencoded parameter 247 | # separator. There's probably only one application somewhere that uses 248 | # something else so don't expect to change this value. 249 | # 250 | SecArgumentSeparator & 251 | 252 | # Settle on version 0 (zero) cookies, as that is what most applications 253 | # use. Using an incorrect cookie version may open your installation to 254 | # evasion attacks (against the rules that examine named cookies). 255 | # 256 | SecCookieFormat 0 257 | 258 | # Specify your Unicode Code Point. 259 | # This mapping is used by the t:urlDecodeUni transformation function 260 | # to properly map encoded data to your language. Properly setting 261 | # these directives helps to reduce false positives and negatives. 262 | # 263 | SecUnicodeMapFile unicode.mapping 20127 264 | 265 | # Improve the quality of ModSecurity by sharing information about your 266 | # current ModSecurity version and dependencies versions. 267 | # The following information will be shared: ModSecurity version, 268 | # Web Server version, APR version, PCRE version, Lua version, Libxml2 269 | # version, Anonymous unique id for host. 270 | SecStatusEngine On 271 | 272 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Table of Contents 3 | - [Secure-Proxy](#Secure-Proxy) 4 | - [Features](#Features) 5 | - [Documentation](#Documentation) 6 | - [Docker settings](#Docker-Settings) 7 | - [Docker-Compose minimal setup](#Docker-Compose-minimal-Setup) 8 | - [Docker-Compose with Crowdsec](#Docker-Compose-with-Crowdsec) 9 | - [Nginx configuration](#Nginx-configuration) 10 | - [GeoIP configuration](#GeoIP-configuration) 11 | - [OpenID Connect configuration](#OICD-configuration) 12 | - [Additional hardenings](#Additional-hardenings) 13 | - [HSTS Preload](#HSTS-preload) 14 | - [Content Security Policy](#Content-Security-Policy) 15 | - [Other great projects](#Other-great-projects) 16 | 17 |
18 | 19 | 20 | # Secure-Proxy 21 | 22 | Secure-Proxy based on nginx with integrated web application firewall, Let's Encrypt, fail2ban, Crowdsec, ClamAV upload scan and a lot more 😉. It is designed as a pure reverse proxy which faces to the public internet. It can also server static files which you place inside the folder `/config/www`. It has an integrated Openresty Lua support. You can also use this internally but you will not be able to use Let's Encrypt as this requires a http challenge at the moment. The proxy server is designed to secure your application and the data you host on them. It comes with serval well known open source security features like fail2ban ip blocking, ClamAV, etc. 23 | 24 |
25 |
26 | 27 | # Features 28 | 29 | - Let's Encrypt support 30 | - Modern web application firewall with ModSecurity and OWASP Core Rule Set 31 | - Crowdsec cloud based protection system 32 | - CalmAV suspicious file scan for all uploaded files 33 | - Anti DDOS enabled 34 | - Open ID Connect / OAuth2 support enabled 35 | - Block vulnarability scanners 36 | - Prevent SQL and XSS Injection 37 | - Blocks bad-bots, user agents, IP addresses, TOR endpoints and a lot more. 38 | - Automatic update of all blocking, and CRS lists 39 | - Automatic ban by strange behavior 40 | - GeoIP database integration to block/allow specific countries 41 | - Lua Module integrated 42 | - HTTP security headers to prevent click-jacking, sniffing, crawler, etc... 43 | - TLS hardening for modern security 44 | - Nginx leak prevention as non root user 45 | - Improved performance with brotli and server tweaking's 46 | - Mailing agent to be informed about attacks, virus detection and blocking actions 47 | - Automatic file system scan once a week to detect malicious files 48 | - Custom error pages to hide that nginx is running 49 | - Docker dns resolving 50 | - Clean image with auto logrotate 51 | 52 | # Features in pipeline 53 | - Cookie challenge with encrypted cookies to prevent bots, something like https://github.com/kyprizel/testcookie-nginx-module or https://medium.com/secjuice/detecting-human-users-is-there-a-way-to-block-enumeration-fuzz-or-web-scan-14102a92f10b 54 | - HTTPv3 support 55 | 56 | 57 |
58 |
59 | 60 | # Documentation 61 | 62 | 63 | 64 | ## Docker settings: 65 | All configuration files are stored in `/config`. Therefore it is recommended to make this folder persistent and mount it with an docker volume or local path. The proxy instance is listening on port `80` and `443`. You need to map both ports and also configure port forwarding for both ports. There is no security issue if you open port 80 as there is an immediate redirect to port 443. Port 80 is only required for the initial connect of the Let's Encrypt http challenge. 66 | 67 | #### General docker parameters: 68 | - `-p 80:80` 69 | - `-p 443:443` 70 | - `-v /local/path/to/config/folder:/config` 71 | - `--cap-add=NET_ADMIN` (Is required to block suspicious endpoints in case of attacks) 72 | 73 | #### Environment variables: 74 | 75 | | Name | Required | Description | 76 | | :--- | :----: | :--- | 77 | | TZ | yes | Set your [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for logs, cron jobs and syncs | 78 | | PUID | no | User id to use in case of permission issues | 79 | | PGID | no | Group id of the user provided in PUID in case of permission issues | 80 | | CERT_FILE_ | yes | Each environment variable that starts with `CERT_FILE_` will be considered as one certificate to create. You can add multiple dns names to this certificate like `CERT_FILE_WORDPRESS=example.com www.example.com wordpress.example.com` If you need additional certificates you can add them as well, just add another variable liek `CERT_FILE_NEXTCLOUD=nextcloud.example.com` to generate another certificate. | 81 | | CERT_STAGING | yes | Set to `true` for testing. For production use `false` to get a valid certificate which is trusted by web browsers | 82 | | CLAMAV_SYSTEM_SCAN | no | Enables or disables the ClamAV file system scan. Values are `enabled` and `disabled`. Default is `enabled` | 83 | | CLAMAV_ACTION | no | Action to perform on infection found. Allowed actions are `delete`, `move` and `ignore`. Default is `delete` | 84 | | CLAMAV_MAIL_REPORT | no | Send ClamAV report for file syste scans. Values: 0 = disabled, 1 = if infected, 2 = on every scan. Default is 1 | 85 | | CLAMAV_SCAN_DIR | no | Directories to scan, seperated by space. If this is empty the whole filesystem will be scanned | 86 | | CLAMAV_IGNORE_DIR | no | Directories to ignore, seperated by space. By default special system locations are ignored but you can add more here | 87 | | CROWDSEC_URL | no | URL of the crowdsec instance to use for a cloud based protection system | 88 | | CROWDSEC_API_TOKEN | no | API token for crowdsec bouncers, if the `CROWDSEC_API_TOKEN_FILE` is not provided it will use the clear text api token | 89 | | CROWDSEC_API_TOKEN_FILE | no | API token to use for crowdsec bouncers | 90 | | FAIL2BAN_ENABLED | no | Defines if fail2ban is enabled. For Testing and modsecurity rule creation you might disable this. Values are `enabled` and `disabled`. Default is `enabled` | 91 | | FAIL2BAN_MAIL_REPORT | no | Send a notification mail for ban/unban actions. Values are `enabled` and `disabled`. Default is `enabled` | 92 | | SMTP_SERVER | no | Smtp server and port for email notifications (e.g. "smtp.example.com:587") | 93 | | SMTP_SENDER_MAIL | no | Smtp sender address for outgoing mails. Required if you provide the parameter `SMTP_SERVER` | 94 | | SMTP_SENDER_NAME | no | Smtp sender name to display on outgoing mails. Required if you provide the parameter `SMTP_SERVER` | 95 | | SMTP_RECEIVER | no | Smtp mail receiver for your notifiations. Required if you provide the parameter `SMTP_SERVER` | 96 | | SMTP_PASSWORD | no | Smtp account password. Required if parameter `SMTP_SERVER` without `SMTP_PASSWORD_FILE` parameter was used | 97 | | SMTP_PASSWORD_FILE | no | Smtp account password which can be linked from a file or an docker secret. It is recommended to use this instead of `SMTP_PASSWORD` as the password is not written in cleartext. Required if parameter `SMTP_SERVER` without `SMTP_PASSWORD` was provided| 98 | | UPDATE_CONFIGURATION | no | Configure automatic updates for configuration files. By default the container informs you about config updates in the container log on startup and only updates required configuration files. You need to remove the outdated config files and restart the container to get the latest updates. To configure automatic updates set this variable to `enabled`. This will not delete your configured sites, it will just update the configuration files. Default is `disabled` | 99 | | UPDATE_SKIP_FILES | no | With this environmne tvariable you can specify files which should not be automatically updated as you modified them manually. All files in `/config/nginx/sites-available/` are skipped by default. IF you want to skip also the security-headers you can include them with `/config/nginx/conf.d/security-headers.conf` | 100 | 101 |
102 |
103 | 104 | ## Docker-Compose with minimal settings 105 | 106 | This docker-compose file will start the secure-proxy with a minimum of settings and shows how the basic setup is working. 107 | ``` 108 | version: "3.7" 109 | services: 110 | secure-proxy: 111 | image: ghcr.io/flo-mic/secure-proxy:latest 112 | hostname: secure-proxy 113 | networks: 114 | - backend 115 | ports: 116 | - "80:80" 117 | - "443:443" 118 | cap_add: 119 | - NET_ADMIN 120 | environment: 121 | - TZ=Europe/Berlin 122 | - CERT_FILE_WORDPRESS=example.com www.example.com wordpress.example.com 123 | - CERT_FILE_NEXTCLOUD=nextcloud.example.com 124 | - CERT_STAGING=false 125 | volumes: 126 | - data:/config 127 | restart: unless-stopped 128 | 129 | networks: 130 | backend: # mount your backend applications here to avoid exposing them to the host 131 | #external: # Uncommend to use external backend, recommended to allow compose restart without removing the other containers before. 132 | # name: application_backend 133 | 134 | volumes: 135 | data: 136 | external: false 137 | ``` 138 | 139 |
140 |
141 | 142 | ## Docker-Compose setup with Crowdsec 143 | 144 | Crowdsec is an online protection platform which shares bad ip's, attacks and other suspicious with the whole community. If someone faces an attack this attack can be blocked immediately. Also the attack details are shared with the whole community and everyone gets a benefit from this knowledge. Crowdsec is tracking all activities inside the nginx log files and can provide prevention actions like blocking or forcing a captcha request immediately. As this is a cloud based protection system it will share some details from your environment with the whole community. The shared details are a timestamp, ip of the attacker and attack details if an suspicious behavior is detected. 145 | 146 | To get this working with the secure proxy you need to perform the following steps: 147 | 148 | 1. Create a docker-compose file and place the following content inside the file. 149 | ``` 150 | version: "3.7" 151 | services: 152 | secure-proxy: 153 | image: ghcr.io/flo-mic/secure-proxy:latest 154 | container_name: secure-proxy 155 | hostname: secure-proxy 156 | networks: 157 | - backend 158 | - crowdsec 159 | ports: 160 | - "80:80" 161 | - "443:443" 162 | cap_add: 163 | - NET_ADMIN 164 | environment: 165 | - TZ=Europe/Berlin 166 | - CERT_FILE_WORDPRESS=example.com www.example.com wordpress.example.com 167 | - CERT_FILE_NEXTCLOUD=nextcloud.example.com 168 | - CERT_STAGING=false 169 | - CROWDSEC_URL=http://crowdsec:8080 170 | - CROWDSEC_API_TOKEN="" # Will be created later 171 | volumes: 172 | - data:/config 173 | - nginx_logs:/config/logs/nginx 174 | restart: unless-stopped 175 | 176 | crowdsec: 177 | image: crowdsecurity/crowdsec 178 | container_name: crowdsec 179 | hostname: crowdsec 180 | networks: 181 | - crowdsec 182 | volumes: 183 | - crowdsec_config:/etc/crowdsec/ 184 | - crowdsec_data:/var/lib/crowdsec/data/ 185 | - nginx_logs:/var/log/nginx:ro 186 | environment: 187 | - COLLECTIONS=crowdsecurity/nginx crowdsecurity/base-http-scenarios crowdsecurity/whitelist-good-actors crowdsecurity/http-cve 188 | - PARSERS=crowdsecurity/whitelists 189 | - REGISTER_TO_ONLINE_API=true 190 | restart: unless-stopped 191 | 192 | networks: 193 | crowdsec: 194 | backend: # mount your backend applications here to avoid exposing them to the host 195 | #external: # Uncommend to use external backend, recommended to allow compose restart without removing the other containers before. 196 | # name: application_backend 197 | 198 | volumes: 199 | data: 200 | external: false 201 | nginx_logs: 202 | external: false 203 | crowdsec_config: 204 | external: false 205 | crowdsec_data: 206 | external: false 207 | ``` 208 | 2. Start Crowdsec from the docker compose file with `docker-compose up -d crowdsec` 209 | 3. Update the Crowdsec config to parse nginx log files (If you have another Crowdsec instance with existing config make sure to append the string instead of replacing it) 210 | ``` 211 | docker-compose exec crowdsec ash -c "echo 'filenames: 212 | - /var/log/nginx/*access*.log 213 | - /var/log/nginx/*error*.log 214 | labels: 215 | type: nginx 216 | ---' > /etc/crowdsec/acquis.yaml" 217 | ``` 218 | 4. Connect to the Crowdsec container to generate an api token `docker-compose exec crowdsec cscli bouncers add secure-proxy` 219 | 5. Read the api token from the console output and place it inside the environment variable `CROWDSEC_API_TOKEN` in the compose file. (Or save it as docker secret and mount as `CROWDSEC_API_TOKEN_FILE` to avoid writing this as clear text.) 220 | 6. Restart the docker-compose to load the api token in the secure-proxy container `docker-compose restart` 221 | 222 |
223 |
224 | 225 | ## Nginx configuration 226 | 227 | The nginx configuration of the secure-proxy is done with configuration files as you may know from existing nginx setups. There is no integration of environment variables as this would be to complex to realize and the amount of nginx configuration options is just to big. This image comes with a preconfigured nginx which requires modern security settings on all end devices. So old devices which do not use TLS 1.3 are not able to connect. If this is to strict for your use case you can change this settings in the related ssl config file. 228 | 229 | ### Nginx config locations: 230 | 231 | - `/config/nginx/nginx.conf` -> Main configuration file of nginx 232 | - `/config/nginx/conf.d/` -> Directory contains configurations which are available globaly and loaded in the http directive of nginx. 233 | - `/config/nginx/modsec.d/` -> Directory contains the main configuration files of the ModSecurity web application firewall and OWASP core rule set. 234 | - `/config/nginx/sites-available/` -> Directory contains all server locations to load. A default location as already available and can be used as reference. 235 | - `/config/nginx/sites-conf.d/` -> Directory contains configurations which will be loaded in the server directive. By default all configurations within this folder are loaded in the available sites. 236 | - `/config/www/` -> Directory contains static websites to serve. By default only an generic index.html site and custom error pages are available. It is recommended to not remove the custom error pages as they are importand to hide the nginx default page which tells an attacker hat this server is running on nginx. 237 | 238 | 239 |
240 |
241 | 242 | ## GeoIP configuration 243 | 244 | This docker container comes with a Geo-IP database which allows to block specific countries or even better only allow certain countries to block unwanted traffic. If you know from which countries your users are connecting it is recommended to allow only this countries. If you want to open your webserver but still want to block countries which are known for suspicious activities, you can also block specific countries. This is for example useful if you do not want to be attacked by e.g. a chinese or russian hacker group which allways connects from a specific chinese/russian ip (they exist, trust me 😉), you can block all traffic from this countries to avoid such attacks. 245 | 246 | To block such attacks you just need to configure the geoip settings in the related configuration file. The file is located under `/config/nginx/conf.d/geoip.conf`. In this documentation we will show two different settings, one to allow specific countries only and one to block specific countries only. You can adapt the configurations as needed. Just make sure to use the ISO 2 letter country codes inside the file which can be found here: https://www.iban.com/country-codes 247 | 248 | ### Allow only requests from e.g. Germany, Austria and Switzerland 249 | ``` 250 | # GeoIP database file, do not modify this part 251 | geoip2 /usr/share/GeoIP/geoip.mmdb { 252 | auto_reload 5m; 253 | $geoip2_metadata_country_build metadata build_epoch; 254 | $geoip2_data_country_code country iso_code; 255 | } 256 | 257 | # GeoIP allow/blocking list 258 | map $geoip2_data_country_code $allowed_country { 259 | 260 | # Default action to choose if does not come from a country listed below. Default is no to block all requests 261 | default no; 262 | 263 | # Allow traffic from Germany, Austria and Switzerland 264 | AT yes; 265 | CH yes; 266 | DE yes; 267 | } 268 | ``` 269 | 270 | ### Block requests from e.g. China and Russia 271 | ``` 272 | # GeoIP database file, do not modify this part 273 | geoip2 /usr/share/GeoIP/geoip.mmdb { 274 | auto_reload 5m; 275 | $geoip2_metadata_country_build metadata build_epoch; 276 | $geoip2_data_country_code country iso_code; 277 | } 278 | 279 | # GeoIP allow/blocking list 280 | map $geoip2_data_country_code $allowed_country { 281 | 282 | # Default action to choose if does not come from a country listed below. Default is yes to allow all requests 283 | default yes; 284 | 285 | # Block traffic from China and Russia 286 | CN no; 287 | RU no; 288 | } 289 | ``` 290 |
291 |
292 | 293 | ## OpenID Connect configuration 294 | 295 | If you want to use openid conenct to connect e.g. keycloak as an OAuth2 provider you need to add the following content to your protected server or location block. Update all values with the details you received from your identity provider. 296 | 297 | - `session_secret` -> A 32 digits session secret which must be predefined so that all worker processes can use it. If you protect multiple sites with the same OAuth2 provider, you shoudl add the same key to all of them. 298 | - `oidc_discovery_url` -> The discovery URL of you OAuth instance. For keycloak it is something like `https://example.com/auth/realms/example.com/.well-known/openid-configuration` 299 | - `oidc_client_id` -> The client id of the openid-connect client. 300 | - `oidc_client_secret` -> The client secret of the openid-connect client. 301 | - `oidc_logout_path` -> The logout path of the client application, e.g. `/profile/logout` 302 | - `oidc_post_logout_redirect_uri` -> The url to open after lockout, normaly it is the root of the server like `https://app.example.com` 303 | ``` 304 | server { 305 | listen 443 ssl http2; 306 | listen [::]:443 ssl http2; 307 | server_name app.example.com; 308 | ... 309 | 310 | # Set session secret of oicd access 311 | set $session_secret 01234567890123456789012345678901; # 32 digits session key for all worker processes 312 | 313 | # Enable OpenID connection 314 | set $oidc_discovery_url "https://keycloak.example.com/auth/realms/example.com/.well-known/openid-configuration"; 315 | set $oidc_client_id "secure-proxy-client"; 316 | set $oidc_client_secret "********************"; 317 | set $oidc_logout_path "/logout"; 318 | set $oidc_post_logout_redirect_uri "https://app.example.com"; 319 | set $oidc_enabled "true"; 320 | 321 | # Application root location 322 | location / { 323 | 324 | # Proxy endpoint location 325 | proxy_pass https://172.16.1.123:8080; 326 | } 327 | } 328 | ``` 329 | 330 | ### CORS configuration to prevent errors 331 | 332 | In order to prevent CORS issues you should allow your individual subdomains to the allowed origins list. Therefore create a new file with the path `/config/nginx/conf.d/cors.conf` and place the following content inside the file. Replace `example.com` with your domain name. This will map the main domain and all subdomains to the allowed origins map table. 333 | ``` 334 | # Create map table for subdomain origin mapping 335 | map $http_origin $allow_origin { 336 | ~^https?://(.*\.)?example.com(:\d+)?$ $http_origin; 337 | default ""; 338 | } 339 | ``` 340 | 341 | If you identity provider like keycloak is also protected with the secure proxy webserver that you probably need to configure some allow origins for the authentication. Open the keycloak nginx server block setting and make sure to add the below configurations to it. You probably need to whitelist additional headers debending on your client applications. In my case I had to allow the headers `Authorization, X-Requested-With, X-Emby-Authorization, Upgrade-Insecure-Requests` to get my application SSO to work. You can find this out in the browser network tab. If you notice CORS errors have a look at the request which is send before or short after the CORS error. It may contain a header `Access-Control-Request-Headers`. Add the content of this to the content in the header `Access-Control-Allow-Headers`. 342 | ``` 343 | # Overwrite Origin to allow same domain and subdomain origin from the mapping table 344 | more_clear_headers "Access-Control-Allow-Origin"; 345 | add_header 'Access-Control-Allow-Origin' $allow_origin; 346 | 347 | # Overwrite Methods to allow specific methods for OICD redirects 348 | more_clear_headers "Access-Control-Allow-Methods"; 349 | add_header 'Access-Control-Allow-Methods' 'POST, GET, OPTIONS'; 350 | 351 | # Overwrite and allow sending of credentials in the redirect 352 | more_clear_headers "Access-Control-Allow-Credentials"; 353 | add_header 'Access-Control-Allow-Credentials' 'true'; 354 | 355 | # Overwrite Headers to allow specific headers in the redirect 356 | more_clear_headers "Access-Control-Allow-Headers"; 357 | add_header 'Access-Control-Allow-Headers' 'Authorization, X-Requested-With, X-Emby-Authorization, Upgrade-Insecure-Requests'; 358 | 359 | # Avoid caching of allowed origin header 360 | add_header Vary "Origin"; 361 | ``` 362 | 363 |
364 |
365 | 366 | ## Additional hardenings 367 | 368 | 369 | 370 | 371 | ### HSTS Preload 372 | 373 | The HSTS Preload functionality is supported by most browsers and enforces a web connection over a secure HTTPS channel. All domains which are listed on this list can only be accessed if they have a valid SSL certificate. If you want to protect your webpage with HSTS preload you can request this here [hstspreload.org](https://hstspreload.org/). Your webserver is already prepared for this and includes the required settings. 374 | 375 | 376 | 377 | 378 | ### Content Security Policy 379 | 380 | It is recommended to have a strong content security policy (CSP) available for your application. Have a look on your application itself, if it is already sending a strong CSP you are good to go. If not have a look on the CSP generator from "https://cspscanner.com". As an alternative you can also use the Firefox extension "https://addons.mozilla.org/en-US/firefox/addon/laboratory-by-mozilla/" to generate a CSP. You can add a CSP by e.g. adding something like this to your server block location: 381 | ``` 382 | # Add CSP to mitigate and report XSS attack 383 | more_set_headers "Content-Security-Policy: default-src 'none'; upgrade-insecure-requests; connect-src 'self'; base-uri 'self'; frame-ancestors 'self'; form-action 'self'; font-src 'self' data:; img-src 'self' data: blob:; script-src 'self'; style-src 'self'"; 384 | ``` 385 | 386 | 387 |
388 |
389 | 390 | # Other great projects 391 | 392 | - Crowdsec https://crowdsec.net 393 | - Ultimate bad bot blocker https://github.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker 394 | - Nikto web server scanner https://github.com/sullo/nikto 395 | - Nginx-errors https://github.com/bartosjiri/nginx-errors 396 | - Let's Encrypt https://letsencrypt.org/de/ 397 | - ModSecurity https://github.com/SpiderLabs/ModSecurity 398 | - OWASP Core rule set https://github.com/coreruleset/coreruleset 399 | - ClamAV antivirus https://www.clamav.net 400 | - R-FX Networks LMD AV definitions for Webserver https://www.rfxn.com/projects/linux-malware-detect 401 | - Firehol abusers list https://iplists.firehol.org/?ipset=firehol_abusers_30d 402 | - Keycloak identity provider https://www.keycloak.org/ 403 | 404 | -------------------------------------------------------------------------------- /root/default/clamav/clamd.conf: -------------------------------------------------------------------------------- 1 | ## 2 | ## Example config file for the Clam AV daemon 3 | ## Please read the clamd.conf(5) manual before editing this file. 4 | ## 5 | 6 | # Uncomment this option to enable logging. 7 | # LogFile must be writable for the user running daemon. 8 | # A full path is required. 9 | # Default: disabled 10 | LogFile /config/logs/clamav/clamd.log 11 | 12 | # By default the log file is locked for writing - the lock protects against 13 | # running clamd multiple times (if want to run another clamd, please 14 | # copy the configuration file, change the LogFile variable, and run 15 | # the daemon with --config-file option). 16 | # This option disables log file locking. 17 | # Default: no 18 | #LogFileUnlock yes 19 | 20 | # Maximum size of the log file. 21 | # Value of 0 disables the limit. 22 | # You may use 'M' or 'm' for megabytes (1M = 1m = 1048576 bytes) 23 | # and 'K' or 'k' for kilobytes (1K = 1k = 1024 bytes). To specify the size 24 | # in bytes just don't use modifiers. If LogFileMaxSize is enabled, log 25 | # rotation (the LogRotate option) will always be enabled. 26 | # Default: 1M 27 | #LogFileMaxSize 2M 28 | 29 | # Log time with each message. 30 | # Default: no 31 | LogTime yes 32 | 33 | # Also log clean files. Useful in debugging but drastically increases the 34 | # log size. 35 | # Default: no 36 | #LogClean yes 37 | 38 | # Use system logger (can work together with LogFile). 39 | # Default: no 40 | #LogSyslog yes 41 | 42 | # Specify the type of syslog messages - please refer to 'man syslog' 43 | # for facility names. 44 | # Default: LOG_LOCAL6 45 | #LogFacility LOG_MAIL 46 | 47 | # Enable verbose logging. 48 | # Default: no 49 | #LogVerbose yes 50 | 51 | # Enable log rotation. Always enabled when LogFileMaxSize is enabled. 52 | # Default: no 53 | #LogRotate yes 54 | 55 | # Enable Prelude output. 56 | # Default: no 57 | #PreludeEnable yes 58 | # 59 | # Set the name of the analyzer used by prelude-admin. 60 | # Default: ClamAV 61 | #PreludeAnalyzerName ClamAV 62 | 63 | # Log additional information about the infected file, such as its 64 | # size and hash, together with the virus name. 65 | #ExtendedDetectionInfo yes 66 | 67 | # This option allows you to save a process identifier of the listening 68 | # daemon (main thread). 69 | # This file will be owned by root, as long as clamd was started by root. 70 | # It is recommended that the directory where this file is stored is 71 | # also owned by root to keep other users from tampering with it. 72 | # Default: disabled 73 | PidFile /run/clamav/clamd.pid 74 | 75 | # Optional path to the global temporary directory. 76 | # Default: system specific (usually /tmp or /var/tmp). 77 | #TemporaryDirectory /var/tmp 78 | 79 | # Path to the database directory. 80 | # Default: hardcoded (depends on installation options) 81 | #DatabaseDirectory /var/lib/clamav 82 | 83 | # Only load the official signatures published by the ClamAV project. 84 | # Default: no 85 | #OfficialDatabaseOnly no 86 | 87 | # The daemon can work in local mode, network mode or both. 88 | # Due to security reasons we recommend the local mode. 89 | 90 | # Path to a local socket file the daemon will listen on. 91 | # Default: disabled (must be specified by a user) 92 | LocalSocket /run/clamav/clamd.sock 93 | 94 | # Sets the group ownership on the unix socket. 95 | # Default: disabled (the primary group of the user running clamd) 96 | #LocalSocketGroup virusgroup 97 | 98 | # Sets the permissions on the unix socket to the specified mode. 99 | # Default: disabled (socket is world accessible) 100 | #LocalSocketMode 660 101 | 102 | # Remove stale socket after unclean shutdown. 103 | # Default: yes 104 | #FixStaleSocket yes 105 | 106 | # TCP port address. 107 | # Default: no 108 | #TCPSocket 3310 109 | 110 | # TCP address. 111 | # By default we bind to INADDR_ANY, probably not wise. 112 | # Enable the following to provide some degree of protection 113 | # from the outside world. This option can be specified multiple 114 | # times if you want to listen on multiple IPs. IPv6 is now supported. 115 | # Default: no 116 | #TCPAddr 127.0.0.1 117 | 118 | # Maximum length the queue of pending connections may grow to. 119 | # Default: 200 120 | #MaxConnectionQueueLength 30 121 | 122 | # Clamd uses FTP-like protocol to receive data from remote clients. 123 | # If you are using clamav-milter to balance load between remote clamd daemons 124 | # on firewall servers you may need to tune the options below. 125 | 126 | # Close the connection when the data size limit is exceeded. 127 | # The value should match your MTA's limit for a maximum attachment size. 128 | # Default: 25M 129 | #StreamMaxLength 10M 130 | 131 | # Limit port range. 132 | # Default: 1024 133 | #StreamMinPort 30000 134 | # Default: 2048 135 | #StreamMaxPort 32000 136 | 137 | # Maximum number of threads running at the same time. 138 | # Default: 10 139 | #MaxThreads 20 140 | 141 | # Waiting for data from a client socket will timeout after this time (seconds). 142 | # Default: 120 143 | #ReadTimeout 300 144 | 145 | # This option specifies the time (in seconds) after which clamd should 146 | # timeout if a client doesn't provide any initial command after connecting. 147 | # Default: 30 148 | #CommandReadTimeout 30 149 | 150 | # This option specifies how long to wait (in milliseconds) if the send buffer 151 | # is full. 152 | # Keep this value low to prevent clamd hanging. 153 | # 154 | # Default: 500 155 | #SendBufTimeout 200 156 | 157 | # Maximum number of queued items (including those being processed by 158 | # MaxThreads threads). 159 | # It is recommended to have this value at least twice MaxThreads if possible. 160 | # WARNING: you shouldn't increase this too much to avoid running out of file 161 | # descriptors, the following condition should hold: 162 | # MaxThreads*MaxRecursion + (MaxQueue - MaxThreads) + 6< RLIMIT_NOFILE (usual 163 | # max is 1024). 164 | # 165 | # Default: 100 166 | #MaxQueue 200 167 | 168 | # Waiting for a new job will timeout after this time (seconds). 169 | # Default: 30 170 | #IdleTimeout 60 171 | 172 | # Don't scan files and directories matching regex 173 | # This directive can be used multiple times 174 | # Default: scan all 175 | #ExcludePath ^/proc/ 176 | #ExcludePath ^/sys/ 177 | 178 | # Maximum depth directories are scanned at. 179 | # Default: 15 180 | #MaxDirectoryRecursion 20 181 | 182 | # Follow directory symlinks. 183 | # Default: no 184 | #FollowDirectorySymlinks yes 185 | 186 | # Follow regular file symlinks. 187 | # Default: no 188 | #FollowFileSymlinks yes 189 | 190 | # Scan files and directories on other filesystems. 191 | # Default: yes 192 | #CrossFilesystems yes 193 | 194 | # Perform a database check. 195 | # Default: 600 (10 min) 196 | #SelfCheck 600 197 | 198 | # Enable non-blocking (multi-threaded/concurrent) database reloads. 199 | # This feature will temporarily load a second scanning engine while scanning 200 | # continues using the first engine. Once loaded, the new engine takes over. 201 | # The old engine is removed as soon as all scans using the old engine have 202 | # completed. 203 | # This feature requires more RAM, so this option is provided in case users are 204 | # willing to block scans during reload in exchange for lower RAM requirements. 205 | # Default: yes 206 | #ConcurrentDatabaseReload no 207 | 208 | # Execute a command when virus is found. In the command string %v will 209 | # be replaced with the virus name. 210 | # Default: no 211 | #VirusEvent /usr/local/bin/send_sms 123456789 "VIRUS ALERT: %v" 212 | 213 | # Run as another user (clamd must be started by root for this option to work) 214 | # Default: don't drop privileges 215 | User clamav 216 | 217 | # Stop daemon when libclamav reports out of memory condition. 218 | #ExitOnOOM yes 219 | 220 | # Don't fork into background. 221 | # Default: no 222 | Foreground yes 223 | 224 | # Enable debug messages in libclamav. 225 | # Default: no 226 | #Debug yes 227 | 228 | # Do not remove temporary files (for debug purposes). 229 | # Default: no 230 | #LeaveTemporaryFiles yes 231 | 232 | # Permit use of the ALLMATCHSCAN command. If set to no, clamd will reject 233 | # any ALLMATCHSCAN command as invalid. 234 | # Default: yes 235 | #AllowAllMatchScan no 236 | 237 | # Detect Possibly Unwanted Applications. 238 | # Default: no 239 | #DetectPUA yes 240 | 241 | # Exclude a specific PUA category. This directive can be used multiple times. 242 | # See https://github.com/vrtadmin/clamav-faq/blob/master/faq/faq-pua.md for 243 | # the complete list of PUA categories. 244 | # Default: Load all categories (if DetectPUA is activated) 245 | #ExcludePUA NetTool 246 | #ExcludePUA PWTool 247 | 248 | # Only include a specific PUA category. This directive can be used multiple 249 | # times. 250 | # Default: Load all categories (if DetectPUA is activated) 251 | #IncludePUA Spy 252 | #IncludePUA Scanner 253 | #IncludePUA RAT 254 | 255 | # This option causes memory or nested map scans to dump the content to disk. 256 | # If you turn on this option, more data is written to disk and is available 257 | # when the LeaveTemporaryFiles option is enabled. 258 | #ForceToDisk yes 259 | 260 | # This option allows you to disable the caching feature of the engine. By 261 | # default, the engine will store an MD5 in a cache of any files that are 262 | # not flagged as virus or that hit limits checks. Disabling the cache will 263 | # have a negative performance impact on large scans. 264 | # Default: no 265 | #DisableCache yes 266 | 267 | # In some cases (eg. complex malware, exploits in graphic files, and others), 268 | # ClamAV uses special algorithms to detect abnormal patterns and behaviors that 269 | # may be malicious. This option enables alerting on such heuristically 270 | # detected potential threats. 271 | # Default: yes 272 | #HeuristicAlerts yes 273 | 274 | # Allow heuristic alerts to take precedence. 275 | # When enabled, if a heuristic scan (such as phishingScan) detects 276 | # a possible virus/phish it will stop scan immediately. Recommended, saves CPU 277 | # scan-time. 278 | # When disabled, virus/phish detected by heuristic scans will be reported only 279 | # at the end of a scan. If an archive contains both a heuristically detected 280 | # virus/phish, and a real malware, the real malware will be reported 281 | # 282 | # Keep this disabled if you intend to handle "*.Heuristics.*" viruses 283 | # differently from "real" malware. 284 | # If a non-heuristically-detected virus (signature-based) is found first, 285 | # the scan is interrupted immediately, regardless of this config option. 286 | # 287 | # Default: no 288 | #HeuristicScanPrecedence yes 289 | 290 | 291 | ## 292 | ## Heuristic Alerts 293 | ## 294 | 295 | # With this option clamav will try to detect broken executables (both PE and 296 | # ELF) and alert on them with the Broken.Executable heuristic signature. 297 | # Default: no 298 | #AlertBrokenExecutables yes 299 | 300 | # With this option clamav will try to detect broken media file (JPEG, 301 | # TIFF, PNG, GIF) and alert on them with a Broken.Media heuristic signature. 302 | # Default: no 303 | #AlertBrokenMedia yes 304 | 305 | # Alert on encrypted archives _and_ documents with heuristic signature 306 | # (encrypted .zip, .7zip, .rar, .pdf). 307 | # Default: no 308 | #AlertEncrypted yes 309 | 310 | # Alert on encrypted archives with heuristic signature (encrypted .zip, .7zip, 311 | # .rar). 312 | # Default: no 313 | #AlertEncryptedArchive yes 314 | 315 | # Alert on encrypted archives with heuristic signature (encrypted .pdf). 316 | # Default: no 317 | #AlertEncryptedDoc yes 318 | 319 | # With this option enabled OLE2 files containing VBA macros, which were not 320 | # detected by signatures will be marked as "Heuristics.OLE2.ContainsMacros". 321 | # Default: no 322 | #AlertOLE2Macros yes 323 | 324 | # Alert on SSL mismatches in URLs, even if the URL isn't in the database. 325 | # This can lead to false positives. 326 | # Default: no 327 | #AlertPhishingSSLMismatch yes 328 | 329 | # Alert on cloaked URLs, even if URL isn't in database. 330 | # This can lead to false positives. 331 | # Default: no 332 | #AlertPhishingCloak yes 333 | 334 | # Alert on raw DMG image files containing partition intersections 335 | # Default: no 336 | #AlertPartitionIntersection yes 337 | 338 | 339 | ## 340 | ## Executable files 341 | ## 342 | 343 | # PE stands for Portable Executable - it's an executable file format used 344 | # in all 32 and 64-bit versions of Windows operating systems. This option 345 | # allows ClamAV to perform a deeper analysis of executable files and it's also 346 | # required for decompression of popular executable packers such as UPX, FSG, 347 | # and Petite. If you turn off this option, the original files will still be 348 | # scanned, but without additional processing. 349 | # Default: yes 350 | #ScanPE yes 351 | 352 | # Certain PE files contain an authenticode signature. By default, we check 353 | # the signature chain in the PE file against a database of trusted and 354 | # revoked certificates if the file being scanned is marked as a virus. 355 | # If any certificate in the chain validates against any trusted root, but 356 | # does not match any revoked certificate, the file is marked as whitelisted. 357 | # If the file does match a revoked certificate, the file is marked as virus. 358 | # The following setting completely turns off authenticode verification. 359 | # Default: no 360 | #DisableCertCheck yes 361 | 362 | # Executable and Linking Format is a standard format for UN*X executables. 363 | # This option allows you to control the scanning of ELF files. 364 | # If you turn off this option, the original files will still be scanned, but 365 | # without additional processing. 366 | # Default: yes 367 | #ScanELF yes 368 | 369 | 370 | ## 371 | ## Documents 372 | ## 373 | 374 | # This option enables scanning of OLE2 files, such as Microsoft Office 375 | # documents and .msi files. 376 | # If you turn off this option, the original files will still be scanned, but 377 | # without additional processing. 378 | # Default: yes 379 | #ScanOLE2 yes 380 | 381 | # This option enables scanning within PDF files. 382 | # If you turn off this option, the original files will still be scanned, but 383 | # without decoding and additional processing. 384 | # Default: yes 385 | #ScanPDF yes 386 | 387 | # This option enables scanning within SWF files. 388 | # If you turn off this option, the original files will still be scanned, but 389 | # without decoding and additional processing. 390 | # Default: yes 391 | #ScanSWF yes 392 | 393 | # This option enables scanning xml-based document files supported by libclamav. 394 | # If you turn off this option, the original files will still be scanned, but 395 | # without additional processing. 396 | # Default: yes 397 | #ScanXMLDOCS yes 398 | 399 | # This option enables scanning of HWP3 files. 400 | # If you turn off this option, the original files will still be scanned, but 401 | # without additional processing. 402 | # Default: yes 403 | #ScanHWP3 yes 404 | 405 | 406 | ## 407 | ## Mail files 408 | ## 409 | 410 | # Enable internal e-mail scanner. 411 | # If you turn off this option, the original files will still be scanned, but 412 | # without parsing individual messages/attachments. 413 | # Default: yes 414 | #ScanMail yes 415 | 416 | # Scan RFC1341 messages split over many emails. 417 | # You will need to periodically clean up $TemporaryDirectory/clamav-partial 418 | # directory. 419 | # WARNING: This option may open your system to a DoS attack. 420 | # Never use it on loaded servers. 421 | # Default: no 422 | #ScanPartialMessages yes 423 | 424 | # With this option enabled ClamAV will try to detect phishing attempts by using 425 | # HTML.Phishing and Email.Phishing NDB signatures. 426 | # Default: yes 427 | #PhishingSignatures no 428 | 429 | # With this option enabled ClamAV will try to detect phishing attempts by 430 | # analyzing URLs found in emails using WDB and PDB signature databases. 431 | # Default: yes 432 | #PhishingScanURLs no 433 | 434 | 435 | ## 436 | ## Data Loss Prevention (DLP) 437 | ## 438 | 439 | # Enable the DLP module 440 | # Default: No 441 | #StructuredDataDetection yes 442 | 443 | # This option sets the lowest number of Credit Card numbers found in a file 444 | # to generate a detect. 445 | # Default: 3 446 | #StructuredMinCreditCardCount 5 447 | 448 | # With this option enabled the DLP module will search for valid Credit Card 449 | # numbers only. Debit and Private Label cards will not be searched. 450 | # Default: no 451 | #StructuredCCOnly yes 452 | 453 | # This option sets the lowest number of Social Security Numbers found 454 | # in a file to generate a detect. 455 | # Default: 3 456 | #StructuredMinSSNCount 5 457 | 458 | # With this option enabled the DLP module will search for valid 459 | # SSNs formatted as xxx-yy-zzzz 460 | # Default: yes 461 | #StructuredSSNFormatNormal yes 462 | 463 | # With this option enabled the DLP module will search for valid 464 | # SSNs formatted as xxxyyzzzz 465 | # Default: no 466 | #StructuredSSNFormatStripped yes 467 | 468 | 469 | ## 470 | ## HTML 471 | ## 472 | 473 | # Perform HTML normalisation and decryption of MS Script Encoder code. 474 | # Default: yes 475 | # If you turn off this option, the original files will still be scanned, but 476 | # without additional processing. 477 | #ScanHTML yes 478 | 479 | 480 | ## 481 | ## Archives 482 | ## 483 | 484 | # ClamAV can scan within archives and compressed files. 485 | # If you turn off this option, the original files will still be scanned, but 486 | # without unpacking and additional processing. 487 | # Default: yes 488 | #ScanArchive yes 489 | 490 | 491 | ## 492 | ## Limits 493 | ## 494 | 495 | # The options below protect your system against Denial of Service attacks 496 | # using archive bombs. 497 | 498 | # This option sets the maximum amount of time to a scan may take. 499 | # In this version, this field only affects the scan time of ZIP archives. 500 | # Value of 0 disables the limit. 501 | # Note: disabling this limit or setting it too high may result allow scanning 502 | # of certain files to lock up the scanning process/threads resulting in a 503 | # Denial of Service. 504 | # Time is in milliseconds. 505 | # Default: 120000 506 | #MaxScanTime 300000 507 | 508 | # This option sets the maximum amount of data to be scanned for each input 509 | # file. Archives and other containers are recursively extracted and scanned 510 | # up to this value. 511 | # Value of 0 disables the limit 512 | # Note: disabling this limit or setting it too high may result in severe damage 513 | # to the system. 514 | # Default: 100M 515 | #MaxScanSize 150M 516 | 517 | # Files larger than this limit won't be scanned. Affects the input file itself 518 | # as well as files contained inside it (when the input file is an archive, a 519 | # document or some other kind of container). 520 | # Value of 0 disables the limit. 521 | # Note: disabling this limit or setting it too high may result in severe damage 522 | # to the system. 523 | # Technical design limitations prevent ClamAV from scanning files greater than 524 | # 2 GB at this time. 525 | # Default: 25M 526 | #MaxFileSize 30M 527 | 528 | # Nested archives are scanned recursively, e.g. if a Zip archive contains a RAR 529 | # file, all files within it will also be scanned. This options specifies how 530 | # deeply the process should be continued. 531 | # Note: setting this limit too high may result in severe damage to the system. 532 | # Default: 16 533 | #MaxRecursion 10 534 | 535 | # Number of files to be scanned within an archive, a document, or any other 536 | # container file. 537 | # Value of 0 disables the limit. 538 | # Note: disabling this limit or setting it too high may result in severe damage 539 | # to the system. 540 | # Default: 10000 541 | #MaxFiles 15000 542 | 543 | # Maximum size of a file to check for embedded PE. Files larger than this value 544 | # will skip the additional analysis step. 545 | # Note: disabling this limit or setting it too high may result in severe damage 546 | # to the system. 547 | # Default: 10M 548 | #MaxEmbeddedPE 10M 549 | 550 | # Maximum size of a HTML file to normalize. HTML files larger than this value 551 | # will not be normalized or scanned. 552 | # Note: disabling this limit or setting it too high may result in severe damage 553 | # to the system. 554 | # Default: 10M 555 | #MaxHTMLNormalize 10M 556 | 557 | # Maximum size of a normalized HTML file to scan. HTML files larger than this 558 | # value after normalization will not be scanned. 559 | # Note: disabling this limit or setting it too high may result in severe damage 560 | # to the system. 561 | # Default: 2M 562 | #MaxHTMLNoTags 2M 563 | 564 | # Maximum size of a script file to normalize. Script content larger than this 565 | # value will not be normalized or scanned. 566 | # Note: disabling this limit or setting it too high may result in severe damage 567 | # to the system. 568 | # Default: 5M 569 | #MaxScriptNormalize 5M 570 | 571 | # Maximum size of a ZIP file to reanalyze type recognition. ZIP files larger 572 | # than this value will skip the step to potentially reanalyze as PE. 573 | # Note: disabling this limit or setting it too high may result in severe damage 574 | # to the system. 575 | # Default: 1M 576 | #MaxZipTypeRcg 1M 577 | 578 | # This option sets the maximum number of partitions of a raw disk image to be 579 | # scanned. 580 | # Raw disk images with more partitions than this value will have up to 581 | # the value number partitions scanned. Negative values are not allowed. 582 | # Note: setting this limit too high may result in severe damage or impact 583 | # performance. 584 | # Default: 50 585 | #MaxPartitions 128 586 | 587 | # This option sets the maximum number of icons within a PE to be scanned. 588 | # PE files with more icons than this value will have up to the value number 589 | # icons scanned. 590 | # Negative values are not allowed. 591 | # WARNING: setting this limit too high may result in severe damage or impact 592 | # performance. 593 | # Default: 100 594 | #MaxIconsPE 200 595 | 596 | # This option sets the maximum recursive calls for HWP3 parsing during 597 | # scanning. HWP3 files using more than this limit will be terminated and 598 | # alert the user. 599 | # Scans will be unable to scan any HWP3 attachments if the recursive limit 600 | # is reached. 601 | # Negative values are not allowed. 602 | # WARNING: setting this limit too high may result in severe damage or impact 603 | # performance. 604 | # Default: 16 605 | #MaxRecHWP3 16 606 | 607 | # This option sets the maximum calls to the PCRE match function during 608 | # an instance of regex matching. 609 | # Instances using more than this limit will be terminated and alert the user 610 | # but the scan will continue. 611 | # For more information on match_limit, see the PCRE documentation. 612 | # Negative values are not allowed. 613 | # WARNING: setting this limit too high may severely impact performance. 614 | # Default: 100000 615 | #PCREMatchLimit 20000 616 | 617 | # This option sets the maximum recursive calls to the PCRE match function 618 | # during an instance of regex matching. 619 | # Instances using more than this limit will be terminated and alert the user 620 | # but the scan will continue. 621 | # For more information on match_limit_recursion, see the PCRE documentation. 622 | # Negative values are not allowed and values > PCREMatchLimit are superfluous. 623 | # WARNING: setting this limit too high may severely impact performance. 624 | # Default: 2000 625 | #PCRERecMatchLimit 10000 626 | 627 | # This option sets the maximum filesize for which PCRE subsigs will be 628 | # executed. Files exceeding this limit will not have PCRE subsigs executed 629 | # unless a subsig is encompassed to a smaller buffer. 630 | # Negative values are not allowed. 631 | # Setting this value to zero disables the limit. 632 | # WARNING: setting this limit too high or disabling it may severely impact 633 | # performance. 634 | # Default: 25M 635 | #PCREMaxFileSize 100M 636 | 637 | # When AlertExceedsMax is set, files exceeding the MaxFileSize, MaxScanSize, or 638 | # MaxRecursion limit will be flagged with the virus 639 | # "Heuristics.Limits.Exceeded". 640 | # Default: no 641 | #AlertExceedsMax yes 642 | 643 | ## 644 | ## On-access Scan Settings 645 | ## 646 | 647 | # Don't scan files larger than OnAccessMaxFileSize 648 | # Value of 0 disables the limit. 649 | # Default: 5M 650 | #OnAccessMaxFileSize 10M 651 | 652 | # Max number of scanning threads to allocate to the OnAccess thread pool at 653 | # startup. These threads are the ones responsible for creating a connection 654 | # with the daemon and kicking off scanning after an event has been processed. 655 | # To prevent clamonacc from consuming all clamd's resources keep this lower 656 | # than clamd's max threads. 657 | # Default: 5 658 | #OnAccessMaxThreads 10 659 | 660 | # Max amount of time (in milliseconds) that the OnAccess client should spend 661 | # for every connect, send, and recieve attempt when communicating with clamd 662 | # via curl. 663 | # Default: 5000 (5 seconds) 664 | # OnAccessCurlTimeout 10000 665 | 666 | # Toggles dynamic directory determination. Allows for recursively watching 667 | # include paths. 668 | # Default: no 669 | #OnAccessDisableDDD yes 670 | 671 | # Set the include paths (all files inside them will be scanned). You can have 672 | # multiple OnAccessIncludePath directives but each directory must be added 673 | # in a separate line. 674 | # Default: disabled 675 | #OnAccessIncludePath /home 676 | #OnAccessIncludePath /students 677 | 678 | # Set the exclude paths. All subdirectories are also excluded. 679 | # Default: disabled 680 | #OnAccessExcludePath /home/user 681 | 682 | # Modifies fanotify blocking behaviour when handling permission events. 683 | # If off, fanotify will only notify if the file scanned is a virus, 684 | # and not perform any blocking. 685 | # Default: no 686 | #OnAccessPrevention yes 687 | 688 | # When using prevention, if this option is turned on, any errors that occur 689 | # during scanning will result in the event attempt being denied. This could 690 | # potentially lead to unwanted system behaviour with certain configurations, 691 | # so the client defaults this to off and prefers allowing access events in 692 | # case of scan or connection error. 693 | # Default: no 694 | #OnAccessDenyOnError yes 695 | 696 | # Toggles extra scanning and notifications when a file or directory is 697 | # created or moved. 698 | # Requires the DDD system to kick-off extra scans. 699 | # Default: no 700 | #OnAccessExtraScanning yes 701 | 702 | # Set the mount point to be scanned. The mount point specified, or the mount 703 | # point containing the specified directory will be watched. If any directories 704 | # are specified, this option will preempt (disable and ignore all options 705 | # related to) the DDD system. This option will result in verdicts only. 706 | # Note that prevention is explicitly disallowed to prevent common, fatal 707 | # misconfigurations. (e.g. watching "/" with prevention on and no exclusions 708 | # made on vital system directories) 709 | # It can be used multiple times. 710 | # Default: disabled 711 | #OnAccessMountPath / 712 | #OnAccessMountPath /home/user 713 | 714 | # With this option you can whitelist the root UID (0). Processes run under 715 | # root with be able to access all files without triggering scans or 716 | # permission denied events. 717 | # Note that if clamd cannot check the uid of the process that generated an 718 | # on-access scan event (e.g., because OnAccessPrevention was not enabled, and 719 | # the process already exited), clamd will perform a scan. Thus, setting 720 | # OnAccessExcludeRootUID is not *guaranteed* to prevent every access by the 721 | # root user from triggering a scan (unless OnAccessPrevention is enabled). 722 | # Default: no 723 | #OnAccessExcludeRootUID no 724 | 725 | # With this option you can whitelist specific UIDs. Processes with these UIDs 726 | # will be able to access all files without triggering scans or permission 727 | # denied events. 728 | # This option can be used multiple times (one per line). 729 | # Using a value of 0 on any line will disable this option entirely. 730 | # To whitelist the root UID (0) please enable the OnAccessExcludeRootUID 731 | # option. 732 | # Also note that if clamd cannot check the uid of the process that generated an 733 | # on-access scan event (e.g., because OnAccessPrevention was not enabled, and 734 | # the process already exited), clamd will perform a scan. Thus, setting 735 | # OnAccessExcludeUID is not *guaranteed* to prevent every access by the 736 | # specified uid from triggering a scan (unless OnAccessPrevention is enabled). 737 | # Default: disabled 738 | #OnAccessExcludeUID -1 739 | 740 | # This option allows exclusions via user names when using the on-access 741 | # scanning client. It can be used multiple times. 742 | # It has the same potential race condition limitations of the 743 | # OnAccessExcludeUID option. 744 | # Default: disabled 745 | #OnAccessExcludeUname clamav 746 | 747 | # Number of times the OnAccess client will retry a failed scan due to 748 | # connection problems (or other issues). 749 | # Default: 0 750 | #OnAccessRetryAttempts 3 751 | 752 | ## 753 | ## Bytecode 754 | ## 755 | 756 | # With this option enabled ClamAV will load bytecode from the database. 757 | # It is highly recommended you keep this option on, otherwise you'll miss 758 | # detections for many new viruses. 759 | # Default: yes 760 | #Bytecode yes 761 | 762 | # Set bytecode security level. 763 | # Possible values: 764 | # None - No security at all, meant for debugging. 765 | # DO NOT USE THIS ON PRODUCTION SYSTEMS. 766 | # This value is only available if clamav was built 767 | # with --enable-debug! 768 | # TrustSigned - Trust bytecode loaded from signed .c[lv]d files, insert 769 | # runtime safety checks for bytecode loaded from other sources. 770 | # Paranoid - Don't trust any bytecode, insert runtime checks for all. 771 | # Recommended: TrustSigned, because bytecode in .cvd files already has these 772 | # checks. 773 | # Note that by default only signed bytecode is loaded, currently you can only 774 | # load unsigned bytecode in --enable-debug mode. 775 | # 776 | # Default: TrustSigned 777 | #BytecodeSecurity TrustSigned 778 | 779 | # Allow loading bytecode from outside digitally signed .c[lv]d files. 780 | # **Caution**: You should NEVER run bytecode signatures from untrusted sources. 781 | # Doing so may result in arbitrary code execution. 782 | # Default: no 783 | #BytecodeUnsigned yes 784 | 785 | # Set bytecode timeout in milliseconds. 786 | # 787 | # Default: 5000 788 | # BytecodeTimeout 1000 789 | --------------------------------------------------------------------------------