initial upload

This commit is contained in:
2025-10-10 11:07:34 +00:00
commit 6224cd01c6
161 changed files with 8964 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
---
apache_consul_service: "{{ consul_services|default(False) }}"
apache_mpm_prefork: true
apache_timeout: 30
apache_monitoring_ips: "{{ (nagios_nrpe_monitoring_ips|default([]) + firewall_monitoring_ips|default([])) | join(' ') }}"
apache_mod_ssl_protocols: all -SSLv2 -SSLv3 -TLSv1
apache_mod_ssl_ciphers:
- ECDHE-RSA-AES128-GCM-SHA256
- ECDHE-ECDSA-AES128-GCM-SHA256
- ECDHE-RSA-AES256-GCM-SHA384
- ECDHE-ECDSA-AES256-GCM-SHA384
- ECDHE-RSA-CHACHA20-POLY1305
- ECDHE-ECDSA-CHACHA20-POLY1305
- ECDHE-RSA-AES128-SHA256
- ECDHE-ECDSA-AES128-SHA256
- ECDHE-RSA-AES256-SHA384
- ECDHE-ECDSA-AES256-SHA384
- ECDHE-RSA-AES128-SHA
- ECDHE-ECDSA-AES128-SHA
- ECDHE-RSA-AES256-SHA
- ECDHE-ECDSA-AES256-SHA
- DHE-RSA-AES128-GCM-SHA256
- DHE-RSA-AES256-GCM-SHA384
- DHE-RSA-AES128-SHA256
- DHE-RSA-AES256-SHA256
- DHE-RSA-AES128-SHA
- DHE-RSA-AES256-SHA
# - AES128-GCM-SHA256
# - AES256-GCM-SHA384
# - AES128-SHA256
# - AES256-SHA256
# - AES128-SHA
# - AES256-SHA
apache_http2_enabled: on
apache_firewall: yes
apache_firewall_public: yes
apache_firewall_public_isolated: no
apache_firewall_acl: []
apache_firewall_drop_dst: []
apache_security_headers: false
apache_mod_evasive: off
apache_mod_evasive_settings:
DOSHashTableSize: 3097
DOSPageCount: 20
DOSSiteCount: 100
DOSPageInterval: 2
DOSSiteInterval: 1
DOSBlockingPeriod: 10
apache_mod_security: "{{ apache_firewall_public }}"
apache_mod_security_enabled: false

View File

@@ -0,0 +1,11 @@
---
- name: Restart Apache
service: name=apache2 state=restarted
- name: Reload Apache
service: name=apache2 state=reloaded
- name: Reload Apache systemd
systemd: daemon_reload=yes

View File

@@ -0,0 +1,8 @@
---
dependencies:
- role: firewall
when: apache_firewall
- role: consul
when: apache_consul_service

164
roles/apache/tasks/main.yml Normal file
View File

@@ -0,0 +1,164 @@
---
- name: Install Apache packages
apt:
pkg:
- apache2
- socat
state: present
tags: packages
- name: Ensure the ssl-cert group exists
group:
name: ssl-cert
system: yes
tags: packages
- name: Ensure apache is a member of ssl-cert
user:
name: www-data
groups: ssl-cert
append: yes
tags: packages
- name: Install Apache config
template:
dest: /etc/apache2/apache2.conf
src: etc_apache2_apache2.conf.j2
mode: 0644
owner: root
group: root
notify: Reload Apache
tags: configs
- name: Install Apache module configs
template:
dest: "/etc/apache2/mods-available/{{ item }}"
src: "etc_apache2_mods-available_{{ item }}.j2"
mode: 0644
owner: root
group: root
with_items:
- deflate.conf
- http2.conf
- ssl.conf
- status.conf
notify: Reload Apache
tags:
- configs
- apache-configs
- name: Enable Apache modules
apache2_module:
name: "{{ item }}"
state: present
force: yes
with_items:
- deflate
- env
- expires
- headers
- http2
- reqtimeout
- rewrite
- setenvif
- ssl
- status
- unique_id
notify: Restart Apache
tags: configs
- name: Install Apache other configs
template:
dest: "/etc/apache2/conf-available/{{ item }}"
src: "etc_apache2_conf-available_{{ item }}.j2"
with_items:
- logging.conf
- security.conf
notify: Reload Apache
tags: [configs, logging]
- name: Enable Apache other configs
command: "a2enconf {{ item }}"
args:
creates: "/etc/apache2/conf-enabled/{{ item }}"
with_items:
- logging.conf
- security.conf
notify: Reload Apache
tags: configs
- name: Enable the SSL default vhost
command: a2ensite default-ssl
args:
creates: /etc/apache2/sites-enabled/default-ssl.conf
notify: Reload Apache
tags: configs
- name: Install Apache logrotate snippet
template:
dest: /etc/logrotate.d/apache2
src: etc_logrotate.d_apache2.j2
mode: 0644
owner: root
group: root
tags: [configs, logrotate]
- name: Install apache2.service override dir
file:
dest: /etc/systemd/system/apache2.service.d
state: directory
mode: 0755
owner: root
group: root
tags: [configs, systemd]
- name: Install apache2.service override
template:
dest: /etc/systemd/system/apache2.service.d/local.conf
src: etc_systemd_system_apache2.service.d_local.conf.j2
mode: 0644
owner: root
group: root
notify: Reload Apache systemd
tags: [configs, systemd]
- name: Ensure Apache is running
systemd:
name: apache2
state: started
enabled: yes
tags: configs
- include: mod_evasive.yml
when: apache_mod_evasive
tags: mod_evasive
- include: mod_security.yml
when: apache_mod_security
tags: mod_security
- name: Install the Apache firewall config
template:
dest: "/etc/firewall/{{ item }}"
src: "etc_firewall_{{ item | replace('/', '_') }}.j2"
mode: 0600
owner: root
group: root
when: firewall_enabled and apache_firewall
notify: Restart firewall
with_items:
- rules-v4.d/40_apache.sh
- rules-v6.d/40_apache.sh
tags:
- configs
- firewall
- name: Register the apache service in Consul
template:
dest: /etc/consul.d/service-apache.hcl
src: etc_consul.d_service-apache.hcl.j2
when: apache_consul_service
notify: Reload consul
tags: configs

View File

@@ -0,0 +1,27 @@
---
- name: Install Apache mod_evasive
apt:
pkg:
- libapache2-mod-evasive
state: present
notify: Restart Apache
tags: packages
- name: Install Apache mod_evasive config
template:
dest: /etc/apache2/mods-available/evasive.conf
src: etc_apache2_mods-available_evasive.conf.j2
mode: 0644
owner: root
group: root
notify: Reload Apache
tags: configs
- name: Enable Apache mod_evasive
apache2_module:
name: evasive
state: present
force: yes
notify: Restart Apache
tags: configs

View File

@@ -0,0 +1,38 @@
---
- name: Install Apache mod_security
apt:
pkg:
- libapache2-mod-security2=2.9.*
- modsecurity-crs=3.*
state: present
notify: Restart Apache
tags: packages
- name: Install Apache mod_security config
template:
dest: /etc/modsecurity/modsecurity.conf
src: etc_modsecurity_modsecurity.conf.j2
mode: 0644
owner: root
group: root
notify: Reload Apache
tags: configs
- name: Install Apache mod_security ruleset config
template:
dest: /etc/modsecurity/crs/crs-setup.conf
src: etc_modsecurity_crs_crs-setup.conf.j2
mode: 0644
owner: root
group: root
notify: Reload Apache
tags: configs
- name: Enable Apache mod_security
apache2_module:
name: security2
state: present
force: yes
notify: Restart Apache
tags: configs

View File

@@ -0,0 +1,233 @@
# {{ ansible_managed }}
# This is the main Apache server configuration file. It contains the
# configuration directives that give the server its instructions.
# See http://httpd.apache.org/docs/2.4/ for detailed information about
# the directives and /usr/share/doc/apache2/README.Debian about Debian specific
# hints.
#
#
# Summary of how the Apache 2 configuration works in Debian:
# The Apache 2 web server configuration in Debian is quite different to
# upstream's suggested way to configure the web server. This is because Debian's
# default Apache2 installation attempts to make adding and removing modules,
# virtual hosts, and extra configuration directives as flexible as possible, in
# order to make automating the changes and administering the server as easy as
# possible.
# It is split into several files forming the configuration hierarchy outlined
# below, all located in the /etc/apache2/ directory:
#
# /etc/apache2/
# |-- apache2.conf
# | `-- ports.conf
# |-- mods-enabled
# | |-- *.load
# | `-- *.conf
# |-- conf-enabled
# | `-- *.conf
# `-- sites-enabled
# `-- *.conf
#
#
# * apache2.conf is the main configuration file (this file). It puts the pieces
# together by including all remaining configuration files when starting up the
# web server.
#
# * ports.conf is always included from the main configuration file. It is
# supposed to determine listening ports for incoming connections which can be
# customized anytime.
#
# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/
# directories contain particular configuration snippets which manage modules,
# global configuration fragments, or virtual host configurations,
# respectively.
#
# They are activated by symlinking available configuration files from their
# respective *-available/ counterparts. These should be managed by using our
# helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See
# their respective man pages for detailed information.
#
# * The binary is called apache2. Due to the use of environment variables, in
# the default configuration, apache2 needs to be started/stopped with
# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not
# work with the default configuration.
# Global configuration
#
#
# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.
#
# NOTE! If you intend to place this on an NFS (or otherwise network)
# mounted filesystem then please read the Mutex documentation (available
# at <URL:http://httpd.apache.org/docs/2.4/mod/core.html#mutex>);
# you will save yourself a lot of trouble.
#
# Do NOT add a slash at the end of the directory path.
#
#ServerRoot "/etc/apache2"
#
# The accept serialization lock file MUST BE STORED ON A LOCAL DISK.
#
#Mutex file:${APACHE_LOCK_DIR} default
#
# The directory where shm and other runtime files will be stored.
#
DefaultRuntimeDir ${APACHE_RUN_DIR}
#
# PidFile: The file in which the server should record its process
# identification number when it starts.
# This needs to be set in /etc/apache2/envvars
#
PidFile ${APACHE_PID_FILE}
#
# Timeout: The number of seconds before receives and sends time out.
#
Timeout {{ apache_timeout }}
#
# KeepAlive: Whether or not to allow persistent connections (more than
# one request per connection). Set to "Off" to deactivate.
#
KeepAlive On
#
# MaxKeepAliveRequests: The maximum number of requests to allow
# during a persistent connection. Set to 0 to allow an unlimited amount.
# We recommend you leave this number high, for maximum performance.
#
MaxKeepAliveRequests 100
#
# KeepAliveTimeout: Number of seconds to wait for the next request from the
# same client on the same connection.
#
KeepAliveTimeout 5
# These need to be set in /etc/apache2/envvars
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}
#
# HostnameLookups: Log the names of clients or just their IP addresses
# e.g., www.apache.org (on) or 204.62.129.132 (off).
# The default is off because it'd be overall better for the net if people
# had to knowingly turn this feature on, since enabling it means that
# each client request will result in AT LEAST one lookup request to the
# nameserver.
#
HostnameLookups Off
# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a <VirtualHost>
# container, error messages relating to that virtual host will be
# logged here. If you *do* define an error logfile for a <VirtualHost>
# container, that host's errors will be logged there and not here.
#
ErrorLog ${APACHE_LOG_DIR}/error.log
#
# LogLevel: Control the severity of messages logged to the error_log.
# Available values: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the log level for particular modules, e.g.
# "LogLevel info ssl:warn"
#
LogLevel warn
# Include module configuration:
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf
# Include list of ports to listen on
Include ports.conf
# Sets the default security model of the Apache2 HTTPD server. It does
# not allow access to the root filesystem outside of /usr/share and /var/www.
# The former is used by web applications packaged in Debian,
# the latter may be used for local directories served by the web server. If
# your system is serving content from a sub-directory in /srv you must allow
# access here, or in any related virtual host.
<Directory />
Options FollowSymLinks
AllowOverride None
Require all denied
</Directory>
<Directory /usr/share>
AllowOverride None
Require all granted
</Directory>
<Directory /var/www/>
Options FollowSymLinks
AllowOverride None
Require all granted
</Directory>
<Directory /srv/www>
Options FollowSymLinks
AllowOverride None
Require all granted
</Directory>
<Directory /opt/kc>
Options FollowSymLinks
AllowOverride None
Require all granted
</Directory>
# AccessFileName: The name of the file to look for in each directory
# for additional configuration directives. See also the AllowOverride
# directive.
#
AccessFileName .htaccess
#
# The following lines prevent dot files from being
# viewed by Web clients.
#
<FilesMatch "^\.(?!well-known)">
Require all denied
</FilesMatch>
#
# The following directives define some format nicknames for use with
# a CustomLog directive.
#
# These deviate from the Common Log Format definitions in that they use %O
# (the actual bytes sent including headers) instead of %b (the size of the
# requested file), because the latter makes it impossible to detect partial
# requests.
#
# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended.
# Use mod_remoteip instead.
#
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
# Include of directories ignores editors' and dpkg's backup files,
# see README.Debian for details.
# Include generic snippets of statements
IncludeOptional conf-enabled/*.conf
# Include the virtual host configurations:
IncludeOptional sites-enabled/*.conf
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,7 @@
# {{ ansible_managed }}
# BufferedLogs On
LogFormat "%v:%p %R %m %>s %H conn=%X %D %O %I %k" metrics
GlobalLog ${APACHE_LOG_DIR}/metrics.log metrics

View File

@@ -0,0 +1,88 @@
# {{ ansible_managed }}
#
# Disable access to the entire file system except for the directories that
# are explicitly allowed later.
#
# This currently breaks the configurations that come with some web application
# Debian packages.
#
<Directory />
AllowOverride None
Require all denied
</Directory>
# Changing the following options will not really affect the security of the
# server, but might make attacks slightly more difficult in some cases.
#
# ServerTokens
# This directive configures what you return as the Server HTTP response
# Header. The default is 'Full' which sends information about the OS-Type
# and compiled in modules.
# Set to one of: Full | OS | Minimal | Minor | Major | Prod
# where Full conveys the most information, and Prod the least.
ServerTokens Prod
#ServerTokens OS
#ServerTokens Full
#
# Optionally add a line containing the server version and virtual host
# name to server-generated pages (internal error documents, FTP directory
# listings, mod_status and mod_info output etc., but not CGI generated
# documents or custom error documents).
# Set to "EMail" to also include a mailto: link to the ServerAdmin.
# Set to one of: On | Off | EMail
ServerSignature Off
#ServerSignature On
#
# Allow TRACE method
#
# Set to "extended" to also reflect the request body (only for testing and
# diagnostic purposes).
#
# Set to one of: On | Off | extended
TraceEnable Off
#TraceEnable On
#
# Forbid access to version control directories
#
# If you use version control systems in your document root, you should
# probably deny access to their directories. For example, for subversion:
#
<DirectoryMatch "/\.(git|svn|subversion)">
Require all denied
</DirectoryMatch>
#
# Setting this header will prevent MSIE from interpreting files as something
# else than declared by the content type in the HTTP headers.
# Requires mod_headers to be enabled.
#
#Header set X-Content-Type-Options: "nosniff"
#
# Setting this header will prevent other sites from embedding pages from this
# site as frames. This defends against clickjacking attacks.
# Requires mod_headers to be enabled.
#
#Header set X-Frame-Options: "sameorigin"
{% if apache_security_headers %}
#
# Security headers for PCI-DSS.
#
Header always set X-Content-Type-Options: "nosniff"
Header always set X-Frame-Options: "sameorigin"
Header always set X-XSS-Protection "1; mode=block"
{% endif %}
#
# Accept host names with _underscores_
#
HTTPProtocolOptions unsafe
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,22 @@
# {{ ansible_managed }}
<IfModule mod_deflate.c>
<IfModule mod_filter.c>
# these are known to be safe with MSIE 6
AddOutputFilterByType DEFLATE text/html text/plain text/xml image/svg+xml
# everything else may cause problems with MSIE 6
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/x-php-serialized-rpc
AddOutputFilterByType DEFLATE image/x-icon text/javascript
DeflateFilterNote Ratio ratio
</IfModule>
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,30 @@
# {{ ansible_managed }}
<IfModule mod_evasive20.c>
{% for key, value in apache_mod_evasive_settings | dictsort %}
{{ key }} {{ value }}
{% endfor %}
#DOSEmailNotify you@yourdomain.com
#DOSSystemCommand "su - someuser -c '/sbin/... %s ...'"
#DOSLogDir "/var/log/mod_evasive"
DOSWhitelist 10.*.*.*
DOSWhitelist 192.168.*.*
DOSWhitelist 63.254.74.*
DOSWhitelist 8.28.239.*
{% for ip in firewall_monitoring_ips|default([]) if ip|ipv4('public') %}
DOSWhitelist {{ ip }}
{% endfor %}
{% for ip in firewall_whitelist_office_ip|default([]) %}
DOSWhitelist {{ ip | regex_replace('[0-9]+/[0-9]+', '*') }}
{% endfor %}
{% for ip in apache_mod_evasive_whitelist|default([]) %}
DOSWhitelist {{ ip | regex_replace('[0-9]+/[0-9]+', '*') }}
{% endfor %}
</IfModule>

View File

@@ -0,0 +1,17 @@
# {{ ansible_managed }}
<IfModule http2_module>
{% if apache_http2_enabled %}
Protocols h2 h2c http/1.1
{% else %}
Protocols http/1.1 # http/2 disabled
{% endif %}
H2Push on
H2PushPriority * after
H2PushPriority text/css before
H2PushPriority image/jpeg after 32
H2PushPriority image/png after 32
H2PushPriority application/javascript interleaved
</IfModule>

View File

@@ -0,0 +1,91 @@
# {{ ansible_managed }}
<IfModule mod_ssl.c>
# Pseudo Random Number Generator (PRNG):
# Configure one or more sources to seed the PRNG of the SSL library.
# The seed data should be of good random quality.
# WARNING! On some platforms /dev/random blocks if not enough entropy
# is available. This means you then cannot use the /dev/random device
# because it would lead to very long connection times (as long as
# it requires to make more entropy available). But usually those
# platforms additionally provide a /dev/urandom device which doesn't
# block. So, if available, use this one instead. Read the mod_ssl User
# Manual for more details.
#
SSLRandomSeed startup builtin
SSLRandomSeed startup file:/dev/urandom 512
SSLRandomSeed connect builtin
SSLRandomSeed connect file:/dev/urandom 512
##
## SSL Global Context
##
## All SSL configuration in this context applies both to
## the main server and all SSL-enabled virtual hosts.
##
#
# Some MIME-types for downloading Certificates and CRLs
#
AddType application/x-x509-ca-cert .crt
AddType application/x-pkcs7-crl .crl
# Pass Phrase Dialog:
# Configure the pass phrase gathering process.
# The filtering dialog program (`builtin' is a internal
# terminal dialog) has to provide the pass phrase on stdout.
SSLPassPhraseDialog exec:/usr/share/apache2/ask-for-passphrase
# Inter-Process Session Cache:
# Configure the SSL Session Cache: First the mechanism
# to use and second the expiring timeout (in seconds).
# (The mechanism dbm has known memory leaks and should not be used).
#SSLSessionCache dbm:${APACHE_RUN_DIR}/ssl_scache
SSLSessionCache shmcb:${APACHE_RUN_DIR}/ssl_scache(512000)
SSLSessionCacheTimeout 300
# Semaphore:
# Configure the path to the mutual exclusion semaphore the
# SSL engine uses internally for inter-process synchronization.
# (Disabled by default, the global Mutex directive consolidates by default
# this)
#Mutex file:${APACHE_LOCK_DIR}/ssl_mutex ssl-cache
# SSL Cipher Suite:
# List the ciphers that the client is permitted to negotiate. See the
# ciphers(1) man page from the openssl package for list of all available
# options.
# Enable only secure ciphers:
SSLCipherSuite "{{ apache_mod_ssl_ciphers | join(':') }}"
#SSLOpenSSLConfCmd DHParameters /etc/apache2/ssl/dhparams.pem
# SSL server cipher order preference:
# Use server priorities for cipher algorithm choice.
# Clients may prefer lower grade encryption. You should enable this
# option if you want to enforce stronger encryption, and can afford
# the CPU cost, and did not override SSLCipherSuite in a way that puts
# insecure ciphers first.
# Default: Off
SSLHonorCipherOrder on
# The protocols to enable.
# Available values: all, SSLv3, TLSv1, TLSv1.1, TLSv1.2
# SSL v2 is no longer supported
SSLProtocol {{ apache_mod_ssl_protocols }}
# Allow insecure renegotiation with clients which do not yet support the
# secure renegotiation protocol. Default: Off
#SSLInsecureRenegotiation on
# Whether to forbid non-SNI clients to access name based virtual hosts.
# Default: Off
#SSLStrictSNIVHostCheck On
SSLStaplingCache shmcb:${APACHE_RUN_DIR}/ssl_stcache(512000)
SSLUseStapling on
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,29 @@
# {{ ansible_managed }}
<IfModule mod_status.c>
# Allow server status reports generated by mod_status,
# with the URL of http://servername/server-status
<Location /server-status>
SetHandler server-status
Require ip 127.0.0.1 ::1 {{ apache_monitoring_ips }}
</Location>
# Keep track of extended status information for each request
ExtendedStatus On
# Determine if mod_status displays the first 63 characters of a request or
# the last 63, assuming the request itself is greater than 63 chars.
# Default: Off
#SeeRequestTail On
<IfModule mod_proxy.c>
# Show Proxy LoadBalancer status in mod_status
ProxyStatus On
</IfModule>
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,11 @@
# {{ ansible_managed }}
service {
name = "apache"
port = 443
check {
http = "https://localhost/server-status?auto"
interval = "30s"
tlsSkipVerify = true
}
}

View File

@@ -0,0 +1,26 @@
# {{ ansible_managed }}
{% if apache_firewall_public %}
iptables -N apache-in
{% if apache_firewall_public_isolated %}
{% for ip in apache_firewall_acl %}
iptables -A apache-in -s {{ ip }} -j ACCEPT
{% endfor %}
{% for ip in datacenter_global_networks + datacenter_public_networks %}
iptables -A apache-in -s {{ ip }} -j RETURN
{% endfor %}
{% for ip in apache_firewall_drop_dst %}
iptables -A apache-in -d {{ ip }} -j RETURN
{% endfor %}
{% endif %}
iptables -A apache-in -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -m comment --comment "apache-http" -j apache-in
iptables -A INPUT -p tcp --dport 443 -m comment --comment "apache-https" -j apache-in
{% else %}
iptables -A internal-in -p tcp --dport 80 -m comment --comment "apache-http" -j ACCEPT
iptables -A internal-in -p tcp --dport 443 -m comment --comment "apache-https" -j ACCEPT
{% endif %}
iptables -A monitoring-in -p tcp --dport 80 -m comment --comment "apache-http" -j ACCEPT
iptables -A monitoring-in -p tcp --dport 443 -m comment --comment "apache-https" -j ACCEPT

View File

@@ -0,0 +1,19 @@
# {{ ansible_managed }}
{% if apache_firewall_public %}
ip6tables -N apache-in
{% if apache_firewall_public_isolated %}
ip6tables -A apache-in -s fe80::/10 -j RETURN
ip6tables -A apache-in -s fc00::/7 -j RETURN
{% for ip in datacenter_public_ipv6_networks %}
ip6tables -A apache-in -s {{ ip }} -j RETURN
{% endfor %}
{% endif %}
ip6tables -A apache-in -j ACCEPT
ip6tables -A INPUT -p tcp --dport 80 -m comment --comment "apache-http" -j apache-in
ip6tables -A INPUT -p tcp --dport 443 -m comment --comment "apache-https" -j apache-in
{% else %}
ip6tables -A internal-in -p tcp --dport 80 -m comment --comment "apache-http" -j ACCEPT
ip6tables -A internal-in -p tcp --dport 443 -m comment --comment "apache-https" -j ACCEPT
{% endif %}

View File

@@ -0,0 +1,23 @@
/var/log/apache2/*.log {
dateext
dateformat .%Y%m%d
dateyesterday
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 640 root adm
sharedscripts
postrotate
if /etc/init.d/apache2 status > /dev/null ; then \
/etc/init.d/apache2 reload > /dev/null; \
fi;
endscript
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi; \
endscript
}

View File

@@ -0,0 +1,853 @@
# {{ ansible_managed }}
# ------------------------------------------------------------------------
# OWASP ModSecurity Core Rule Set ver.3.1.0
# Copyright (c) 2006-2018 Trustwave and contributors. All rights reserved.
#
# The OWASP ModSecurity Core Rule Set is distributed under
# Apache Software License (ASL) version 2
# Please see the enclosed LICENSE file for full details.
# ------------------------------------------------------------------------
#
# -- [[ Introduction ]] --------------------------------------------------------
#
# The OWASP ModSecurity Core Rule Set (CRS) is a set of generic attack
# detection rules that provide a base level of protection for any web
# application. They are written for the open source, cross-platform
# ModSecurity Web Application Firewall.
#
# See also:
# https://coreruleset.org/
# https://github.com/SpiderLabs/owasp-modsecurity-crs
# https://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project
#
#
# -- [[ System Requirements ]] -------------------------------------------------
#
# CRS requires ModSecurity version 2.8.0 or above.
# We recommend to always use the newest ModSecurity version.
#
# The configuration directives/settings in this file are used to control
# the OWASP ModSecurity CRS. These settings do **NOT** configure the main
# ModSecurity settings (modsecurity.conf) such as SecRuleEngine,
# SecRequestBodyAccess, SecAuditEngine, SecDebugLog, and XML processing.
#
# The CRS assumes that modsecurity.conf has been loaded. It is bundled with
# ModSecurity. If you don't have it, you can get it from:
# 2.x: https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v2/master/modsecurity.conf-recommended
# 3.x: https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended
#
# The order of file inclusion in your webserver configuration should always be:
# 1. modsecurity.conf
# 2. crs-setup.conf (this file)
# 3. rules/*.conf (the CRS rule files)
#
# Please refer to the INSTALL file for detailed installation instructions.
#
#
# -- [[ Mode of Operation: Anomaly Scoring vs. Self-Contained ]] ---------------
#
# The CRS can run in two modes:
#
# -- [[ Anomaly Scoring Mode (default) ]] --
# In CRS3, anomaly mode is the default and recommended mode, since it gives the
# most accurate log information and offers the most flexibility in setting your
# blocking policies. It is also called "collaborative detection mode".
# In this mode, each matching rule increases an 'anomaly score'.
# At the conclusion of the inbound rules, and again at the conclusion of the
# outbound rules, the anomaly score is checked, and the blocking evaluation
# rules apply a disruptive action, by default returning an error 403.
#
# -- [[ Self-Contained Mode ]] --
# In this mode, rules apply an action instantly. This was the CRS2 default.
# It can lower resource usage, at the cost of less flexibility in blocking policy
# and less informative audit logs (only the first detected threat is logged).
# Rules inherit the disruptive action that you specify (i.e. deny, drop, etc).
# The first rule that matches will execute this action. In most cases this will
# cause evaluation to stop after the first rule has matched, similar to how many
# IDSs function.
#
# -- [[ Alert Logging Control ]] --
# In the mode configuration, you must also adjust the desired logging options.
# There are three common options for dealing with logging. By default CRS enables
# logging to the webserver error log (or Event viewer) plus detailed logging to
# the ModSecurity audit log (configured under SecAuditLog in modsecurity.conf).
#
# - To log to both error log and ModSecurity audit log file, use: "log,auditlog"
# - To log *only* to the ModSecurity audit log file, use: "nolog,auditlog"
# - To log *only* to the error log file, use: "log,noauditlog"
#
# Examples for the various modes follow.
# You must leave one of the following options enabled.
# Note that you must specify the same line for phase:1 and phase:2.
#
# Default: Anomaly Scoring mode, log to error log, log to ModSecurity audit log
# - By default, offending requests are blocked with an error 403 response.
# - To change the disruptive action, see RESPONSE-999-EXCEPTIONS.conf.example
# and review section 'Changing the Disruptive Action for Anomaly Mode'.
# - In Apache, you can use ErrorDocument to show a friendly error page or
# perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html
#
SecDefaultAction "phase:1,log,auditlog,pass"
SecDefaultAction "phase:2,log,auditlog,pass"
# Example: Anomaly Scoring mode, log only to ModSecurity audit log
# - By default, offending requests are blocked with an error 403 response.
# - To change the disruptive action, see RESPONSE-999-EXCEPTIONS.conf.example
# and review section 'Changing the Disruptive Action for Anomaly Mode'.
# - In Apache, you can use ErrorDocument to show a friendly error page or
# perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html
#
# SecDefaultAction "phase:1,nolog,auditlog,pass"
# SecDefaultAction "phase:2,nolog,auditlog,pass"
# Example: Self-contained mode, return error 403 on blocking
# - In this configuration the default disruptive action becomes 'deny'. After a
# rule triggers, it will stop processing the request and return an error 403.
# - You can also use a different error status, such as 404, 406, et cetera.
# - In Apache, you can use ErrorDocument to show a friendly error page or
# perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html
#
# SecDefaultAction "phase:1,log,auditlog,deny,status:403"
# SecDefaultAction "phase:2,log,auditlog,deny,status:403"
# Example: Self-contained mode, redirect back to homepage on blocking
# - In this configuration the 'tag' action includes the Host header data in the
# log. This helps to identify which virtual host triggered the rule (if any).
# - Note that this might cause redirect loops in some situations; for example
# if a Cookie or User-Agent header is blocked, it will also be blocked when
# the client subsequently tries to access the homepage. You can also redirect
# to another custom URL.
# SecDefaultAction "phase:1,log,auditlog,redirect:'http://%{request_headers.host}/',tag:'Host: %{request_headers.host}'"
# SecDefaultAction "phase:2,log,auditlog,redirect:'http://%{request_headers.host}/',tag:'Host: %{request_headers.host}'"
#
# -- [[ Paranoia Level Initialization ]] ---------------------------------------
#
# The Paranoia Level (PL) setting allows you to choose the desired level
# of rule checks that will add to your anomaly scores.
#
# With each paranoia level increase, the CRS enables additional rules
# giving you a higher level of security. However, higher paranoia levels
# also increase the possibility of blocking some legitimate traffic due to
# false alarms (also named false positives or FPs). If you use higher
# paranoia levels, it is likely that you will need to add some exclusion
# rules for certain requests and applications receiving complex input.
#
# - A paranoia level of 1 is default. In this level, most core rules
# are enabled. PL1 is advised for beginners, installations
# covering many different sites and applications, and for setups
# with standard security requirements.
# At PL1 you should face FPs rarely. If you encounter FPs, please
# open an issue on the CRS GitHub site and don't forget to attach your
# complete Audit Log record for the request with the issue.
# - Paranoia level 2 includes many extra rules, for instance enabling
# many regexp-based SQL and XSS injection protections, and adding
# extra keywords checked for code injections. PL2 is advised
# for moderate to experienced users desiring more complete coverage
# and for installations with elevated security requirements.
# PL2 comes with some FPs which you need to handle.
# - Paranoia level 3 enables more rules and keyword lists, and tweaks
# limits on special characters used. PL3 is aimed at users experienced
# at the handling of FPs and at installations with a high security
# requirement.
# - Paranoia level 4 further restricts special characters.
# The highest level is advised for experienced users protecting
# installations with very high security requirements. Running PL4 will
# likely produce a very high number of FPs which have to be
# treated before the site can go productive.
#
# Rules in paranoia level 2 or higher will log their PL to the audit log;
# example: [tag "paranoia-level/2"]. This allows you to deduct from the
# audit log how the WAF behavior is affected by paranoia level.
#
# It is important to also look into the variable
# tx.enforce_bodyproc_urlencoded (Enforce Body Processor URLENCODED)
# defined below. Enabling it closes a possible bypass of CRS.
#
# Uncomment this rule to change the default:
#
#SecAction \
# "id:900000,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.paranoia_level=1"
# It is possible to execute rules from a higher paranoia level but not include
# them in the anomaly scoring. This allows you to take a well-tuned system on
# paranoia level 1 and add rules from paranoia level 2 without having to fear
# the new rules would lead to false positives that raise your score above the
# threshold.
# This optional feature is enabled by uncommenting the following rule and
# setting the tx.executing_paranoia_level.
# Technically, rules up to the level defined in tx.executing_paranoia_level
# will be executed, but only the rules up to tx.paranoia_level affect the
# anomaly scores.
# By default, tx.executing_paranoia_level is set to tx.paranoia_level.
# tx.executing_paranoia_level must not be lower than tx.paranoia_level.
#
# Please notice that setting tx.executing_paranoia_level to a higher paranoia
# level results in a performance impact that is equally high as setting
# tx.paranoia_level to said level.
#
#SecAction \
# "id:900001,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.executing_paranoia_level=1"
#
# -- [[ Enforce Body Processor URLENCODED ]] -----------------------------------
#
# ModSecurity selects the body processor based on the Content-Type request
# header. But clients are not always setting the Content-Type header for their
# request body payloads. This will leave ModSecurity with limited vision into
# the payload. The variable tx.enforce_bodyproc_urlencoded lets you force the
# URLENCODED body processor in these situations. This is off by default, as it
# implies a change of the behaviour of ModSecurity beyond CRS (the body
# processor applies to all rules, not only CRS) and because it may lead to
# false positives already on paranoia level 1. However, enabling this variable
# closes a possible bypass of CRS so it should be considered.
#
# Uncomment this rule to change the default:
#
#SecAction \
# "id:900010,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.enforce_bodyproc_urlencoded=1"
#
# -- [[ Anomaly Mode Severity Levels ]] ----------------------------------------
#
# Each rule in the CRS has an associated severity level.
# These are the default scoring points for each severity level.
# These settings will be used to increment the anomaly score if a rule matches.
# You may adjust these points to your liking, but this is usually not needed.
#
# - CRITICAL severity: Anomaly Score of 5.
# Mostly generated by the application attack rules (93x and 94x files).
# - ERROR severity: Anomaly Score of 4.
# Generated mostly from outbound leakage rules (95x files).
# - WARNING severity: Anomaly Score of 3.
# Generated mostly by malicious client rules (91x files).
# - NOTICE severity: Anomaly Score of 2.
# Generated mostly by the protocol rules (92x files).
#
# In anomaly mode, these scores are cumulative.
# So it's possible for a request to hit multiple rules.
#
# (Note: In this file, we use 'phase:1' to set CRS configuration variables.
# In general, 'phase:request' is used. However, we want to make absolutely sure
# that all configuration variables are set before the CRS rules are processed.)
#
#SecAction \
# "id:900100,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.critical_anomaly_score=5,\
# setvar:tx.error_anomaly_score=4,\
# setvar:tx.warning_anomaly_score=3,\
# setvar:tx.notice_anomaly_score=2"
#
# -- [[ Anomaly Mode Blocking Threshold Levels ]] ------------------------------
#
# Here, you can specify at which cumulative anomaly score an inbound request,
# or outbound response, gets blocked.
#
# Most detected inbound threats will give a critical score of 5.
# Smaller violations, like violations of protocol/standards, carry lower scores.
#
# [ At default value ]
# If you keep the blocking thresholds at the defaults, the CRS will work
# similarly to previous CRS versions: a single critical rule match will cause
# the request to be blocked and logged.
#
# [ Using higher values ]
# If you want to make the CRS less sensitive, you can increase the blocking
# thresholds, for instance to 7 (which would require multiple rule matches
# before blocking) or 10 (which would require at least two critical alerts - or
# a combination of many lesser alerts), or even higher. However, increasing the
# thresholds might cause some attacks to bypass the CRS rules or your policies.
#
# [ New deployment strategy: Starting high and decreasing ]
# It is a common practice to start a fresh CRS installation with elevated
# anomaly scoring thresholds (>100) and then lower the limits as your
# confidence in the setup grows. You may also look into the Sampling
# Percentage section below for a different strategy to ease into a new
# CRS installation.
#
# [ Anomaly Threshold / Paranoia Level Quadrant ]
#
# High Anomaly Limit | High Anomaly Limit
# Low Paranoia Level | High Paranoia Level
# -> Fresh Site | -> Experimental Site
# ------------------------------------------------------
# Low Anomaly Limit | Low Anomaly Limit
# Low Paranoia Level | High Paranoia Level
# -> Standard Site | -> High Security Site
#
# Uncomment this rule to change the defaults:
#
#SecAction \
# "id:900110,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.inbound_anomaly_score_threshold=5,\
# setvar:tx.outbound_anomaly_score_threshold=4"
#
# -- [[ Application Specific Rule Exclusions ]] ----------------------------------------
#
# Some well-known applications may undertake actions that appear to be
# malicious. This includes actions such as allowing HTML or Javascript within
# parameters. In such cases the CRS aims to prevent false positives by allowing
# administrators to enable prebuilt, application specific exclusions on an
# application by application basis.
# These application specific exclusions are distinct from the rules that would
# be placed in the REQUEST-900-EXCLUSION-RULES-BEFORE-CRS configuration file as
# they are prebuilt for specific applications. The 'REQUEST-900' file is
# designed for users to add their own custom exclusions. Note, using these
# application specific exclusions may loosen restrictions of the CRS,
# especially if used with an application they weren't designed for. As a result
# they should be applied with care.
# To use this functionality you must specify a supported application. To do so
# uncomment rule 900130. In addition to uncommenting the rule you will need to
# specify which application(s) you'd like to enable exclusions for. Only a
# (very) limited set of applications are currently supported, please use the
# filenames prefixed with 'REQUEST-903' to guide you in your selection.
# Such filenames use the following convention:
# REQUEST-903.9XXX-{APPNAME}-EXCLUSIONS-RULES.conf
#
# It is recommended if you run multiple web applications on your site to limit
# the effects of the exclusion to only the path where the excluded webapp
# resides using a rule similar to the following example:
# SecRule REQUEST_URI "@beginsWith /wordpress/" setvar:tx.crs_exclusions_wordpress=1
#
# Modify and uncomment this rule to select which application:
#
#SecAction \
# "id:900130,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.crs_exclusions_drupal=1,\
# setvar:tx.crs_exclusions_wordpress=1,\
# setvar:tx.crs_exclusions_nextcloud=1,\
# setvar:tx.crs_exclusions_dokuwiki=1,\
# setvar:tx.crs_exclusions_cpanel=1"
#
# -- [[ HTTP Policy Settings ]] ------------------------------------------------
#
# This section defines your policies for the HTTP protocol, such as:
# - allowed HTTP versions, HTTP methods, allowed request Content-Types
# - forbidden file extensions (e.g. .bak, .sql) and request headers (e.g. Proxy)
#
# These variables are used in the following rule files:
# - REQUEST-911-METHOD-ENFORCEMENT.conf
# - REQUEST-912-DOS-PROTECTION.conf
# - REQUEST-920-PROTOCOL-ENFORCEMENT.conf
# HTTP methods that a client is allowed to use.
# Default: GET HEAD POST OPTIONS
# Example: for RESTful APIs, add the following methods: PUT PATCH DELETE
# Example: for WebDAV, add the following methods: CHECKOUT COPY DELETE LOCK
# MERGE MKACTIVITY MKCOL MOVE PROPFIND PROPPATCH PUT UNLOCK
# Uncomment this rule to change the default.
#SecAction \
# "id:900200,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.allowed_methods=GET HEAD POST OPTIONS'"
# Content-Types that a client is allowed to send in a request.
# Default: application/x-www-form-urlencoded|multipart/form-data|text/xml|\
# application/xml|application/soap+xml|application/x-amf|application/json|\
# application/octet-stream|text/plain
# Uncomment this rule to change the default.
SecAction \
"id:900220,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:'tx.allowed_request_content_type=application/x-php-serialized-rpc|application/x-www-form-urlencoded|multipart/form-data|text/xml|application/xml|application/soap+xml|application/x-amf|application/json|application/octet-stream|text/plain'"
# Content-Types charsets that a client is allowed to send in a request.
# Default: utf-8|iso-8859-1|iso-8859-15|windows-1252
# Uncomment this rule to change the default.
# Use "|" to separate multiple charsets like in the rule defining
# tx.allowed_request_content_type.
#SecAction \
# "id:900270,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.allowed_request_content_type_charset=utf-8|iso-8859-1|iso-8859-15|windows-1252'"
# Allowed HTTP versions.
# Default: HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0
# Example for legacy clients: HTTP/0.9 HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0
# Note that some web server versions use 'HTTP/2', some 'HTTP/2.0', so
# we include both version strings by default.
# Uncomment this rule to change the default.
#SecAction \
# "id:900230,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.allowed_http_versions=HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0'"
# Forbidden file extensions.
# Guards against unintended exposure of development/configuration files.
# Default: .asa/ .asax/ .ascx/ .axd/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .resources/ .resx/ .sql/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/
# Example: .bak/ .config/ .conf/ .db/ .ini/ .log/ .old/ .pass/ .pdb/ .sql/
# Uncomment this rule to change the default.
#SecAction \
# "id:900240,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.restricted_extensions=.asa/ .asax/ .ascx/ .axd/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .resources/ .resx/ .sql/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/'"
# Forbidden request headers.
# Header names should be lowercase, enclosed by /slashes/ as delimiters.
# Blocking Proxy header prevents 'httpoxy' vulnerability: https://httpoxy.org
# Default: /proxy/ /lock-token/ /content-range/ /translate/ /if/
# Uncomment this rule to change the default.
#SecAction \
# "id:900250,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.restricted_headers=/proxy/ /lock-token/ /content-range/ /translate/ /if/'"
# File extensions considered static files.
# Extensions include the dot, lowercase, enclosed by /slashes/ as delimiters.
# Used in DoS protection rule. See section "Anti-Automation / DoS Protection".
# Default: /.jpg/ /.jpeg/ /.png/ /.gif/ /.js/ /.css/ /.ico/ /.svg/ /.webp/
# Uncomment this rule to change the default.
#SecAction \
# "id:900260,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.static_extensions=/.jpg/ /.jpeg/ /.png/ /.gif/ /.js/ /.css/ /.ico/ /.svg/ /.webp/'"
#
# -- [[ HTTP Argument/Upload Limits ]] -----------------------------------------
#
# Here you can define optional limits on HTTP get/post parameters and uploads.
# This can help to prevent application specific DoS attacks.
#
# These values are checked in REQUEST-920-PROTOCOL-ENFORCEMENT.conf.
# Beware of blocking legitimate traffic when enabling these limits.
#
# Block request if number of arguments is too high
# Default: unlimited
# Example: 255
# Uncomment this rule to set a limit.
#SecAction \
# "id:900300,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.max_num_args=255"
# Block request if the length of any argument name is too high
# Default: unlimited
# Example: 100
# Uncomment this rule to set a limit.
#SecAction \
# "id:900310,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.arg_name_length=100"
# Block request if the length of any argument value is too high
# Default: unlimited
# Example: 400
# Uncomment this rule to set a limit.
#SecAction \
# "id:900320,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.arg_length=400"
# Block request if the total length of all combined arguments is too high
# Default: unlimited
# Example: 64000
# Uncomment this rule to set a limit.
#SecAction \
# "id:900330,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.total_arg_length=64000"
# Block request if the file size of any individual uploaded file is too high
# Default: unlimited
# Example: 1048576
# Uncomment this rule to set a limit.
#SecAction \
# "id:900340,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.max_file_size=1048576"
# Block request if the total size of all combined uploaded files is too high
# Default: unlimited
# Example: 1048576
# Uncomment this rule to set a limit.
#SecAction \
# "id:900350,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.combined_file_sizes=1048576"
#
# -- [[ Easing In / Sampling Percentage ]] -------------------------------------
#
# Adding the Core Rule Set to an existing productive site can lead to false
# positives, unexpected performance issues and other undesired side effects.
#
# It can be beneficial to test the water first by enabling the CRS for a
# limited number of requests only and then, when you have solved the issues (if
# any) and you have confidence in the setup, to raise the ratio of requests
# being sent into the ruleset.
#
# Adjust the percentage of requests that are funnelled into the Core Rules by
# setting TX.sampling_percentage below. The default is 100, meaning that every
# request gets checked by the CRS. The selection of requests, which are going
# to be checked, is based on a pseudo random number generated by ModSecurity.
#
# If a request is allowed to pass without being checked by the CRS, there is no
# entry in the audit log (for performance reasons), but an error log entry is
# written. If you want to disable the error log entry, then issue the
# following directive somewhere after the inclusion of the CRS
# (E.g., RESPONSE-999-EXCEPTIONS.conf).
#
# SecRuleUpdateActionById 901150 "nolog"
#
# ATTENTION: If this TX.sampling_percentage is below 100, then some of the
# requests will bypass the Core Rules completely and you lose the ability to
# protect your service with ModSecurity.
#
# Uncomment this rule to enable this feature:
#
#SecAction "id:900400,\
# phase:1,\
# pass,\
# nolog,\
# setvar:tx.sampling_percentage=100"
#
# -- [[ Project Honey Pot HTTP Blacklist ]] ------------------------------------
#
# Optionally, you can check the client IP address against the Project Honey Pot
# HTTPBL (dnsbl.httpbl.org). In order to use this, you need to register to get a
# free API key. Set it here with SecHttpBlKey.
#
# Project Honeypot returns multiple different malicious IP types.
# You may specify which you want to block by enabling or disabling them below.
#
# Ref: https://www.projecthoneypot.org/httpbl.php
# Ref: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#wiki-SecHttpBlKey
#
# Uncomment these rules to use this feature:
#
#SecHttpBlKey XXXXXXXXXXXXXXXXX
#SecAction "id:900500,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.block_search_ip=1,\
# setvar:tx.block_suspicious_ip=1,\
# setvar:tx.block_harvester_ip=1,\
# setvar:tx.block_spammer_ip=1"
#
# -- [[ GeoIP Database ]] ------------------------------------------------------
#
# There are some rulesets that inspect geolocation data of the client IP address
# (geoLookup). The CRS uses geoLookup to implement optional country blocking.
#
# To use geolocation, we make use of the MaxMind GeoIP database.
# This database is not included with the CRS and must be downloaded.
# You should also update the database regularly, for instance every month.
# The CRS contains a tool to download it to util/geo-location/GeoIP.dat:
# util/upgrade.py --geoip
#
# This product includes GeoLite data created by MaxMind, available from:
# http://www.maxmind.com.
#
# Ref: http://blog.spiderlabs.com/2010/10/detecting-malice-with-modsecurity-geolocation-data.html
# Ref: http://blog.spiderlabs.com/2010/11/detecting-malice-with-modsecurity-ip-forensics.html
#
# Uncomment this rule to use this feature:
#
SecGeoLookupDB /usr/share/GeoIP/GeoIPCity.dat
#
# -=[ Block Countries ]=-
#
# Rules in the IP Reputation file can check the client against a list of high
# risk country codes. These countries have to be defined in the variable
# tx.high_risk_country_codes via their ISO 3166 two-letter country code:
# https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements
#
# If you are sure that you are not getting any legitimate requests from a given
# country, then you can disable all access from that country via this variable.
# The rule performing the test has the rule id 910100.
#
# This rule requires SecGeoLookupDB to be enabled and the GeoIP database to be
# downloaded (see the section "GeoIP Database" above.)
#
# By default, the list is empty. A list used by some sites was the following:
# setvar:'tx.high_risk_country_codes=UA ID YU LT EG RO BG TR RU PK MY CN'"
#
# Uncomment this rule to use this feature:
#
#SecAction \
# "id:900600,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.high_risk_country_codes='"
#
# -- [[ Anti-Automation / DoS Protection ]] ------------------------------------
#
# Optional DoS protection against clients making requests too quickly.
#
# When a client is making more than 100 requests (excluding static files) within
# 60 seconds, this is considered a 'burst'. After two bursts, the client is
# blocked for 600 seconds.
#
# Requests to static files are not counted towards DoS; they are listed in the
# 'tx.static_extensions' setting, which you can change in this file (see
# section "HTTP Policy Settings").
#
# For a detailed description, see rule file REQUEST-912-DOS-PROTECTION.conf.
#
# Uncomment this rule to use this feature:
#
#SecAction \
# "id:900700,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:'tx.dos_burst_time_slice=60',\
# setvar:'tx.dos_counter_threshold=100',\
# setvar:'tx.dos_block_timeout=600'"
#
# -- [[ Check UTF-8 encoding ]] ------------------------------------------------
#
# The CRS can optionally check request contents for invalid UTF-8 encoding.
# We only want to apply this check if UTF-8 encoding is actually used by the
# site; otherwise it will result in false positives.
#
# Uncomment this rule to use this feature:
#
#SecAction \
# "id:900950,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.crs_validate_utf8_encoding=1"
#
# -- [[ Blocking Based on IP Reputation ]] ------------------------------------
#
# Blocking based on reputation is permanent in the CRS. Unlike other rules,
# which look at the indvidual request, the blocking of IPs is based on
# a persistent record in the IP collection, which remains active for a
# certain amount of time.
#
# There are two ways an individual client can become flagged for blocking:
# - External information (RBL, GeoIP, etc.)
# - Internal information (Core Rules)
#
# The record in the IP collection carries a flag, which tags requests from
# individual clients with a flag named IP.reput_block_flag.
# But the flag alone is not enough to have a client blocked. There is also
# a global switch named tx.do_reput_block. This is off by default. If you set
# it to 1 (=On), requests from clients with the IP.reput_block_flag will
# be blocked for a certain duration.
#
# Variables
# ip.reput_block_flag Blocking flag for the IP collection record
# ip.reput_block_reason Reason (= rule message) that caused to blocking flag
# tx.do_reput_block Switch deciding if we really block based on flag
# tx.reput_block_duration Setting to define the duration of a block
#
# It may be important to know, that all the other core rules are skipped for
# requests, when it is clear that they carry the blocking flag in question.
#
# Uncomment this rule to use this feature:
#
#SecAction \
# "id:900960,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.do_reput_block=1"
#
# Uncomment this rule to change the blocking time:
# Default: 300 (5 minutes)
#
#SecAction \
# "id:900970,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# setvar:tx.reput_block_duration=300"
#
# -- [[ Collection timeout ]] --------------------------------------------------
#
# Set the SecCollectionTimeout directive from the ModSecurity default (1 hour)
# to a lower setting which is appropriate to most sites.
# This increases performance by cleaning out stale collection (block) entries.
#
# This value should be greater than or equal to:
# tx.reput_block_duration (see section "Blocking Based on IP Reputation") and
# tx.dos_block_timeout (see section "Anti-Automation / DoS Protection").
#
# Ref: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#wiki-SecCollectionTimeout
# Please keep this directive uncommented.
# Default: 600 (10 minutes)
SecCollectionTimeout 600
#
# -- [[ Debug Mode ]] ----------------------------------------------------------
#
# To enable rule development and debugging, CRS has an optional debug mode
# that does not block a request, but instead sends detection information
# back to the HTTP client.
#
# This functionality is currently only supported with the Apache web server.
# The Apache mod_headers module is required.
#
# In debug mode, the webserver inserts "X-WAF-Events" / "X-WAF-Score"
# response headers whenever a debug client makes a request. Example:
#
# # curl -v 'http://192.168.1.100/?foo=../etc/passwd'
# X-WAF-Events: TX:930110-OWASP_CRS/WEB_ATTACK/DIR_TRAVERSAL-REQUEST_URI,
# TX:930120-OWASP_CRS/WEB_ATTACK/FILE_INJECTION-ARGS:foo,
# TX:932160-OWASP_CRS/WEB_ATTACK/RCE-ARGS:foo
# X-WAF-Score: Total=15; sqli=0; xss=0; rfi=0; lfi=10; rce=5; php=0; http=0; ses=0
#
# To enable debug mode, include the RESPONSE-981-DEBUG.conf file.
# This file resides in a separate folder, as it is not compatible with
# nginx and IIS.
#
# You must specify the source IP address/network where you will be running the
# tests from. The source IP will BYPASS all CRS blocking, and will be sent the
# response headers as specified above. Be careful to only list your private
# IP addresses/networks here.
#
# Tip: for regression testing of CRS or your own ModSecurity rules, you may
# be interested in using the OWASP CRS regression testing suite instead.
# View the file util/regression-tests/README for more information.
#
# Uncomment these rules, filling in your CRS path and the source IP address,
# to enable debug mode:
#
#Include /usr/share/modsecurity-crs/util/debug/RESPONSE-981-DEBUG.conf
#SecRule REMOTE_ADDR "@ipMatch 192.168.1.100" \
# "id:900980,\
# phase:1,\
# nolog,\
# pass,\
# t:none,\
# ctl:ruleEngine=DetectionOnly,\
# setvar:tx.crs_debug_mode=1"
#
# -- [[ End of setup ]] --------------------------------------------------------
#
# The CRS checks the tx.crs_setup_version variable to ensure that the setup
# has been loaded. If you are not planning to use this setup template,
# you must manually set the tx.crs_setup_version variable before including
# the CRS rules/* files.
#
# The variable is a numerical representation of the CRS version number.
# E.g., v3.0.0 is represented as 300.
#
SecAction \
"id:900990,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.crs_setup_version=310"
# -- [[ Customization ]] -------------------------------------------------------
# triggers on user.profile for google login urls
SecRuleRemoveById 930120

View File

@@ -0,0 +1,230 @@
# {{ ansible_managed }}
# -- Rule engine initialization ----------------------------------------------
# Enable ModSecurity, attaching it to every transaction. Use detection
# only to start with, because that minimises the chances of post-installation
# disruption.
#
SecRuleEngine {{ 'On' if apache_mod_security_enabled else 'DetectionOnly' }}
# -- Request body handling ---------------------------------------------------
# Allow ModSecurity to access request bodies. If you don't, ModSecurity
# won't be able to see any POST parameters, which opens a large security
# hole for attackers to exploit.
#
SecRequestBodyAccess On
# Enable XML request body parser.
# Initiate XML Processor in case of xml content-type
#
SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\+|/)|text/)xml" \
"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
# Enable JSON request body parser.
# Initiate JSON Processor in case of JSON content-type; change accordingly
# if your application does not use 'application/json'
#
SecRule REQUEST_HEADERS:Content-Type "application/json" \
"id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
# Maximum request body size we will accept for buffering. If you support
# file uploads then the value given on the first line has to be as large
# as the largest file you are willing to accept. The second value refers
# to the size of data, with files excluded. You want to keep that value as
# low as practical.
#
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
# Store up to 128 KB of request body data in memory. When the multipart
# parser reachers this limit, it will start using your hard disk for
# storage. That is slow, but unavoidable.
#
SecRequestBodyInMemoryLimit 131072
# What do do if the request body size is above our configured limit.
# Keep in mind that this setting will automatically be set to ProcessPartial
# when SecRuleEngine is set to DetectionOnly mode in order to minimize
# disruptions when initially deploying ModSecurity.
#
SecRequestBodyLimitAction Reject
# Verify that we've correctly processed the request body.
# As a rule of thumb, when failing to process a request body
# you should reject the request (when deployed in blocking mode)
# or log a high-severity alert (when deployed in detection-only mode).
#
SecRule REQBODY_ERROR "!@eq 0" \
"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
# By default be strict with what we accept in the multipart/form-data
# request body. If the rule below proves to be too strict for your
# environment consider changing it to detection-only. You are encouraged
# _not_ to remove it altogether.
#
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
"id:'200003',phase:2,t:none,log,deny,status:400, \
msg:'Multipart request body failed strict validation: \
PE %{REQBODY_PROCESSOR_ERROR}, \
BQ %{MULTIPART_BOUNDARY_QUOTED}, \
BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
DB %{MULTIPART_DATA_BEFORE}, \
DA %{MULTIPART_DATA_AFTER}, \
HF %{MULTIPART_HEADER_FOLDING}, \
LF %{MULTIPART_LF_LINE}, \
SM %{MULTIPART_MISSING_SEMICOLON}, \
IQ %{MULTIPART_INVALID_QUOTING}, \
IP %{MULTIPART_INVALID_PART}, \
IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
# Did we see anything that might be a boundary?
#
SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \
"id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"
# PCRE Tuning
# We want to avoid a potential RegEx DoS condition
#
SecPcreMatchLimit 500000
SecPcreMatchLimitRecursion 500000
# Some internal errors will set flags in TX and we will need to look for these.
# All of these are prefixed with "MSC_". The following flags currently exist:
#
# MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded.
#
SecRule TX:/^MSC_/ "!@streq 0" \
"id:'200005',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
# -- Response body handling --------------------------------------------------
# Allow ModSecurity to access response bodies.
# You should have this directive enabled in order to identify errors
# and data leakage issues.
#
# Do keep in mind that enabling this directive does increases both
# memory consumption and response latency.
#
SecResponseBodyAccess On
# Which response MIME types do you want to inspect? You should adjust the
# configuration below to catch documents but avoid static files
# (e.g., images and archives).
#
SecResponseBodyMimeType text/plain text/html text/xml
# Buffer response bodies of up to 512 KB in length.
SecResponseBodyLimit 524288
# What happens when we encounter a response body larger than the configured
# limit? By default, we process what we have and let the rest through.
# That's somewhat less secure, but does not break any legitimate pages.
#
SecResponseBodyLimitAction ProcessPartial
# -- Filesystem configuration ------------------------------------------------
# The location where ModSecurity stores temporary files (for example, when
# it needs to handle a file upload that is larger than the configured limit).
#
# This default setting is chosen due to all systems have /tmp available however,
# this is less than ideal. It is recommended that you specify a location that's private.
#
SecTmpDir /tmp/
# The location where ModSecurity will keep its persistent data. This default setting
# is chosen due to all systems have /tmp available however, it
# too should be updated to a place that other users can't access.
#
SecDataDir /tmp/
# -- File uploads handling configuration -------------------------------------
# The location where ModSecurity stores intercepted uploaded files. This
# location must be private to ModSecurity. You don't want other users on
# the server to access the files, do you?
#
#SecUploadDir /opt/modsecurity/var/upload/
# By default, only keep the files that were determined to be unusual
# in some way (by an external inspection script). For this to work you
# will also need at least one file inspection rule.
#
#SecUploadKeepFiles RelevantOnly
# Uploaded files are by default created with permissions that do not allow
# any other user to access them. You may need to relax that if you want to
# interface ModSecurity to an external program (e.g., an anti-virus).
#
#SecUploadFileMode 0600
# -- Debug log configuration -------------------------------------------------
# The default debug log configuration is to duplicate the error, warning
# and notice messages from the error log.
#
#SecDebugLog /opt/modsecurity/var/log/debug.log
#SecDebugLogLevel 3
# -- Audit log configuration -------------------------------------------------
# Log the transactions that are marked by a rule, as well as those that
# trigger a server error (determined by a 5xx or 4xx, excluding 404,
# level response status codes).
#
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
# Log everything we know about a transaction.
SecAuditLogParts ABDEFHIJZ
# Use a single file for logging. This is much easier to look at, but
# assumes that you will use the audit log only ocassionally.
#
SecAuditLogType Serial
SecAuditLogFormat JSON
SecAuditLog /var/log/apache2/modsec_audit.log
#SecAuditLog "|/usr/bin/socat -u - tcp:127.0.0.1:5172"
# Specify the path for concurrent audit logging.
#SecAuditLogStorageDir /opt/modsecurity/var/audit/
# -- Miscellaneous -----------------------------------------------------------
# Use the most commonly used application/x-www-form-urlencoded parameter
# separator. There's probably only one application somewhere that uses
# something else so don't expect to change this value.
#
SecArgumentSeparator &
# Settle on version 0 (zero) cookies, as that is what most applications
# use. Using an incorrect cookie version may open your installation to
# evasion attacks (against the rules that examine named cookies).
#
SecCookieFormat 0
# Specify your Unicode Code Point.
# This mapping is used by the t:urlDecodeUni transformation function
# to properly map encoded data to your language. Properly setting
# these directives helps to reduce false positives and negatives.
#
SecUnicodeMapFile unicode.mapping 20127
# Improve the quality of ModSecurity by sharing information about your
# current ModSecurity version and dependencies versions.
# The following information will be shared: ModSecurity version,
# Web Server version, APR version, PCRE version, Lua version, Libxml2
# version, Anonymous unique id for host.
SecStatusEngine On

View File

@@ -0,0 +1,4 @@
# {{ ansible_managed }}
[Service]
PrivateTmp=false

View File

@@ -0,0 +1,21 @@
---
apache_phpfpm_php: "{{ 'php7.4' if ansible_distribution_release == 'focal' else 'php7.4' }}"
apache_phpfpm_etc_dir: "{{ '/etc/php/7.4/fpm' if ansible_distribution_release == 'focal' else '/etc/php/7.4/fpm' }}"
apache_phpfpm_max_workers: 30
apache_phpfpm_timeout: 120
apache_phpfpm_php_settings:
short_open_tag: on
display_errors: off
apache_phpfpm_php_admin_settings:
log_errors: on
error_log: /var/log/php-fpm.$pool.log
memory_limit: 512M
open_basedir: /srv/www:/var/www:/opt:/usr/share:/var/lib/{{ apache_phpfpm_php }}:/var/lib/php:/dev:/tmp:/var/log/kc:/var/spool/asterisk
apache_phpfpm_xcache_size: 128M

View File

@@ -0,0 +1,5 @@
---
- name: Reload PHP-FPM
service: name={{ apache_phpfpm_php }}-fpm state=reloaded

View File

@@ -0,0 +1,4 @@
---
dependencies:
- apache

View File

@@ -0,0 +1,65 @@
---
- name: Install PHP packages
apt:
pkg:
- "{{ apache_phpfpm_php }}-fpm"
- php-apcu
# check_php-fpm nagios plugin dependencies:
- libany-moose-perl
- libjson-perl
- libjson-xs-perl
state: present
tags: packages
- name: Disable Apache modules
apache2_module: name="{{ item }}" state=absent force=yes
with_items:
- "{{ apache_phpfpm_php }}"
notify: Restart Apache
tags: configs
- name: Enable Apache modules
apache2_module: name="{{ item }}" state=present force=yes
with_items:
- proxy_fcgi
notify: Restart Apache
tags: configs
- name: Ensure mod-php is not installed
apt:
pkg:
- libapache2-mod-{{ apache_phpfpm_php }}
- "{{ apache_phpfpm_php }}-cgi"
state: absent
purge: yes
notify: Restart Apache
tags: packages
- name: Install Apache other configs
template: src="etc_apache2_conf-available_php-fpm.conf.j2" dest="/etc/apache2/conf-available/{{ apache_phpfpm_php }}-fpm.conf"
notify: Reload Apache
tags: configs
- name: Install PHP-FPM pool config
template: src=etc_php_fpm_pool.d_www.conf.j2 dest={{ apache_phpfpm_etc_dir }}/pool.d/www.conf
notify: Reload PHP-FPM
tags: configs
- name: Install the FGCI client script
template: src=usr_local_bin_fcgi-client dest=/usr/local/bin/fcgi-client mode=0755
- name: Enable PHP-FPM
file: dest=/etc/apache2/conf-enabled/{{ apache_phpfpm_php }}-fpm.conf src=../conf-available/{{ apache_phpfpm_php }}-fpm.conf state=link
notify: Reload Apache
tags: configs
- name: Ensure PHP-FPM is running
service: name={{ apache_phpfpm_php }}-fpm state=started enabled=yes
tags: configs
- name: Register the php-fpm service in Consul
template: dest=/etc/consul.d/service-php-fpm.hcl src=etc_consul.d_service-php-fpm.hcl.j2
when: apache_consul_service
notify: Reload consul
tags: configs

View File

@@ -0,0 +1,12 @@
# {{ ansible_managed }}
<Proxy "unix:/run/php/{{ apache_phpfpm_php }}-fpm.sock|fcgi://{{ apache_phpfpm_php }}-fpm">
ProxySet max={{ apache_phpfpm_max_workers // 2 - 1 }}
ProxySet timeout={{ apache_phpfpm_timeout }}
ProxySet retry=0
</Proxy>
<FilesMatch "\.php$">
SetEnvIf ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
SetHandler "proxy:fcgi://{{ apache_phpfpm_php }}-fpm"
</FilesMatch>

View File

@@ -0,0 +1,6 @@
# {{ ansible_managed }}
service {
name = "php-fpm"
port = 443
}

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
ARGS= \
--phpfpm.socket-directories=/run/php \
--phpfpm.status-path=/_fpm/status

View File

@@ -0,0 +1,2 @@
# {{ ansible_managed }}
command[check_php-fpm]={{ nagios_nrpe_tools_dir }}/plugins/check_php-fpm -s /run/php/{{ apache_phpfpm_php }}-fpm.sock -w active_workers:{{ (apache_phpfpm_max_workers * 80 / 100)|int }} -c active_workers:{{ (apache_phpfpm_max_workers * 90 / 100)|int }}

View File

@@ -0,0 +1,85 @@
; {{ ansible_managed }}
[www]
;prefix = /path/to/pools/$pool
user = www-data
group = www-data
listen = /run/php/{{ apache_phpfpm_php }}-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
;listen.allowed_clients = 127.0.0.1
; process.priority = -19
pm = dynamic
pm.max_children = {{ apache_phpfpm_max_workers }}
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 7
;pm.process_idle_timeout = 10s
pm.max_requests = {{ apache_phpfpm_max_requests | default(50000) }}
pm.status_path = /_fpm/status
ping.path = /_fpm/ping
ping.response = pong
;access.log = /var/log/{{ apache_phpfpm_php }}-fpm.$pool.access.log
;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
;slowlog = /var/log/{{ apache_phpfpm_php }}-fpm.$pool.slow.log
;request_slowlog_timeout = 10s
;request_terminate_timeout = 0
;rlimit_files = 1024
;rlimit_core = 0
;chroot =
chdir = /
;catch_workers_output = yes
;clear_env = no
;security.limit_extensions = .php .php3 .php4 .php5 .php7
;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp
; Additional php.ini defines, specific to this pool of workers. These settings
; overwrite the values previously defined in the php.ini. The directives are the
; same as the PHP SAPI:
; php_value/php_flag - you can set classic ini defines which can
; be overwritten from PHP call 'ini_set'.
; php_admin_value/php_admin_flag - these directives won't be overwritten by
; PHP call 'ini_set'
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
; Defining 'extension' will load the corresponding shared extension from
; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
; overwrite previously defined php.ini values, but will append the new value
; instead.
; Note: path INI options can be relative and will be expanded with the prefix
; (pool, global or /usr)
{% for key, value in apache_phpfpm_php_admin_settings|dictsort %}
{% if value in (True,False) %}
php_admin_flag[{{ key }}] = {{ 'on' if value else 'off' }}
{% else %}
php_admin_value[{{ key }}] = {{ value }}
{% endif %}
{% endfor %}
{% for key, value in apache_phpfpm_php_settings|dictsort %}
{% if value in (True,False) %}
php_flag[{{ key }}] = {{ 'on' if value else 'off' }}
{% else %}
php_value[{{ key }}] = {{ value }}
{% endif %}
{% endfor %}

View File

@@ -0,0 +1,88 @@
; {{ ansible_managed }}
; configuration for php Xcache module
[xcache-common]
;; non-Windows example:
extension = xcache.so
;; Windows example:
; extension = php_xcache.dll
[xcache.admin]
xcache.admin.enable_auth = On
; Configure this to use admin pages
; xcache.admin.user = "mOo"
; xcache.admin.pass = md5($your_password)
; xcache.admin.pass = ""
xcache.admin.user = "admin"
xcache.admin.pass = "726be9b7e6dea1ed28c70800d68be36c"
[xcache]
; ini only settings, all the values here is default unless explained
; select low level shm implemenation
xcache.shm_scheme = "mmap"
; to disable: xcache.size=0
; to enable : xcache.size=64M etc (any size > 0) and your system mmap allows
xcache.size = {{ apache_phpfpm_xcache_size }}
; set to cpu count (cat /proc/cpuinfo |grep -c processor)
xcache.count = 2
; just a hash hints, you can always store count(items) > slots
xcache.slots = 8K
; ttl of the cache item, 0=forever
xcache.ttl = 0
; interval of gc scanning expired items, 0=no scan, other values is in seconds
xcache.gc_interval = 0
; same as aboves but for variable cache
xcache.var_size = 64M
xcache.var_count = 1
xcache.var_slots = 8K
; default value for $ttl parameter of xcache_*() functions
xcache.var_ttl = 0
; hard limit ttl that cannot be exceed by xcache_*() functions. 0=unlimited
xcache.var_maxttl = 0
xcache.var_gc_interval = 300
; mode:0, const string specified by xcache.var_namespace
; mode:1, $_SERVER[xcache.var_namespace]
; mode:2, uid or gid (specified by xcache.var_namespace)
xcache.var_namespace_mode = 0
xcache.var_namespace = ""
; N/A for /dev/zero
xcache.readonly_protection = Off
; for *nix, xcache.mmap_path is a file path, not directory. (auto create/overwrite)
; Use something like "/tmp/xcache" instead of "/dev/*" if you want to turn on ReadonlyProtection
; different process group of php won't share the same /tmp/xcache
; for win32, xcache.mmap_path=anonymous map name, not file path
xcache.mmap_path = "/dev/zero"
; Useful when XCache crash. leave it blank(disabled) or "/tmp/phpcore/" (writable by php)
xcache.coredump_directory = ""
; Windows only. leave it as 0 (default) until you're told by XCache dev
xcache.coredump_type = 0
; disable cache after crash
xcache.disable_on_crash = Off
; enable experimental documented features for each release if available
xcache.experimental = Off
; per request settings. can ini_set, .htaccess etc
xcache.cacher = On
xcache.stat = On
xcache.optimizer = Off
[xcache.coverager]
; enabling this feature will impact performance
; enabled only if xcache.coverager == On && xcache.coveragedump_directory == "non-empty-value"
; per request settings. can ini_set, .htaccess etc
; enable coverage data collecting and xcache_coverager_start/stop/get/clean() functions
xcache.coverager = Off
xcache.coverager_autostart = On
; set in php ini file only
; make sure it's readable (open_basedir is checked) by coverage viewer script
xcache.coveragedump_directory = ""

View File

@@ -0,0 +1,46 @@
#!/usr/bin/perl
use strict;
use warnings;
use Pod::Usage;
use Getopt::Long;
use IO::Socket;
use IO::Socket::UNIX;
use lib '/usr/local/lib/nagios/plugins';
use FCGI::Client;
GetOptions(
'h|help' => \my $help,
) or pod2usage();
pod2usage() if $help;
pod2usage() if @ARGV < 2;
my ($fcgi_file, $uri, $query_string) = @ARGV;
my $sock = IO::Socket::UNIX->new(
Type => SOCK_STREAM(),
Peer => $fcgi_file
) or die $!;
my $client = FCGI::Client::Connection->new( sock => $sock );
my ( $stdout, $stderr ) = $client->request(
+{
REQUEST_METHOD => 'GET',
REQUEST_URI => $uri,
SCRIPT_FILENAME => "/a/b/c$uri",
SCRIPT_NAME => $uri,
QUERY_STRING => $query_string || '',
},
''
);
print STDERR $stderr if $stderr;
print $stdout;
__END__
=head1 NAME
fcgi-client -
=head1 SYNOPSIS
$ fcgi-client foo.fcgi URI [foo=bar&hoge=fuga]

View File

@@ -0,0 +1,19 @@
---
consul_version: 1.8.5
consul_url: https://releases.hashicorp.com/consul/{{ consul_version }}/consul_{{ consul_version }}_{{ ansible_system|lower }}_{{ ansible_userspace_architecture|replace('x86_64', 'amd64') }}.zip
consul_data_dir: /opt/consul
consul_config_dir: /etc/consul.d
consul_server: true
consul_bootstrap_expect: 2
consul_wan_peers: []
consul_encrypt_key: eRhnp22+c0bkV0wPolk6Mw==
consul_expose_apis: no
consul_client_addr: "{{ '0.0.0.0' if consul_expose_apis else '127.0.0.1' }}"
consul_stub_mode: no
consul_dns_forwarders: []
consul_firewall: yes

View File

@@ -0,0 +1,7 @@
---
- name: Restart consul
service: name=consul state=restarted
- name: Reload consul
service: name=consul state=reloaded

View File

@@ -0,0 +1,6 @@
---
dependencies:
- role: firewall
when: consul_firewall
- network

122
roles/consul/tasks/main.yml Normal file
View File

@@ -0,0 +1,122 @@
---
- name: Ensure the consul user exists
user:
name: consul
home: '{{ consul_data_dir }}'
system: yes
groups: ssl-cert
append: yes
shell: /bin/false
createhome: no
state: present
tags: packages
- name: Ensure the consul config dir exists
file:
dest: /etc/consul.d
owner: root
group: consul
mode: 0750
state: directory
tags: packages
- name: Ensure the consul data dir exists
file:
dest: /opt/consul
owner: consul
group: consul
mode: 0750
state: directory
tags: packages
- name: Remove old consul config
file:
dest: /etc/consul.d/00-base_config.json
state: absent
tags: configs
- name: Install consul config
template:
dest: /etc/consul.d/00-base_config.hcl
src: etc_consul.d_00-base_config.hcl.j2
#validate: 'consul validate %s'
mode: 0640
owner: root
group: consul
notify: Restart consul
tags:
- configs
- consul.conf
- name: Install consul service config
template:
dest: /etc/default/consul
src: etc_default_consul.j2
when: not consul_stub_mode
notify: Restart consul
tags: configs
- name: Install consul service
template:
dest: /etc/systemd/system/consul.service
src: etc_systemd_system_consul.service.j2
when: not consul_stub_mode
notify: Restart consul
tags: configs
- name: Enable the consul service
systemd:
name: consul
state: "{{ 'started' if not consul_stub_mode else 'stopped' }}"
enabled: "{{ not consul_stub_mode }}"
daemon_reload: yes
when: not consul_stub_mode
tags: configs
- name: Remove the master token if present
lineinfile:
dest: /root/.bashrc
regexp: '^export CONSUL_HTTP_TOKEN=.*'
state: absent
when: consul_acl_master_token is defined and consul_acl_master_token and not consul_stub_mode
tags: configs
- name: Install packages needed by consul-tag
apt:
pkg:
- python3
- python3-requests
state: present
when: not consul_stub_mode
tags: consul-tag
- name: Install consul-tag
template:
dest: /usr/local/bin/consul-tag
src: usr_local_bin_consul-tag.j2
mode: 0755
owner: root
group: root
when: not consul_stub_mode
tags: consul-tag
- name: Remove old firewall config
file: dest=/etc/firewall/rules-v4.d/28_consul.sh state=absent
when: consul_firewall and not consul_stub_mode
notify: Restart firewall
tags:
- configs
- firewall
- name: Install the consul firewall config
template:
dest: /etc/firewall/rules-v4.d/78_consul.sh
src: etc_firewall_rules-v4.d_78_consul.sh.j2
mode: 0600
when: consul_firewall
notify: Restart firewall
tags:
- configs
- firewall

View File

@@ -0,0 +1,83 @@
# {{ ansible_managed }}
# Logging
enable_syslog = true
log_level = "INFO"
disable_update_check = true
# Basics
data_dir = "{{ consul_data_dir }}"
datacenter = "{{ datacenter_id }}"
server = {{ 'false' if consul_server else 'true' }}
ui = true
# Network
{% if consul_bootstrap_expect > 0 %}
encrypt = "{{ consul_encrypt_key }}"
{% endif %}
client_addr = "{{ consul_client_addr }}"
bind_addr = "{{ network_private_ip }}"
advertise_addr = "{{ network_private_ip }}"
retry_join = [
{% for peer in consul_servers if peer != ansible_hostname and hostvars[peer].datacenter_id == datacenter_id %}
"{{ hostvars[peer].network_private_ip }}"{{ ',' if not loop.last else '' }}
{% endfor %}
]
{% if consul_server %}
{% if consul_bootstrap_expect > 0 %}
bootstrap_expect = {{ consul_bootstrap_expect }}
{% endif %}
rejoin_after_leave = true
retry_join_wan = [
{% for peer in consul_servers if hostvars[peer].datacenter_id != datacenter_id %}
"{{ hostvars[peer].network_private_ip }}"{{ ',' if not loop.last else '' }}
{% endfor %}
]
{% endif %}
# TLS
#ports {
# https = 8501
#}
#key_file = "/etc/letsencrypt/live/{{ ansible_hostname }}.maruntiel.net/privkey1.pem"
#cert_file = "/etc/letsencrypt/live/{{ ansible_hostname }}.maruntiel.net/fullchain1.pem"
#ca_file = "/etc/letsencrypt/live/{{ ansible_hostname }}.maruntiel.net/chain1.pem"
#verify_incoming = true
#verify_outgoing = true
#tls_min_version = "tls12"
# Features
enable_script_checks = true
disable_remote_exec = true
# ACLs
#{% if consul_acl_datacenter is defined and consul_acl_datacenter %}
#acl_datacenter = "{{ consul_acl_datacenter }}"
#acl_default_policy = "deny"
#acl_down_policy = "extend-cache"
#acl_agent_token = "{{ consul_acl_agent_token }}"
#acl_token = "{{ consul_acl_token }}"
#{% if datacenter_id != consul_acl_datacenter %}
#acl_replication_token = "{{ consul_acl_replication_token | default(consul_acl_master_token) }}"
#{% endif %}
#{% endif %}
# DNS
dns_config {
node_ttl = "60s"
service_ttl {
"*" = "15s"
}
}
# Metadata
node_meta {
architecture = "{{ ansible_userspace_architecture }}"
product_name = "{{ ansible_system_vendor|replace(' Inc.', '') }} {{ ansible_product_name }}"
virtualization_role = "{{ ansible_virtualization_role }}"
}
# Consul Stats
telemetry {
disable_hostname = true
}

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
{% if consul_ui_beta|default(False) %}
ui_config=enable
{% endif %}

View File

@@ -0,0 +1,25 @@
# {{ ansible_managed }}
{% if not consul_stub_mode %}
{% if consul_server %}
iptables -A internal-in -p tcp --dport 8300:8302 -m comment --comment "consul" -j ACCEPT
iptables -A internal-in -p udp --dport 8300:8302 -m comment --comment "consul" -j ACCEPT
{% else %}
{% for ip in datacenter_local_networks %}
iptables -A internal-in -s {{ ip }} -p tcp --dport 8300:8302 -m comment --comment "consul" -j ACCEPT
iptables -A internal-in -s {{ ip }} -p udp --dport 8300:8302 -m comment --comment "consul" -j ACCEPT
{% endfor %}
{% endif %}
{% if consul_expose_apis %}
iptables -A internal-in -p tcp --dport 8500:8501 -m comment --comment "consul-http" -j ACCEPT
iptables -A internal-in -p tcp --dport 8600 -m comment --comment "consul-dns" -j ACCEPT
iptables -A internal-in -p udp --dport 8600 -m comment --comment "consul-dns" -j ACCEPT
{% endif %}
iptables -A internal-out -p tcp --dport 8300:8302 -m comment --comment "consul" -j ACCEPT
iptables -A internal-out -p udp --dport 8300:8302 -m comment --comment "consul" -j ACCEPT
iptables -A internal-out -p tcp --dport 8500:8501 -m comment --comment "consul-http" -j ACCEPT
iptables -A internal-out -p tcp --dport 8600 -m comment --comment "consul-dns" -j ACCEPT
iptables -A internal-out -p udp --dport 8600 -m comment --comment "consul-dns" -j ACCEPT
{% endif %}

View File

@@ -0,0 +1,20 @@
# {{ ansible_managed }}
[Unit]
Description=Consul Agent
Requires=network-online.target
After=network-online.target
RequiresMountsFor={{ consul_data_dir }}
[Service]
EnvironmentFile=-/etc/default/consul
ExecStart=/usr/local/bin/consul agent $CONSUL_FLAGS -config-dir={{ consul_config_dir }} -config-dir={{ consul_data_dir }}
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGINT
StandardOutput=null
User=consul
Group=consul
Restart=on-failure
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,71 @@
#!/usr/bin/python3
# {{ ansible_managed }}
import os
import sys
import requests
CONSUL_API = 'http://localhost:8500'
def get_service(sess, service_id):
r = sess.get(CONSUL_API + '/v1/agent/services', timeout=2)
r.raise_for_status()
services = r.json()
for svc in services.values():
if svc['ID'] == service_id:
return svc
return None
def change_service_tags(service, tags_to_add, tags_to_remove):
with requests.Session() as sess:
sess.headers = {'X-Consul-Token': os.getenv('CONSUL_HTTP_TOKEN')}
svc = get_service(sess, service)
if svc:
new_tags = (set(svc.get('Tags', [])) | tags_to_add) - tags_to_remove
new_svc = {
'ID': svc['ID'],
'Name': svc['Service'],
'Address': svc.get('Address', ''),
'Port': svc.get('Port', 0),
'Meta': svc.get('Meta', {}),
'Tags': sorted(list(new_tags)),
'EnableTagOverride': svc.get('EnableTagOverride', False),
}
for k, v in new_svc.items():
print('{} = {}'.format(k, v))
r = sess.put(CONSUL_API + '/v1/agent/service/register', json=new_svc, timeout=2)
r.raise_for_status()
def main(argv):
if len(argv) < 3:
print("Usage: consul-tag service +tag -tag...")
return 1
service = argv[1]
tags_to_add = set()
tags_to_remove = set()
for tag in argv[2:]:
if tag.startswith('-'):
tags_to_remove.add(tag[1:])
elif tag.startswith('+'):
tags_to_add.add(tag[1:])
else:
tags_to_add.add(tag)
try:
change_service_tags(service, tags_to_add, tags_to_remove)
except Exception as exc:
print("Error: {}".format(exc))
return 2
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))

View File

@@ -0,0 +1,28 @@
---
firewall_enabled: yes
firewall_standard_rules: yes
firewall_log_prefix: "FW:"
firewall_whitelist_ip: []
firewall_whitelist_ipv6: []
firewall_late_whitelist_ip: []
firewall_late_whitelist_ipv6: []
firewall_input_default_drop: true
firewall_output_default_drop: true
firewall_output_whitelist_domains: []
firewall_output_whitelist_ipv4: []
firewall_output_whitelist_ipv6: []
firewall_output_learning: false
firewall_whitelist_office_ip: []
firewall_whitelist_office_ports: []
firewall_ssh_acl: []
firewall_ssh_acl_extra: []
firewall_influx_acl: []
firewall_influx_acl_extra: []
firewall_allow_internal_dns: true
firewall_custom_ipv4_rules: ""
firewall_custom_ipv6_rules: ""

View File

@@ -0,0 +1,4 @@
- name: Restart firewall
service:
name: firewall
state: restarted

View File

@@ -0,0 +1,122 @@
---
- name: Ensure iptables packages are installed
apt:
pkg:
- iptables
- ipset
- conntrack
- ipv6calc # Required by update-firewall-outbound
state: present
when: firewall_run is not defined
tags: packages
- name: Install the firewall init.d script
template:
dest: /etc/init.d/firewall
src: etc_init.d_firewall.j2
mode: 0755
owner: root
group: root
when: firewall_run is not defined and firewall_enabled
tags:
- configs
- firewall
- name: Enable the firewall init.d script
service:
name: firewall
enabled: yes
when: firewall_run is not defined and firewall_enabled
tags:
- configs
- firewall
- name: Ensure the rules directories exist
file:
path: "/etc/firewall/{{ item }}"
state: directory
owner: root
group: root
mode: 0700
with_items:
- rules-v4.d
- rules-v6.d
when: firewall_run is not defined
tags:
- configs
- firewall
- name: Install the firewall configs
template: dest=/etc/firewall/{{ item }} src={{ item }}.j2 mode=0600
with_items:
- rules-v4.d/10_conntrack.sh
- rules-v4.d/15_local.sh
- rules-v4.d/17_monitoring.sh
- rules-v4.d/18_internal.sh
- rules-v4.d/20_whitelist.sh
- rules-v4.d/22_ssh.sh
- rules-v4.d/24_influxdb.sh
- rules-v4.d/33_mariadb.sh
- rules-v4.d/85_whitelist.sh
- rules-v4.d/90_allow_outbound.sh
- rules-v4.d/90_drop_all.sh
- rules-v4.d/95_fail2ban.sh
- rules-v6.d/10_conntrack.sh
- rules-v6.d/15_local.sh
- rules-v6.d/18_internal.sh
- rules-v6.d/20_whitelist.sh
- rules-v4.d/24_influxdb.sh
- rules-v4.d/33_mariadb.sh
- rules-v4.d/85_whitelist.sh
- rules-v6.d/90_allow_outbound.sh
- rules-v6.d/90_drop_all.sh
when: firewall_run is not defined and firewall_enabled and firewall_standard_rules
notify: Restart firewall
tags:
- configs
- firewall
- name: Install the extra firewall configs
template: dest=/etc/firewall/{{ item }} src={{ item }}.j2 mode=0600
with_items:
- rules-v4.d/50_custom.sh
- rules-v6.d/50_custom.sh
when: firewall_run is not defined and firewall_enabled and (firewall_custom_ipv4_rules or firewall_custom_ipv6_rules)
notify: Restart firewall
tags:
- configs
- firewall
- name: Install the firewall outbound ACLs
template: dest=/etc/firewall/outbound_whitelist.acl src=etc_firewall_outbound_whitelist.acl.j2 mode=0600
when: firewall_run is not defined and firewall_enabled and firewall_output_whitelist_domains
notify: Restart firewall
tags:
- configs
- firewall
- whitelists
- name: Remove obsolete configs
file: dest=/etc/firewall/{{ item }} state=absent
with_items:
- rules-v4.d/19_monitoring.sh
when: firewall_run is not defined and firewall_enabled
notify: Restart firewall
tags:
- configs
- firewall
- name: Install the firewall outbound update script
template: dest=/usr/sbin/update-firewall-outbound src=usr_sbin_update-firewall-outbound.j2 mode=0700
when: firewall_run is not defined and firewall_enabled and firewall_output_whitelist_domains
notify: Restart firewall
tags:
- firewall
- scripts
- whitelists
- set_fact:
firewall_run: true
when: firewall_run is not defined

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
{% for name in firewall_output_whitelist_domains %}
{{ name }}
{% endfor %}

View File

@@ -0,0 +1,133 @@
#!/bin/sh
# {{ ansible_managed }}
### BEGIN INIT INFO
# Provides: firewall
# Required-Start: $network
# Required-Stop: $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Set up iptables rules
# Description: Loads current iptables rules from/to /etc/firewall
### END INIT INFO
. /lib/lsb/init-functions
PATH="/sbin:$PATH"
rc=0
flush_ipv4()
{
for chain in INPUT FORWARD OUTPUT; do
iptables -P $chain ACCEPT
done
for table in $(iptables-save | awk '/^\*/ { print substr($1,2) }'); do
iptables -t $table -F
iptables -t $table -X
iptables -t $table -Z
done
}
flush_ipv6()
{
for chain in INPUT FORWARD OUTPUT; do
ip6tables -P $chain ACCEPT
done
for table in $(ip6tables-save | awk '/^\*/ { print substr($1,2) }'); do
ip6tables -t $table -F
ip6tables -t $table -X
ip6tables -t $table -Z
done
}
load_rules()
{
log_action_begin_msg "Loading iptables rules"
# load IPv4 rules
if [ ! -d /etc/firewall/rules-v4.d ]; then
log_action_cont_msg " skipping IPv4 (no rules to load)"
else
log_action_cont_msg " IPv4"
flush_ipv4
for frag in /etc/firewall/rules-v4.d/*.sh; do
if [ -r "$frag" ]; then
. "$frag"
if [ $? -ne 0 ]; then
rc=1
fi
fi
done
fi
# load IPv6 rules
if [ ! -d /etc/firewall/rules-v6.d ]; then
log_action_cont_msg " skipping IPv6 (no rules to load)"
else
log_action_cont_msg " IPv6"
flush_ipv6
for frag in /etc/firewall/rules-v6.d/*.sh; do
if [ -r "$frag" ]; then
. "$frag"
if [ $? -ne 0 ]; then
rc=1
fi
fi
done
fi
log_action_end_msg $rc
}
flush_rules()
{
log_action_begin_msg "Flushing rules"
if [ ! -f /proc/net/ip_tables_names ]; then
log_action_cont_msg " skipping IPv4"
else
log_action_cont_msg " IPv4"
flush_ipv4
fi
if [ ! -f /proc/net/ip6_tables_names ]; then
log_action_cont_msg " skipping IPv6"
else
log_action_cont_msg " IPv6"
flush_ipv6
fi
log_action_end_msg 0
}
case "$1" in
start|restart|reload|force-reload)
load_rules
;;
stop)
echo "Automatic flushing disabled, use \"flush\" instead of \"stop\""
;;
flush)
flush_rules
;;
debug)
iptables() { echo "iptables $@"; }
ip6tables() { echo "ip6tables $@"; }
ipset() { echo "ipset $@"; }
log_action_begin_msg() { :; }
log_action_cont_msg() { :; }
log_action_end_msg() { :; }
load_rules
;;
*)
echo "Usage: $0 {start|restart|reload|force-reload|save|flush}" >&2
exit 1
;;
esac
exit $rc

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
# Allow established connections
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
# Allow all traffic from localhost
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

View File

@@ -0,0 +1,7 @@
# {{ ansible_managed }}
iptables -N monitoring-in
{% for srcip in firewall_monitoring_ips|default([]) %}
iptables -A INPUT -s {{ srcip }} -j monitoring-in
{% endfor %}

View File

@@ -0,0 +1,24 @@
# {{ ansible_managed }}
{% if datacenter_global_networks is defined %}
iptables -N internal-in
{% if firewall_allow_internal_dns %}
iptables -A internal-in -p tcp --dport 53 -m comment --comment "common-dns" -j ACCEPT
iptables -A internal-in -p udp --dport 53 -m comment --comment "common-dns" -j ACCEPT
{% endif %}
{% for srcip in datacenter_global_networks + datacenter_public_networks %}
iptables -A INPUT -s {{ srcip }} -j internal-in
{% endfor %}
iptables -N internal-out
iptables -A internal-out -p tcp -m multiport --dports 53,80,443,2181,3306:3310,8086,10231 -m comment --comment "common-services" -j ACCEPT
iptables -A internal-out -p udp --dport 53 -m comment --comment "common-dns" -j ACCEPT
iptables -A internal-out -p tcp --dport 10514 -m owner --uid-owner 0 -m comment --comment "syslog" -j ACCEPT
iptables -A internal-out -p icmp -j ACCEPT
{% for dstip in datacenter_global_networks + datacenter_public_networks %}
iptables -A OUTPUT -d {{ dstip }} -j internal-out
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,8 @@
# {{ ansible_managed }}
{% if firewall_whitelist_ip %}
# Whitelist IPs
{% for ip in firewall_whitelist_ip %}
iptables -A INPUT -s {{ ip }} -m comment --comment "whitelist" -j ACCEPT
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,8 @@
# {{ ansible_managed }}
# Allow SSH only from IPs:
iptables -N ssh-in
{% for ip in firewall_ssh_acl|default([]) + firewall_ssh_acl_extra|default([]) %}
iptables -A ssh-in -s {{ ip }} -j ACCEPT
{% endfor %}
iptables -A INPUT -p tcp --dport 22 -m comment --comment "ssh" -j ssh-in

View File

@@ -0,0 +1,8 @@
# {{ ansible_managed }}
# Allow InfluxDB Replication only from IPs:
iptables -N influx-in
{% for ip in firewall_influx_acl|default([]) + firewall_influx_acl_extra|default([]) %}
iptables -A influx-in -s {{ ip }} -j ACCEPT
{% endfor %}
iptables -A INPUT -p tcp --dport 8086 -m comment --comment "influx" -j influx-in

View File

@@ -0,0 +1,8 @@
# {{ ansible_managed }}
# Allow MariaDB Replication only from IPs:
iptables -N mariadb-in
{% for ip in firewall_mariadb_acl|default([]) + firewall_mariadb_acl_extra|default([]) %}
iptables -A mariadb-in -s {{ ip }} -j ACCEPT
{% endfor %}
iptables -A INPUT -p tcp --dport 3306 -m comment --comment "mariadb" -j mariadb-in

View File

@@ -0,0 +1,3 @@
# {{ ansible_managed }}
{{ firewall_custom_ipv4_rules }}

View File

@@ -0,0 +1,15 @@
# {{ ansible_managed }}
{% if firewall_late_whitelist_ip %}
# Whitelist IPs
{% for ip in firewall_late_whitelist_ip %}
iptables -A INPUT -s {{ ip }} -m comment --comment "whitelist" -j ACCEPT
{% endfor %}
{% endif %}
{% if firewall_whitelist_office_ip and firewall_whitelist_office_ports %}
# Offices TODO remove
{% for ip in firewall_whitelist_office_ip %}
iptables -A INPUT -s {{ ip }} -p tcp -m multiport --dports "{{ firewall_whitelist_office_ports | join(',') }}" -m comment --comment "office-whitelist" -j ACCEPT
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,69 @@
# {{ ansible_managed }}
{% if firewall_output_default_drop or firewall_output_whitelist_ipv4 %}
{% for ip in network_nameservers + ['8.8.8.8'] if ip|ipv4 %}
{% if loop.first %}
# Allow DNS
{% endif %}
iptables -A OUTPUT -d {{ ip }} -p tcp --dport 53 -m comment --comment "dns" -j ACCEPT
iptables -A OUTPUT -d {{ ip }} -p udp --dport 53 -m comment --comment "dns" -j ACCEPT
{% endfor %}
if getent group postfix >/dev/null 2>&1; then
# Permit outbound SMTP for Postfix only (TODO: move to postfix role)
iptables -A OUTPUT -p tcp --dport 25 -m owner --gid-owner postfix -m comment --comment "smtp" -j ACCEPT
fi
{% if not (firewall_output_learning or firewall_output_whitelist_ipv4) %}
# Permit outbound HTTP for user _apt
iptables -A OUTPUT -p tcp -m multiport --dports 80,443 -m owner --uid-owner _apt -m comment --comment "apt" -j ACCEPT
# Permit outbound SSH for normal users
iptables -A OUTPUT -p tcp --dport 22 -m owner --uid-owner 1000-65500 -m comment --comment "ssh" -j ACCEPT
# Allow all outbound traffic for the root user
iptables -A OUTPUT -m owner --uid-owner 0 -j ACCEPT
{% endif %}
{% for ip in datacenter_global_networks|default([]) + datacenter_all_networks|default([]) %}
iptables -A OUTPUT -d {{ ip }} -m comment --comment "keepcalling" -j ACCEPT
{% endfor %}
{% if firewall_output_whitelist_domains %}
# Outbound ACL for whitelist
if [ -r /etc/firewall/outbound_whitelist_ipv4.acl ]; then
ipset -exist create outbound-whitelist hash:net counters comment
ipset flush outbound-whitelist
grep -v '^#' /etc/firewall/outbound_whitelist_ipv4.acl | while read ip name; do
ipset -exist add outbound-whitelist "$ip" comment "$name"
done < /etc/firewall/outbound_whitelist_ipv4.acl
# iptables -A OUTPUT -m set --match-set outbound-whitelist,dst -j ACCEPT
fi
{% endif %}
{% if firewall_output_whitelist_ipv4 %}
# Outbound ACL for whitelist
iptables -N outbound-whitelist
{% for item in firewall_output_whitelist_ipv4 %}
iptables -A outbound-whitelist -d {{ item.ip }} -m comment --comment "{{ item.name }}" -j ACCEPT
{% endfor %}
iptables -A OUTPUT -j outbound-whitelist
{% endif %}
{% if firewall_output_learning %}
ipset -exist create outbound hash:ip counters
iptables -A OUTPUT -p tcp --syn -m set --match-set outbound dst -j ACCEPT
iptables -A OUTPUT -p udp -m set --match-set outbound dst -j ACCEPT
iptables -A OUTPUT -p tcp --syn -j SET --add-set outbound dst
iptables -A OUTPUT -p udp -j SET --add-set outbound dst
iptables -A internal-out -m limit --limit 10/min --limit-burst 2 -j LOG --log-prefix "{{ firewall_log_prefix }} internal-out DROP: " --log-level 5 --log-uid
{% endif %}
{% endif %}

View File

@@ -0,0 +1,28 @@
# {{ ansible_managed }}
{% if firewall_input_default_drop %}
# Allow Safe ICMP
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
#iptables -A INPUT -p icmp --icmp-type redirect -j ACCEPT
iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
# Drop everything else
iptables -A INPUT -m pkttype --pkt-type broadcast -j DROP
iptables -A INPUT -m limit --limit 10/min --limit-burst 2 -j LOG --log-prefix "{{ firewall_log_prefix }} INPUT DROP: " --log-level 5
iptables -A INPUT -j DROP
{% endif %}
{% if firewall_output_default_drop %}
# Allow Safe ICMP
iptables -A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT
iptables -A OUTPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
#iptables -A OUTPUT -p icmp --icmp-type redirect -j ACCEPT
iptables -A OUTPUT -p icmp --icmp-type time-exceeded -j ACCEPT
# Drop everything else
iptables -A OUTPUT -p tcp --syn -m limit --limit 10/min --limit-burst 2 -j LOG --log-prefix "{{ firewall_log_prefix }} OUTPUT DROP: " --log-level 5 --log-uid
iptables -A OUTPUT ! -p tcp -m limit --limit 10/min --limit-burst 2 -j LOG --log-prefix "{{ firewall_log_prefix }} OUTPUT DROP: " --log-level 5 --log-uid
iptables -A OUTPUT -j REJECT
{% endif %}

View File

@@ -0,0 +1,6 @@
# {{ ansible_managed }}
# Restart fail2ban
if systemctl -q is-active fail2ban.service; then
systemctl try-restart fail2ban.service
fi

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
# Allow established connections
ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
ip6tables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
# Allow all traffic from localhost
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A OUTPUT -o lo -j ACCEPT

View File

@@ -0,0 +1,21 @@
# {{ ansible_managed }}
{% if datacenter_global_networks is defined %}
ip6tables -N internal-in
ip6tables -A INPUT -s fe80::/10 -j internal-in
ip6tables -A INPUT -s fc00::/7 -j internal-in
{% for net in datacenter_public_ipv6_networks|default([]) %}
ip6tables -A INPUT -s {{ net }} -j internal-in
{% endfor %}
ip6tables -N internal-out
ip6tables -A internal-out -p tcp -m multiport --dports 53,80,443,3306:3310 -m comment --comment "common-services" -j ACCEPT
ip6tables -A internal-out -p udp -m multiport --dports 53,123 -m comment --comment "common-services" -j ACCEPT
ip6tables -A internal-out -p icmpv6 -j ACCEPT
ip6tables -A OUTPUT -d fe80::/10 -j internal-out
ip6tables -A OUTPUT -d fc00::/7 -j internal-out
{% for net in datacenter_public_ipv6_networks|default([]) %}
ip6tables -A OUTPUT -d {{ net }} -j internal-out
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,8 @@
# {{ ansible_managed }}
{% if firewall_whitelist_ipv6 %}
# Whitelist IPs
{% for ip in firewall_whitelist_ipv6 %}
ip6tables -A INPUT -s {{ ip }} -m comment --comment "whitelist" -j ACCEPT
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,3 @@
# {{ ansible_managed }}
{{ firewall_custom_ipv6_rules }}

View File

@@ -0,0 +1,8 @@
# {{ ansible_managed }}
{% if firewall_late_whitelist_ipv6 %}
# Whitelist IPs
{% for ip in firewall_late_whitelist_ipv6 %}
ip6tables -A INPUT -s {{ ip }} -m comment --comment "whitelist" -j ACCEPT
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,69 @@
# {{ ansible_managed }}
{% if firewall_output_default_drop or firewall_output_whitelist_ipv6 %}
{% for ip in network_nameservers if ip|ipv6 %}
{% if loop.first %}
# Allow DNS
{% endif %}
ip6tables -A OUTPUT -d {{ ip }} -p tcp --dport 53 -m comment --comment "dns" -j ACCEPT
ip6tables -A OUTPUT -d {{ ip }} -p udp --dport 53 -m comment --comment "dns" -j ACCEPT
{% endfor %}
if getent group postfix >/dev/null 2>&1; then
# Permit outbound SMTP for Postfix only (TODO: move to postfix role)
ip6tables -A OUTPUT -p tcp --dport 25 -m owner --gid-owner postfix -m comment --comment "smtp" -j ACCEPT
fi
{% if not firewall_output_learning %}
# Permit outbound HTTP for user _apt
ip6tables -A OUTPUT -p tcp -m multiport --dports 80,443 -m owner --uid-owner _apt -m comment --comment "apt" -j ACCEPT
# Permit outbound SSH for normal users
ip6tables -A OUTPUT -p tcp --dport 22 -m owner --uid-owner 1000-65500 -m comment --comment "ssh" -j ACCEPT
# Allow all outbound traffic for the root user
ip6tables -A OUTPUT -m owner --uid-owner 0 -j ACCEPT
{% endif %}
{% for ip in datacenter_all_ipv6_networks|default([]) %}
ip6tables -A OUTPUT -d {{ ip }} -m comment --comment "keepcalling" -j ACCEPT
{% endfor %}
{% if firewall_output_whitelist_domains %}
# Outbound ACL for whitelist
if [ -r /etc/firewall/outbound_whitelist_ipv6.acl ]; then
ipset -exist create outbound-whitelist hash:net family inet6 counters comment
ipset flush outbound-whitelist
grep -v '^#' /etc/firewall/outbound_whitelist_ipv6.acl | while read ip name; do
ipset -exist add outbound-whitelist "$ip" comment "$name"
done < /etc/firewall/outbound_whitelist_ipv6.acl
# ip6tables -A OUTPUT -m set --match-set outbound-whitelist,dst -j ACCEPT
fi
{% endif %}
{% if firewall_output_whitelist_ipv6 %}
# Outbound ACL for whitelist
ip6tables -N outbound-whitelist
{% for item in firewall_output_whitelist_ipv6 %}
ip6tables -A OUTPUT -d {{ item.ip }} -m comment --comment "{{ item.name }}" -j ACCEPT
{% endfor %}
ip6tables -A OUTPUT -j outbound-whitelist
{% endif %}
{% if firewall_output_learning %}
ipset -exist create outbound-ipv6 hash:ip family inet6 netmask 64 counters
ip6tables -A OUTPUT -p tcp --syn -m set --match-set outbound-ipv6 dst -j ACCEPT
ip6tables -A OUTPUT -p udp -m set --match-set outbound dst -j ACCEPT
ip6tables -A OUTPUT -p tcp --syn -j SET --add-set outbound-ipv6 dst
ip6tables -A OUTPUT -p udp -j SET --add-set outbound-ipv6 dst
ip6tables -A internal-out -m limit --limit 10/min --limit-burst 2 -j LOG --log-prefix "{{ firewall_log_prefix }} internal-out DROP: " --log-level 5 --log-uid
{% endif %}
{% endif %}

View File

@@ -0,0 +1,21 @@
# {{ ansible_managed }}
{% if firewall_input_default_drop %}
# Allow ICMP
ip6tables -A INPUT -p icmpv6 -j ACCEPT
# Drop everything else
ip6tables -A INPUT -m pkttype --pkt-type broadcast -j DROP
ip6tables -A INPUT -m limit --limit 10/min --limit-burst 2 -j LOG --log-prefix "{{ firewall_log_prefix }} INPUT DROP: " --log-level 5
ip6tables -A INPUT -j DROP
{% endif %}
{% if firewall_output_default_drop %}
# Allow ICMP
ip6tables -A OUTPUT -p icmpv6 ! --icmpv6-type echo-request -j ACCEPT
# Drop everything else
ip6tables -A OUTPUT -p tcp --syn -m limit --limit 10/min --limit-burst 2 -j LOG --log-prefix "{{ firewall_log_prefix }} OUTPUT DROP: " --log-level 5 --log-uid
ip6tables -A OUTPUT ! -p tcp -m limit --limit 10/min --limit-burst 2 -j LOG --log-prefix "{{ firewall_log_prefix }} OUTPUT DROP: " --log-level 5 --log-uid
ip6tables -A OUTPUT -j REJECT
{% endif %}

View File

@@ -0,0 +1,75 @@
#!/bin/bash
# Update /etc/firewall/outbound_whitelist_ipv*.acl from /etc/firewall/outbound_whitelist.acl
resolve_hosts() {
local type="$1" out="$2"
local tmp=$(mktemp "$out.XXXXXX")
(
echo "# AUTO-GENERATED FROM outbound_whitelist.acl"
while read domain; do
case $domain in
"#"* | "") ;;
*)
(host -t "$type" "$domain" 2>&1 || true) | sort -n | while read line; do
case $line in
*"not found"*)
echo "$line" >&2
;;
*"has address"*)
ip="${line##* }"
case $ip in
13.108.*) ip="13.108.0.0/14" ;;
*) ip="${ip%.*}.0/24" ;;
esac
echo "$ip $domain"
;;
*"has IPv6 address"*)
ip="${line##* }"
case $ip in
2607:f8b0:*) ip="2607:f8b0::/32" ;;
*) ip=$(ipv6calc --addr_to_uncompressed "$ip" | cut -d: -f1-4)::/64 ;;
esac
echo "$ip $domain"
;;
esac
done
;;
esac
done < /etc/firewall/outbound_whitelist.acl | sort -n -u
) > "$tmp"
if [ $? -ne 0 ]; then
echo "Error writing to $out" >&2
rm -f "$tmp"
elif cmp -s "$tmp" "$out"; then
rm -f "$tmp"
else
echo "--- Differences in $(basename $out): ---"
echo
diff -u "$out" "$tmp" | grep -v '^\(+++\|---\)' | grep '^[+-]'
echo
mv -f "$tmp" "$out"
fi
}
load_ipset() {
local name="$1" family="$2" file="$3"
local tmp="$name-$$"
ipset -exist create "$name" hash:net family "$family" counters comment
ipset create "$tmp" hash:net family "$family" counters comment
grep -v '^#' "$file" | while read ip name; do
ipset -exist add "$tmp" "$ip" comment "$name"
done
ipset swap "$name" "$tmp"
ipset destroy "$tmp"
}
resolve_hosts A /etc/firewall/outbound_whitelist_ipv4.acl
resolve_hosts AAAA /etc/firewall/outbound_whitelist_ipv6.acl
load_ipset outbound-whitelist inet /etc/firewall/outbound_whitelist_ipv4.acl
load_ipset outbound-whitelist-ipv6 inet6 /etc/firewall/outbound_whitelist_ipv6.acl

View File

@@ -0,0 +1,8 @@
# {{ ansible_managed }}
{# use the local bind #}
nameserver 127.0.0.1
{% if network_fallback_resolvers|d('') %}
{% for ip in network_fallback_resolvers if ip != network_private_ip %}
nameserver {{ ip }}
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,9 @@
---
ntp_servers:
- time1.google.com
- time2.google.com
- time3.google.com
- time4.google.com
ntp_firewall: no

View File

@@ -0,0 +1,3 @@
---
- name: Restart NTP
service: name=ntp state=restarted

5
roles/ntp/meta/main.yml Normal file
View File

@@ -0,0 +1,5 @@
---
dependencies:
- role: firewall
when: ntp_firewall

24
roles/ntp/tasks/main.yml Normal file
View File

@@ -0,0 +1,24 @@
---
- name: Install NTP
apt: pkg=ntp state=present
- name: Configure NTP
template: src=ntp.conf.j2 dest=/etc/ntp.conf
notify: Restart NTP
- name: Configure NTP keys
template: src=ntp.keys.j2 dest=/etc/ntp.keys owner=ntp group=ntp mode=0400
notify: Restart NTP
- name: Ensure NTP is running
service: name=ntp state=started enabled=yes
- name: Configure firewall rules for NTP
template:
dest: /etc/firewall/rules-v4.d/21_ntp.sh
src: etc_firewall_rules-v4.d_21_ntp.sh.j2
owner: root
group: root
mode: 0600
when: ntp_firewall
notify: Restart firewall

View File

@@ -0,0 +1,6 @@
# {{ ansible_managed }}
{% for ip in ntp_servers | default([]) %}
iptables -A INPUT -s {{ ip }} -p udp --dport 123 -m comment --comment "ntp" -j ACCEPT
iptables -A OUTPUT -d {{ ip }} -p udp --dport 123 -m comment --comment "ntp" -j ACCEPT
{% endfor %}

View File

@@ -0,0 +1,34 @@
# {{ ansible_managed }}
# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
driftfile /var/lib/ntp/ntp.drift
keys /etc/ntp.keys
# Enable this if you want statistics to be logged.
#statsdir /var/log/ntpstats/
statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable
{% if ntp_broadcast_key is defined %}
broadcastclient
trustedkey 22
{% else %}
# You do need to talk to an NTP server or two (or three).
{% for server in ntp_servers %}
server {{ server }} iburst
{% endfor %}
{% endif %}
# By default, exchange time with everybody, but don't allow configuration.
restrict -4 default kod notrap nomodify nopeer noquery limited
restrict -6 default kod notrap nomodify nopeer noquery limited
# Local users may interrogate the ntp server more closely.
restrict 127.0.0.1
restrict ::1
# Needed for adding pool entries
restrict source notrap nomodify noquery

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
{% if ntp_broadcast_key is defined %}
22 M {{ ntp_broadcast_key }}
{% endif %}

View File

@@ -0,0 +1,165 @@
postfix_mynetworks: []
postfix_mydestination_local:
- "{{ ansible_hostname }}.maruntiel.net"
- "localhost"
- "localhost.{{ ansible_domain }}"
postfix_mydestination_extra: []
# main.cf settings
postfix_settings:
compatibility_level: 2
myhostname: "{{ ansible_hostname }}.maruntiel.net"
myorigin: /etc/mailname
mydestination: "{{ postfix_mydestination_local + postfix_mydestination_extra }}"
mynetworks: "10.11.0.0/16 62.171.160.169/32 207.244.234.58/32 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 {{ postfix_mynetworks | join(' ') }}"
relayhost: ""
alias_maps: hash:/etc/aliases
alias_database: hash:/etc/aliases
biff: no
mailbox_command:
home_mailbox: Maildir/
mailbox_size_limit: 0
recipient_delimiter: +
append_dot_mydomain: no
readme_directory: no
dovecot_destination_recipient_limit: 1
tls_random_source: dev:/dev/urandom
default_transport: smtp
relay_transport: smtp
relay_domains: ""
inet_protocols: ipv4
inet_interfaces: all
virtual_mailbox_domains:
- /etc/postfix/virtual_domains
virtual_mailbox_base:
- /var/mail/vhosts
virtual_mailbox_maps:
- hash:/etc/postfix/vmailbox
virtual_alias_maps:
- hash:/etc/postfix/virtual_alias
virtual_minimum_uid:
- 100
virtual_uid_maps:
- static:5000
virtual_gid_maps:
- static:5000
virtual_transport:
- virtual
virtual_alias_domains:
- maruntiel.net
- maruntiel.com
- stillmob.ro
- pedimedic.ro
# SMTP SETTINGS
smtp_use_tls: yes
smtp_tls_security_level: may
smtp_tls_note_starttls_offer: yes
smtp_tls_session_cache_database: "btree:${data_directory}/smtp_scache"
# SMTPD SETTINGS
smtpd_use_tls: yes
smtpd_tls_auth_only: no
smtpd_tls_security_level: may
smtpd_tls_loglevel: 1
smtpd_tls_received_header: yes
smtpd_tls_session_cache_timeout: 3600s
smtpd_tls_session_cache_database: "btree:${data_directory}/smtpd_scache"
smtpd_tls_cert_file: "/etc/letsencrypt/live/maruntiel.net/fullchain.pem"
smtpd_tls_key_file: "/etc/letsencrypt/live/maruntiel.net/privkey.pem"
smtpd_banner: "$myhostname ESMTP $mail_name"
smtpd_client_restrictions:
- permit_mynetworks
- permit_sasl_authenticated
- reject_invalid_hostname
- reject_unknown_client
- reject_rbl_client sbl-xbl.spamhaus.org
smtpd_sender_restrictions:
- permit_mynetworks
- reject_unknown_address
- reject_unknown_sender_domain
- reject_non_fqdn_sender
smtpd_recipient_limit: 250
smtpd_recipient_restrictions:
- reject_invalid_hostname
- reject_non_fqdn_sender
- reject_non_fqdn_recipient
- reject_unlisted_sender
- permit_mynetworks
- permit_sasl_authenticated
- reject_unauth_pipelining
- reject_unauth_destination
- check_policy_service unix:private/policyd-spf
- reject_non_fqdn_hostname
- reject_unknown_sender_domain
- reject_rbl_client bl.spamcop.net
- reject_rbl_client zen.spamhaus.org
- permit
smtpd_relay_restrictions:
- reject_invalid_hostname
- reject_non_fqdn_sender
- reject_non_fqdn_recipient
- reject_unlisted_sender
- permit_mynetworks
- permit_sasl_authenticated
- reject_unauth_pipelining
- reject_unauth_destination
- check_policy_service unix:private/policyd-spf
- reject_non_fqdn_hostname
- reject_unknown_sender_domain
- reject_rbl_client bl.spamcop.net
- reject_rbl_client zen.spamhaus.org
- permit
smtpd_client_connection_rate_limit: 10
smtpd_client_message_rate_limit: 10
# SASL
smtpd_sasl_auth_enable: yes
smtpd_sasl_type: dovecot
smtpd_sasl_path: private/auth
broken_sasl_auth_clients: yes
smtpd_sasl_local_domain: \$mydomain
smtpd_sasl_security_options: noanonymous
# Other
header_checks:
- regexp:/etc/postfix/header_checks
# DKIM
milter_default_action: accept
milter_protocol: 6
smtpd_milters: local:opendkim/opendkim.sock
non_smtpd_milters: $smtpd_milters
# SPF
policyd-spf_time_limit: 3600
postfix_opendkim: "{{ postfix_dkim_domains|count > 0 }}"
postfix_relay: no
postfix_smtpd_public: yes
postfix_firewall: "{{ firewall_enabled|default(true) }}"
postfix_dkim_domains: []
virtual_mailbox_domains: /etc/postfix/virtual_domains
virtual_mailbox_base: /var/mail/vhosts
virtual_mailbox_maps: hash:/etc/postfix/vmailbox
virtual_alias_maps: hash:/etc/postfix/virtual_alias
virtual_minimum_uid: 100
virtual_uid_maps: static:5000
virtual_gid_maps: static:5000
virtual_transport: virtual

View File

@@ -0,0 +1,12 @@
- name: Restart postfix
service:
name: "postfix@-"
state: restarted
- name: Rebuild postfix map files
shell: postmap /etc/postfix/*.map
- name: Restart opendkim
service:
name: opendkim
state: restarted

View File

@@ -0,0 +1,4 @@
dependencies:
- role: firewall
when: postfix_firewall

View File

@@ -0,0 +1,151 @@
---
- name: Install postfix
apt:
pkg:
- postfix
- postfix-pcre
state: present
tags: packages
- name: Install postfix configs
template:
dest: "/etc/postfix/{{ item }}"
src: "etc_postfix_{{ item }}.j2"
mode: 0644
owner: root
group: root
with_items:
- main.cf
- master.cf
- header_checks
notify: Restart postfix
tags: configs
- name: Install postfix maps
template:
dest: "/etc/postfix/{{ item }}"
src: "etc_postfix_{{ item }}.j2"
mode: 0640
owner: root
group: postfix
with_items:
- sasl_passwd.map
- transport.map
# - virtual.map
notify: Rebuild postfix map files
tags: configs
- name: Install empty postfix maps
copy:
dest: "/etc/postfix/{{ item }}"
content: ""
force: no
mode: 0644
owner: root
group: root
with_items:
- virtual.map
notify: Rebuild postfix map files
tags: configs
- name: Install postfix-policyd-spf
apt:
pkg:
- postfix-policyd-spf-python
state: present
tags: packages
- name: Ensure postfix is running
service:
name: postfix
state: started
enabled: yes
tags: configs
- name: Install the postfix firewall config
template:
dest: /etc/firewall/rules-v4.d/40_postfix.sh
src: etc_firewall_rules-v4.d_40_postfix.sh.j2
mode: 0644
owner: root
group: root
when: postfix_firewall
notify: Restart firewall
tags:
- configs
- firewall
# =====================================================================
- name: Install opendkim
apt:
pkg:
- opendkim
- opendkim-tools
state: present
when: postfix_opendkim
tags: packages
- name: Ensure postfix is a member of opendkim
user:
name: postfix
groups: opendkim
append: yes
when: postfix_opendkim
notify: Restart postfix
tags: configs
- name: Ensure /etc/opendkim dir exists
file:
path: /etc/opendkim
state: directory
mode: 0755
owner: root
group: root
when: postfix_opendkim
tags: configs
- name: Ensure /etc/opendkim/keys dir exists
file:
path: /etc/opendkim/keys
state: directory
mode: 0750
owner: root
group: opendkim
when: postfix_opendkim
tags: configs
- name: Install opendkim configs
template:
dest: "/{{ item }}"
src: "{{ item | replace('/', '_') }}.j2"
mode: 0644
owner: root
group: root
with_items:
- etc/opendkim.conf
- etc/opendkim/key.table
- etc/opendkim/signing.table
- etc/opendkim/trusted.hosts
when: postfix_opendkim
notify: Restart opendkim
tags: configs
- name: Ensure /var/spool/postfix/opendkim dir exists
file:
path: /var/spool/postfix/opendkim
state: directory
mode: 0755
owner: opendkim
group: postfix
when: postfix_opendkim
tags: configs
- name: Ensure opendkim is running
service:
name: postfix
state: started
enabled: yes
when: postfix_opendkim
tags: configs

View File

@@ -0,0 +1,11 @@
# {{ ansible_managed }}
{% if postfix_smtpd_public %}
iptables -A INPUT -p tcp --dport 25 -m comment --comment "postfix-smtp" -j ACCEPT
{% elif postfix_relay %}
iptables -A internal-in -p tcp --dport 25 -m comment --comment "postfix-smtp" -j ACCEPT
{% endif %}
{% if firewall_output_default_drop %}
iptables -A OUTPUT -p tcp --dport 25 -m owner --gid-owner postfix -m comment --comment "smtp" -j ACCEPT
{% endif %}

View File

@@ -0,0 +1,38 @@
# {{ ansible_managed }}
# This is a basic configuration that can easily be adapted to suit a standard
# installation. For more advanced options, see opendkim.conf(5) and/or
# /usr/share/doc/opendkim/examples/opendkim.conf.sample.
Syslog yes
LogWhy yes
PidFile /var/run/opendkim/opendkim.pid
Socket local:/var/spool/postfix/opendkim/opendkim.sock
UMask 002
UserID opendkim
# Map domains in From addresses to keys used to sign messages
KeyTable file:/etc/opendkim/key.table
SigningTable file:/etc/opendkim/signing.table
# Hosts to ignore when verifying signatures
ExternalIgnoreList /etc/opendkim/trusted.hosts
InternalHosts /etc/opendkim/trusted.hosts
# Commonly-used options; the commented-out versions show the defaults.
Canonicalization relaxed/simple
Mode sv
SubDomains yes
#ADSPAction continue
AutoRestart yes
AutoRestartRate 10/1M
Background yes
DNSTimeout 5
SignatureAlgorithm rsa-sha256
# Always oversign From (sign using actual From and a null From to prevent
# malicious signatures header fields (From and/or others) between the signer
# and the verifier. From is oversigned by default in the Debian package
# because it is often the identity key used by reputation systems and thus
# somewhat security sensitive.
OversignHeaders From

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
{% for domain, info in postfix_dkim_domains | dictsort %}
mail._domainkey.{{ domain }} {{ domain }}:mail:/etc/opendkim/keys/{{ domain }}/mail.private
{% endfor %}

View File

@@ -0,0 +1,6 @@
# {{ ansible_managed }}
{% for domain, info in postfix_dkim_domains | dictsort %}
{{ domain }} mail._domainkey.{{ domain }}
.{{ domain }} mail._domainkey.{{ domain }}
{% endfor %}

View File

@@ -0,0 +1,4 @@
127.0.0.0/8
[::ffff:127.0.0.0]/104
[::1]/128
{{ postfix_mynetworks | join("\n") }}

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
{% for item in postfix_header_checks | default([]) %}
{{ item }}
{% endfor %}

View File

@@ -0,0 +1,13 @@
# {{ ansible_managed }}
# See /usr/share/postfix/main.cf.dist for a commented, more complete version
{% for key, value in postfix_settings | dictsort %}
{% if value is sequence and value is not string %}
{{ key }} = {{ value | flatten | join(",\n\t") }}
{% elif value is sameas true or value is sameas false %}
{{ key }} = {{ 'yes' if value else 'no' }}
{% else %}
{{ key }} = {{ value }}
{% endif %}
{% endfor %}

View File

@@ -0,0 +1,132 @@
# {{ ansible_managed }}
#
# Postfix master process configuration file. For details on the format
# of the file, see the master(5) manual page (command: "man 5 master" or
# on-line: http://www.postfix.org/master.5.html).
#
# Do not forget to execute "postfix reload" after editing this file.
#
# ==========================================================================
# service type private unpriv chroot wakeup maxproc command + args
# (yes) (yes) (no) (never) (100)
# ==========================================================================
smtp inet n - y - - smtpd
#smtp inet n - y - 1 postscreen
#smtpd pass - - y - - smtpd
#dnsblog unix - - y - 0 dnsblog
#tlsproxy unix - - y - 0 tlsproxy
#submission inet n - y - - smtpd
# -o syslog_name=postfix/submission
# -o smtpd_tls_security_level=encrypt
# -o smtpd_sasl_auth_enable=yes
# -o smtpd_reject_unlisted_recipient=no
# -o smtpd_client_restrictions=$mua_client_restrictions
# -o smtpd_helo_restrictions=$mua_helo_restrictions
# -o smtpd_sender_restrictions=$mua_sender_restrictions
# -o smtpd_recipient_restrictions=
# -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
# -o milter_macro_daemon_name=ORIGINATING
smtps inet n - y - - smtpd
# -o syslog_name=postfix/smtps
# -o smtpd_tls_wrappermode=yes
# -o smtpd_sasl_auth_enable=yes
# -o smtpd_reject_unlisted_recipient=no
# -o smtpd_client_restrictions=$mua_client_restrictions
# -o smtpd_helo_restrictions=$mua_helo_restrictions
# -o smtpd_sender_restrictions=$mua_sender_restrictions
# -o smtpd_recipient_restrictions=
# -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
# -o milter_macro_daemon_name=ORIGINATING
#628 inet n - y - - qmqpd
pickup unix n - y 60 1 pickup
cleanup unix n - y - 0 cleanup
qmgr unix n - n 300 1 qmgr
#qmgr unix n - n 300 1 oqmgr
tlsmgr unix - - y 1000? 1 tlsmgr
rewrite unix - - y - - trivial-rewrite
bounce unix - - y - 0 bounce
defer unix - - y - 0 bounce
trace unix - - y - 0 bounce
verify unix - - y - 1 verify
flush unix n - y 1000? 0 flush
proxymap unix - - n - - proxymap
proxywrite unix - - n - 1 proxymap
smtp unix - - y - - smtp
relay unix - - y - - smtp
-o smtp_fallback_relay=
# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
showq unix n - y - - showq
error unix - - y - - error
retry unix - - y - - error
discard unix - - y - - discard
local unix - n n - - local
virtual unix - n n - - virtual
lmtp unix - - y - - lmtp
anvil unix - - y - 1 anvil
scache unix - - y - 1 scache
{% if ansible_lsb.major_release|int >= 10 %}
postlog unix-dgram n - n - 1 postlogd
{% endif %}
#
# ====================================================================
# Interfaces to non-Postfix software. Be sure to examine the manual
# pages of the non-Postfix software to find out what options it wants.
#
# Many of the following services use the Postfix pipe(8) delivery
# agent. See the pipe(8) man page for information about ${recipient}
# and other message envelope options.
# ====================================================================
#
# maildrop. See the Postfix MAILDROP_README file for details.
# Also specify in main.cf: maildrop_destination_recipient_limit=1
#
maildrop unix - n n - - pipe
flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
#
# ====================================================================
#
# Recent Cyrus versions can use the existing "lmtp" master.cf entry.
#
# Specify in cyrus.conf:
# lmtp cmd="lmtpd -a" listen="localhost:lmtp" proto=tcp4
#
# Specify in main.cf one or more of the following:
# mailbox_transport = lmtp:inet:localhost
# virtual_transport = lmtp:inet:localhost
#
# ====================================================================
#
# Cyrus 2.1.5 (Amos Gouaux)
# Also specify in main.cf: cyrus_destination_recipient_limit=1
#
#cyrus unix - n n - - pipe
# user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}
#
# ====================================================================
# Old example of delivery via Cyrus.
#
#old-cyrus unix - n n - - pipe
# flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}
#
# ====================================================================
#
# See the Postfix UUCP_README file for configuration details.
#
uucp unix - n n - - pipe
flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
#
# Other external delivery methods.
#
ifmail unix - n n - - pipe
flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp unix - n n - - pipe
flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
scalemail-backend unix - n n - 2 pipe
flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
mailman unix - n n - - pipe
flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
${nexthop} ${user}
policyd-spf unix - n n - 0 spawn
user=policyd-spf argv=/usr/bin/policyd-spf

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
{% for key, value in postfix_sasl_passwd_map | default({}) | dictsort %}
{{ key }} {{ value.username }}:{{ value.password }}
{% endfor %}

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
{% for key, value in postfix_transport_map | default({}) | dictsort %}
{{ key }} {{ value }}
{% endfor %}

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
{% for key, value in postfix_virtual_map | default({}) | dictsort %}
{{ key }} {{ value }}
{% endfor %}

View File

@@ -0,0 +1,43 @@
---
ssh_client_settings:
# Host:
# - Host: "*"
# SendEnv: LANG LC_*
# HashKnownHosts: yes
ForwardAgent: yes
HashKnownHosts: yes
ssh_server_settings:
Port: 22
Protocol: 2
HostKey:
- /etc/ssh/ssh_host_rsa_key
- /etc/ssh/ssh_host_ecdsa_key
- /etc/ssh/ssh_host_ed25519_key
SyslogFacility: AUTH
LogLevel: INFO
PermitRootLogin: prohibit-password
PubkeyAuthentication: yes
PermitEmptyPasswords: no
AuthenticationMethods publickey,keyboard-interactive
ChallengeResponseAuthentication: yes
PasswordAuthentication: no
X11Forwarding: no
PrintMotd: no
PrintLastLog: yes
AcceptEnv: LANG LC_*
Subsystem:
- sftp /usr/lib/openssh/sftp-server
UsePAM: yes
# Hardened cipher list
KexAlgorithms: curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256
Ciphers: chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs: hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256,hmac-sha2-512
HostKeyAlgorithms: ssh-rsa,ssh-ed25519
# Match:
# - Match: "*"
# AllowAgentForwarding: yes

View File

@@ -0,0 +1,4 @@
---
- name: Restart SSH
service: name=ssh state=restarted

45
roles/ssh/tasks/main.yml Normal file
View File

@@ -0,0 +1,45 @@
---
# Tasks to install and configure OpenSSH
- name: Make sure the SSH server and client packages are installed
apt:
pkg:
- openssh-client
- openssh-server
state: present
tags: ssh
- name: Configure the SSH Client
template:
src: etc_ssh_ssh_config.j2
dest: /etc/ssh/ssh_config
owner: root
group: root
mode: 0644
tags: ssh
- name: Configure the SSH Server
template:
src: etc_ssh_sshd_config.j2
dest: /etc/ssh/sshd_config
owner: root
group: root
mode: 0644
notify: Restart SSH
tags: ssh
#- name: Update ssh_known_hosts
# lineinfile:
# dest: /etc/ssh/ssh_known_hosts
# regexp: "^{{ hostvars[item].ansible_hostname }},"
# line: >
# {{ hostvars[item].ansible_hostname }},{{ hostvars[item].ansible_fqdn }},{{ hostvars[item].ansible_default_ipv4.address }}
# ssh-rsa {{ hostvars[item].ansible_ssh_host_key_rsa_public }}
# state: present
# create: yes
# owner: root
# group: root
# mode: 0644
# with_items: "{{ groups.all|sort }}"
# when: item in hostvars
# tags: ssh

View File

@@ -0,0 +1,8 @@
# {{ ansible_managed }}
# See the ssh_config(5) manpage for details
{% from 'ssh_common.j2' import ssh_config with context %}
{% call ssh_config(ssh_client_settings) %}{% endcall %}
# EOF

View File

@@ -0,0 +1,8 @@
# {{ ansible_managed }}
# See the sshd_config(5) manpage for details
{% from 'ssh_common.j2' import ssh_config with context %}
{% call ssh_config(ssh_server_settings) %}{% endcall %}
# EOF

View File

@@ -0,0 +1,25 @@
# {{ ansible_managed }}
{% macro ssh_config(settings, caller='') %}
{% set sections = ('Host', 'Match') %}
{%- for key, value in settings|dictsort if key not in sections %}
{% if value is sequence and value is not string %}
{% for item in value %}
{{ key }} {{ item }}
{% endfor %}
{% else %}
{{ key }} {{ ['no','yes'][value|int] if value in (False,True) else value }}
{% endif %}
{% endfor %}
{%- for section in sections if section in settings %}
{% for item in settings[section] %}
{{ section }} {{ item[section] }}
{% for key, value in item|dictsort if key != section %}
{{ key }} {{ ['no','yes'][value|int] if value in (False,True) else value }}
{% endfor %}
{% endfor %}
{% endfor -%}
{% endmacro %}