This commit is contained in:
AnsibleGuy 2021-11-02 22:06:52 +01:00
commit 4875e6ef1f
20 changed files with 809 additions and 0 deletions

21
LICENSE.txt Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 AnsibleGuy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

50
README.md Normal file
View File

@ -0,0 +1,50 @@
# Apache2 Ansible Role
Ansible role to install apache2 sites on the target server.
**Tested:**
* Debian 11
## Functionality
* Package installation
* Ansible dependencies (_minimal_)
* Apache2
* Configuration
*
* Default opt-in:
*
* Default opt-outs:
*
* Default config:
*
## Info
* **Note:** Most of this functionality can be opted in or out using the main defaults file and variables!
* **Note:** this role currently only supports debian-based systems
## Requirements
* Community collection: ```ansible-galaxy install -r requirements.yml```
## Usage
Run the playbook:
```bash
ansible-playbook -K -D -i inventory/hosts.yml playbook.yml
```
You need to define your instances by configuring the 'mariadb' dictionary!
```yaml
apache
```
There are also some useful **tags** available:
* base => only configure basics; sites will not be touched
* sites
* config => configuration (base and instances)
* certs

148
defaults/main.yml Normal file
View File

@ -0,0 +1,148 @@
---
# main switches
configure_anti_ddos: true # mod_evasive
configure_security: true # https://www.digitalocean.com/community/tutorials/how-to-set-up-mod_security-with-apache-on-debian-ubuntu
# default config => is overwritten by provided config
default_apache:
sites: {}
log:
path: '/var/log/apache2'
per_site: true
syslog: true
syslog_host:
syslog_port: 514
syslog_max_size: '4KiB' # see: https://manpages.ubuntu.com/manpages/xenial/man1/logger.1.html
prefix_ue: 'apache_plain_'
prefix_ssl: 'apache_ssl_'
user: 'www-data'
group: 'www-data'
# additions to the main apache config
config: # see: https://httpd.apache.org/docs/2.4/mod/core.html
ServerTokens: 'Prod'
ServerSignature: 'Off'
FileETag: 'None'
KeepAlive: 'On'
KeepAliveTimeout: 5
MaxKeepAliveRequests: 100
LimitRequestBody: 51200000 # 50MB => if you use file-uploads you might need to change this (0=unlimited, max=2147483647 [2GB])
LimitRequestFields: 50
LimitRequestFieldSize: 8190
LimitRequestLine: 8190
LimitXMLRequestBody: 1000000
TimeOut: 60
TraceEnable: 'off'
# ssl option => see: https://httpd.apache.org/docs/2.4/mod/mod_ssl.html
SSLProtocol: 'ALL -TLSv1.1 -TLSv1 -SSLv2 -SSLv3'
SSLCipherSuite: 'ALL:+HIGH:!ADH:!EXP:!SSLv2:!SSLv3:!MEDIUM:!LOW:!NULL:!aNULL'
SSLHonorCipherOrder: 'on'
SSLOptions: '+StrictRequire'
SSLSessionTickets: 'off'
SSLCompression: 'off'
headers: # https://htaccessbook.com/important-security-headers/ | https://geekflare.com/http-header-implementation/
'Header always set Strict-Transport-Security': '"max-age=31536000; includeSubDomains; preload"'
'Referrer-Policy': '"same-origin"'
'Content-Security-Policy': "\"default-src 'self';\""
'X-Frame-Options': 'SAMEORIGIN'
'X-Content-Type-Options': 'nosniff'
'X-Permitted-Cross-Domain-Policies': '"none"'
'X-XSS-Protection': '"1; mode=block"'
'Header always edit Set-Cookie ^(.*)$': '$1;HttpOnly;Secure;SameSite=None'
# 'Header set Permissions-Policy': '"none"'
# 'Header set Content-Security-Policy': '"default-src https:; font-src https:; img-src https:; script-src https:; style-src https:;"'
modules:
present: ['ssl', 'headers', 'rewrite']
absent: ['autoindex']
letsencrypt:
key_size: 4096
path: '/etc/letsencrypt'
path_key: '/etc/ssl/private'
path_cert: '/etc/ssl/certs'
renew_timer: 'Mon *-*-* 00:00:00'
verbosity: 'v'
APACHE_CONFIG: "{{ default_apache | combine(apache, recursive=true) }}"
# site-specific config
default_site_config:
mode: 'serve'
admin: 'apache@template.ansibleguy.net'
port_plain: 80
port_ssl: 443
config: {} # site-specific setting-value pairs
config_additions: [] # lines that will 1-to-1 be appended to the site-config
security: # https://www.nixpal.com/apache-httpd-hardening/
disable_root_index: true
disable_directory_access: true
disable_ssi_cgi: true
limit_directory_access: true
redirect:
target: 'https://github.com/ansibleguy'
request_uri: true
serve:
path: '/var/www/html'
ssl:
mode: 'letsencrypt' # local/selfsigned/letsencrypt
file_pub: '/etc/apache2/ssl/DOMAIN.crt' # should use the certificate chain => top is server cert; bottom root cert
file_key: '/etc/apache2/ssl/DOMAIN.key'
file_csr: '/etc/apache2/ssl/DOMAIN.csr'
file_ca:
csr_data:
country: 'AT'
org: 'AnsibleGuy'
email: 'apache@template.ansibleguy.net'
cn: 'Apache Certificate'
default_modules:
# <IfModule ${MOD}>
# </IfModule>
prefork: # see: https://httpd.apache.org/docs/2.4/mod/mpm_common.html
ifname: 'prefork.c'
settings:
StartServers: 5
MinSpareServers: 5
MaxSpareServers: 10
MaxRequestWorkers: 256
MaxConnectionsPerChild: 0
mod_evasive:
ifname: 'mod_evasive20.c'
settings:
DOSHashTableSize: 4096
DOSPageCount: 25
DOSSiteCount: 100
DOSPageInterval: 1
DOSSiteInterval: 1
DOSBlockingPeriod: 60
DOSLogDir: "{{ CONFIG.log.path }}"
# DOSSystemCommand:
# DOSEmailNotify: mail@yourdomain.com
DOSWhitelist: [
'127.0.0.*', '192.168.*.*', '10.*.*.*', '172.16.*.*', '172.17.*.*', '172.18.*.*', '172.19.*.*',
'172.20.*.*', '172.21.*.*', '172.22.*.*', '172.23.*.*', '172.24.*.*', '172.25.*.*', '172.26.*.*',
'172.27.*.*', '172.28.*.*', '172.29.*.*', '172.30.*.*', '172.31.*.*', '172.32.*.*',
]
APACHE_MODULES: "{{ default_modules | combine(modules, recursive=true) }}"
packages:
apache: ['apache2']
letsencrypt: ['python3-certbot-apache']
apache_config_graylist: [
'SSLEngine', 'SSLCertificateKeyFile', 'SSLCertificateFile', 'SSLCertificateChainFile', 'ErrorLog', 'CustomLog', 'ServerAdmin',
'ServerAlias', 'ServerName', 'Redirect'
]
apache_restricted_methods: ['GET', 'POST', 'HEAD']

18
filter_plugins/utils.py Normal file
View File

@ -0,0 +1,18 @@
from re import sub as regex_replace
class FilterModule(object):
def filters(self):
return {
"safe_key": self.safe_key,
"all_true": self.all_true,
}
@staticmethod
def safe_key(key: str) -> str:
return regex_replace('[^0-9a-zA-Z]+', '', key.replace(' ', '_'))
@staticmethod
def all_true(data: list) -> bool:
return all(data)

23
meta/main.yml Normal file
View File

@ -0,0 +1,23 @@
---
galaxy_info:
author: 'AnsibleGuy <guy@ansibleguy.net>'
readme: 'README.md'
license: 'MIT'
repository: 'https://github.com/ansibleguy/infra_apache'
issue_tracker_url: 'https://github.com/ansibleguy/infra_apache/issues'
github_branch: 'stable'
min_ansible_version: 2.9.0
description: 'Role to deploy apache2 sites on a linux server'
platforms:
- name: Debian
versions:
- bullseye
galaxy_tags:
- 'web'
- 'webserver'
- 'apache'
collections:
- 'community.crypto'
- 'community.general'

9
playbook.yml Normal file
View File

@ -0,0 +1,9 @@
---
# ansible-playbook -K -D -i inventory/hosts.yml playbook.yml
- hosts: all # should be limited to web-servers
become: true
gather_facts: yes
roles:
- ansibleguy.infra_apache

9
requirements.yml Normal file
View File

@ -0,0 +1,9 @@
# external roles and collections to download
# install: ansible-galaxy install -r requirements.yml
collections:
- name: 'community.crypto'
source: 'https://galaxy.ansible.com'
- name: 'community.general'
source: 'https://galaxy.ansible.com'

68
tasks/debian/add_site.yml Normal file
View File

@ -0,0 +1,68 @@
---
- name: "Apache | Debian | Config | Site '{{ name }}' | Configuring listen-ports"
ansible.builtin.blockinfile:
path: '/etc/apache2/ports.conf'
block: |
Listen {{ port }}
marker: "# {mark} ANSIBLE MANAGED BLOCK - port '{{ port }}'"
insertafter: '# /etc/apache2/sites-enabled/000-default.conf'
ignore_errors: true
when:
- port != 80
- port != 443
- port != '80'
- port != '443'
loop_control:
loop_var: port
with_items:
- "{{ site.port_plain }}"
- "{{ site.port_ssl }}"
- name: "Apache | Debian | Config | Site '{{ name }}' | Create root directory"
ansible.builtin.file:
path: "{{ site.serve.path }}"
state: directory
owner: "{{ APACHE_CONFIG.user }}"
group: "{{ APACHE_CONFIG.group }}"
mode: 0755
when: site.mode == 'serve'
- name: "Apache | Debian | Config | Site '{{ name }}' | Configuring site"
ansible.builtin.template:
src: 'templates/etc/apache2/sites-available/site.conf.j2'
dest: "/etc/apache2/sites-available/site_{{ name }}.conf"
owner: 'root'
group: 'root'
mode: 0644
validate: 'apachectl -t -f %s'
register: apache_config_deployment
ignore_errors: yes
- name: "Apache | Debian | Config | Site '{{ name }}' | Ask user"
ansible.builtin.pause:
prompt: "The apache config validation failed! Sometimes this is a false-negative.
Do you want to force the deployment? (yes/no)"
register: force_deploy
when: apache_config_deployment.failed
- name: "Apache | Debian | Config | Site '{{ name }}' | Configuring site (forced)"
ansible.builtin.template:
src: 'templates/etc/apache2/sites-available/site.conf.j2'
dest: "/etc/apache2/sites-available/site_{{ name }}.conf"
owner: 'root'
group: 'root'
mode: 0644
backup: true
when:
- apache_config_deployment.failed
- force_deploy.user_input == 'yes'
- name: "Apache | Debian | Config | Site '{{ name }}' | Enabling site"
ansible.builtin.file:
state: link
src: "/etc/apache2/sites-available/site_{{ name }}.conf"
dest: "/etc/apache2/sites-enabled/site_{{ name }}.conf"
owner: 'root'
group: 'root'
mode: 0644

View File

@ -0,0 +1,13 @@
---
- name: Apache | Debian | LetsEncrypt Certbot | Cleanup | Disable temporary apache site
ansible.builtin.file:
state: absent
dest: '/etc/apache2/sites-enabled/tmp_le_dummy.conf'
register: tmp_site_config
- name: Apache | Debian | LetsEncrypt Certbot | Cleanup | Reload apache
ansible.builtin.systemd:
name: 'apache2.service'
state: reloaded
when: tmp_site_config.changed

View File

@ -0,0 +1,23 @@
---
- name: Apache | Debian | LetsEncrypt Certbot | Dependencies | Deploying temporary apache site
ansible.builtin.template:
src: 'templates/etc/apache2/sites-available/le_dummy.conf.j2'
dest: '/etc/apache2/sites-available/tmp_le_dummy.conf'
owner: 'root'
group: 'root'
mode: 0644
- name: Apache | Debian | LetsEncrypt Certbot | Dependencies | Enable apache site
ansible.builtin.file:
state: link
src: '/etc/apache2/sites-available/tmp_le_dummy.conf'
dest: '/etc/apache2/sites-enabled/tmp_le_dummy.conf'
owner: 'root'
group: 'root'
mode: 0644
- name: Apache | Debian | LetsEncrypt Certbot | Dependencies | Reload apache
ansible.builtin.systemd:
name: 'apache2.service'
state: reloaded

View File

@ -0,0 +1,45 @@
---
- name: "Apache | Debian | LetsEncrypt Certbot | Checking if cert for domain '{{ site.domain }}' exists"
ansible.builtin.shell: 'certbot certificates'
register: domain_cert
changed_when: false
# todo: check domains registered in current certificate (certbot certificates) and remove it if there are more than configured before re-configuring it
- name: "Apache | Debian | LetsEncrypt Certbot | Set key/cert paths for domain '{{ site.domain }}'"
ansible.builtin.set_fact:
_path_key: "{{ APACHE_CONFIG.letsencrypt.path_key }}/{{ name }}"
_path_cert: "{{ APACHE_CONFIG.letsencrypt.path_cert }}/{{ name }}"
_path_live: "{{ APACHE_CONFIG.letsencrypt.path }}/live/{{ name }}"
- name: "Apache | Debian | LetsEncrypt Certbot | Creating key/cert directories for domain '{{ site.domain }}'"
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: 'root'
group: 'root'
mode: 0755
with_items:
- "{{ _path_key }}"
- "{{ _path_cert }}"
- name: Apache | Debian | LetsEncrypt Certbot | Getting cert
ansible.builtin.include_tasks: domain_new.yml
when: domain_cert.stdout.find(site.domain) == -1
- name: "Apache | Debian | LetsEncrypt Certbot | Linking certificates for domain '{{ site.domain }}'"
ansible.builtin.file:
state: link
src: "{{ item.value.src }}"
dest: "{{ item.value.dst }}"
owner: "{{ APACHE_CONFIG.user }}"
group: "{{ APACHE_CONFIG.group }}"
mode: 0400
follow: yes
with_dict:
- {'config': {'dst': "{{ _path_key }}/privkey.pem", 'src': "{{ _path_live }}/privkey.pem"}}
- {'config': {'dst': "{{ _path_cert }}/cert.pem", 'src': "{{ _path_live }}/cert.pem"}}
- {'config': {'dst': "{{ _path_cert }}/chain.pem", 'src': "{{ _path_live }}/chain.pem"}}
- {'config': {'dst': "{{ _path_cert }}/fullchain.pem", 'src': "{{ _path_live }}/fullchain.pem"}}
ignore_errors: yes

View File

@ -0,0 +1,26 @@
---
- name: "Apache | Debian | LetsEncrypt Certbot | Creating alternative name string (1/3)"
ansible.builtin.set_fact:
_aliases: "{{ site.aliases | join(' --domain ') }}"
when: apache_aliases | length > 0
- name: "Apache | Debian | LetsEncrypt Certbot | Creating alternative name string (2/3)"
ansible.builtin.set_fact:
_apache_aliases: "{{ '--domain ' + _aliases }}"
when: apache_aliases | length > 0
- name: "Apache | Debian | LetsEncrypt Certbot | Creating alternative name string (3/3)"
ansible.builtin.set_fact:
_apache_aliases: ''
when: apache_aliases | length == 0
- name: debug
ansible.builtin.debug:
msg: "certbot certonly --apache -{{ APACHE_CONFIG.letsencrypt.verbosity }} --non-interactive --agree-tos --email {{ site.admin }} --cert-name {{ name }}
--rsa-key-size {{ APACHE_CONFIG.letsencrypt.key_size }} --no-redirect --domain {{ site.domain }} {{ _apache_aliases }}"
- name: "Apache | Debian | LetsEncrypt Certbot | Starting certbot for domain '{{ site.domain }}'"
ansible.builtin.shell: "certbot certonly --apache -{{ APACHE_CONFIG.letsencrypt.verbosity }} --non-interactive --agree-tos --email {{ site.admin }} --cert-name {{ name }}
--rsa-key-size {{ APACHE_CONFIG.letsencrypt.key_size }} --no-redirect --domain {{ site.domain }} {{ _apache_aliases }}"
ignore_errors: yes

View File

@ -0,0 +1,41 @@
---
- name: Apache | Debian | LetsEncrypt Certbot | Install package
ansible.builtin.apt:
name: "{{ packages.letsencrypt }}"
state: present
- name: Apache | Debian | LetsEncrypt Certbot | Check if a apache virtualhost is available
ansible.builtin.shell: 'ls /etc/apache2/sites-enabled/'
register: enabled_apache_sites
- name: Apache | Debian | LetsEncrypt Certbot | Checking dependencies
ansible.builtin.include_tasks: dependencies.yml
when: enabled_apache_sites.stdout == ''
- name: Apache | Debian | LetsEncrypt Certbot | Processing apache sites
ansible.builtin.include_tasks: domain.yml
vars:
site: "{{ default_site_config | combine(site_item, recursive=true) }}"
name: "{{ site_item.key | safe_key }}"
loop_control:
loop_var: site_item
with_dict: "{{ APACHE_CONFIG.sites }}"
- name: Apache | Debian | LetsEncrypt Certbot | Cleanup dependencies
ansible.builtin.include_tasks: cleanup.yml
- name: Apache | Debian | LetsEncrypt Certbot | Adding systemd files for certbot renewal
ansible.builtin.template:
src: "templates/etc/systemd/system/{{ item }}.j2"
dest: "/etc/systemd/system/{{ item }}"
with_items:
- 'ansibleguy.infra_apache.LetsEncryptCertbot.service'
- 'ansibleguy.infra_apache.LetsEncryptCertbot.timer'
- name: Apache | Debian | LetsEncrypt Certbot | Enabling cert-renewal systemd timer
ansible.builtin.systemd:
daemon_reload: yes
name: 'LetsEncryptCertbot.timer'
enabled: yes
state: started

100
tasks/debian/main.yml Normal file
View File

@ -0,0 +1,100 @@
---
- name: Apache | Debian | Install apache
ansible.builtin.apt:
name: "{{ packages.apache }}"
state: present
- name: Apache | Debian | Checking if all sites exist (1/2)
ansible.builtin.stat:
path: "/etc/apache2/sites-available/site_{{ item.key | safe_key }}.conf"
register: sites_exist_raw
with_dict: "{{ APACHE_CONFIG.sites }}"
- name: Apache | Debian | Checking if all sites exist (2/2)
ansible.builtin.set_fact:
sites_exist: "{{ sites_exist_raw | json_query('[*].results.stat.exists') | all_true }}"
- name: Apache | Debian | Getting certificate via LetsEncrypt
ansible.builtin.import_tasks: letsencrypt/main.yml
when: >
(APACHE_CONFIG.ssl.renew or
not sites_exist) and
APACHE_CONFIG.ssl.mode == 'letsencrypt'
- name: Apache | Debian | Enabling apache modules
community.general.apache2_module:
state: present
name: "{{ item }}"
when: item not in APACHE_CONFIG.modules.absent
loop: "{{ APACHE_CONFIG.modules.present }}"
- name: Apache | Debian | Disabling apache modules
community.general.apache2_module:
state: absent
name: "{{ item }}"
loop: "{{ APACHE_CONFIG.modules.absent }}"
# todo: configure module settings
# todo: check if apache2.conf editing is still needed
#- name: Apache | Debian | Adding global config
# ansible.builtin.blockinfile:
# path: '/etc/apache2/apache2.conf'
# block: |
# {% for setting, value in apache_config_additions_default.items() %}
# {{ setting }} {{ value }}
# {% endfor %}
# {% for setting, value in apache_config_additions.items() %}
# {{ setting }} {{ value }}
# {% endfor %}
# marker: "# {mark} ANSIBLE MANAGED BLOCK - global config"
# validate: 'apachectl -t -f %s'
- name: Apache | Debian | Disabling default apache sites
ansible.builtin.file:
state: absent
dest: "/etc/apache2/sites-enabled/{{ item }}"
with_items:
- '000-default.conf'
- 'default-ssl.conf'
- name: Apache | Debian | Removing apache site
ansible.builtin.include_tasks: rm_site.yml
vars:
site: "{{ default_site_config | combine(site_item, recursive=true) }}"
name: "{{ site_item.key | safe_key }}"
when: site_item.state | default('present') != 'present'
loop_control:
loop_var: site_item
with_dict: "{{ APACHE_CONFIG.sites }}"
- name: Apache | Debian | Reloading apache
ansible.builtin.systemd:
name: 'apache2.service'
state: reloaded
tags: [base, config, sites, certs]
- name: Apache | Debian | Adding apache site
ansible.builtin.include_tasks: add_site.yml
vars:
site: "{{ default_site_config | combine(site_item, recursive=true) }}"
name: "{{ site_item.key | safe_key }}"
when: site_item.state | default('present') == 'present'
loop_control:
loop_var: site_item
with_dict: "{{ APACHE_CONFIG.sites }}"
- name: Apache | Debian | Starting/Enabling apache
ansible.builtin.systemd:
name: 'apache2.service'
enabled: yes
state: started
tags: [base]
- name: Apache | Debian | Reloading apache
ansible.builtin.systemd:
name: 'apache2.service'
enabled: yes
state: reloaded
tags: [base, config, sites, certs]

24
tasks/debian/rm_site.yml Normal file
View File

@ -0,0 +1,24 @@
---
# ports will be left configured since I found no clean way to manage them statefully
- name: "Apache | Debian | Config | Site '{{ name }}' | Removing web-root"
ansible.builtin.file:
path: "{{ site.serve.path }}"
state: absent
force: yes
when: site.mode == 'serve'
- name: "Apache | Debian | Config | Site '{{ name }}' | Removing/Disabling site"
ansible.builtin.template:
path: "{{ item }}"
state: absent
loop:
- "/etc/apache2/sites-available/site_{{ name }}.conf"
- "/etc/apache2/sites-enabled/site_{{ name }}.conf"
- name: "Apache | Debian | Config | Site '{{ name }}' | Removing certificate from certbot"
ansible.builtin.shell: "certbot certonly --apache -{{ APACHE_LE_CONFIG.verbosity }} --non-interactive --agree-tos --email {{ site.admin }} --cert-name {{ name }}
--rsa-key-size {{ APACHE_LE_CONFIG.key_size }} --no-redirect --domain {{ site.domain }} {{ _apache_aliases }}"
ignore_errors: yes
when: site.ssl.mode == 'letsencrypt'

5
tasks/main.yml Normal file
View File

@ -0,0 +1,5 @@
---
- name: Apache | Processing debian config
ansible.builtin.import_tasks: debian/main.yml
when: "ansible_distribution|lower in ['debian', 'ubuntu']"

View File

@ -0,0 +1,6 @@
<VirtualHost *:80>
ServerName dummy.letsencrypt.localhost
ServerAdmin webmaster@localhost
ErrorLog {{ APACHE_CONFIG.log.path }}/error.log
CustomLog {{ APACHE_CONFIG.log.path }}/access.log combined
</VirtualHost>

View File

@ -0,0 +1,163 @@
<VirtualHost *:{{ site.port_plain }}>
ServerName {{ site.domain }}
{% if site.aliases | length > 0 %}
ServerAlias {% for name in site.aliases %} {{ name }} {% endfor %}
{% endif %}
ServerAdmin {{ site.admin }}
# log config
{% if APACHE_CONFIG.log.syslog and APACHE_CONFIG.log.syslog_host is not none %}
ErrorLog "| /usr/bin/logger -n {{ APACHE_CONFIG.log.syslog_host }} -P {{ APACHE_CONFIG.log.syslog_port }} -p local1.error -t {{ APACHE_CONFIG.log.prefix_ue }}{{ name }}"
CustomLog "| /usr/bin/logger -n {{ APACHE_CONFIG.log.syslog_host }} -P {{ APACHE_CONFIG.log.syslog_port }} -p local1.info -t {{ APACHE_CONFIG.log.prefix_ue }}{{ name }}" combined
{% elif APACHE_CONFIG.log.syslog %}
ErrorLog "| /usr/bin/logger -p local1.error -t {{ APACHE_CONFIG.log.prefix_ue }}{{ name }}"
CustomLog "| /usr/bin/logger -p local1.info -t {{ APACHE_CONFIG.log.prefix_ue }}{{ name }}" combined
{% elif APACHE_CONFIG.log.per_site %}
ErrorLog {{ APACHE_CONFIG.log.path }}/{{ name }}_error.log
CustomLog {{ APACHE_CONFIG.log.path }}/{{ name }}_access.log combined
{% else %}
ErrorLog {{ APACHE_CONFIG.log.path }}/error.log
CustomLog {{ APACHE_CONFIG.log.path }}/access.log combined
{% endif %}
# redirect all to secure connection
Redirect permanent / https://{{ site.domain }}
</VirtualHost>
<VirtualHost *:{{ site.port_ssl }}>
ServerName {{ site.domain }}
{% if site.aliases | length > 0 %}
ServerAlias {% for name in site.aliases %} {{ name }} {% endfor %}
{% endif %}
ServerAdmin {{ site.admin }}
# log config
{% if APACHE_CONFIG.log.syslog and APACHE_CONFIG.log.syslog_host is not none %}
ErrorLog "| /usr/bin/logger -n {{ APACHE_CONFIG.log.syslog_host }} -P {{ APACHE_CONFIG.log.syslog_port }} -p local1.error -t {{ APACHE_CONFIG.log.prefix_ue }}{{ name }}"
CustomLog "| /usr/bin/logger -n {{ APACHE_CONFIG.log.syslog_host }} -P {{ APACHE_CONFIG.log.syslog_port }} -p local1.info -t {{ APACHE_CONFIG.log.prefix_ue }}{{ name }}" combined
{% elif APACHE_CONFIG.log.syslog %}
ErrorLog "| /usr/bin/logger -p local1.error -t {{ APACHE_CONFIG.log.prefix_ue }}{{ name }}"
CustomLog "| /usr/bin/logger -p local1.info -t {{ APACHE_CONFIG.log.prefix_ue }}{{ name }}" combined
{% elif APACHE_CONFIG.log.per_site %}
ErrorLog {{ APACHE_CONFIG.log.path }}/{{ name }}_error.log
CustomLog {{ APACHE_CONFIG.log.path }}/{{ name }}_access.log combined
{% else %}
ErrorLog {{ APACHE_CONFIG.log.path }}/error.log
CustomLog {{ APACHE_CONFIG.log.path }}/access.log combined
{% endif %}
# ssl config
<IfModule mod_ssl.c>
SSLEngine on
SSLCertificateKeyFile /etc/ssl/private/{{ apache_site }}/privkey.pem
SSLCertificateFile /etc/ssl/certs/{{ apache_site }}/cert.pem
SSLCertificateChainFile /etc/ssl/certs/{{ apache_site }}/fullchain.pem
</IfModule>
{% if APACHE_CONFIG.config | length > 0 %}
# global config
{% for setting, value in APACHE_CONFIG.config.items() %}
{% if setting not in apache_config_graylist %}
{{ setting }} {{ value }}
{% endif %}
{% endfor %}
{% endif %}
{% if site.config | length > 0 %}
# site-specific config
{% for setting, value in site.config.items() %}
{% if setting not in apache_config_graylist %}
{{ setting }} {{ value }}
{% endif %}
{% endfor %}
{% endif %}
{% if APACHE_CONFIG.headers | length > 0 %}
# global headers
<IfModule mod_headers.c>
{% for header, value in APACHE_CONFIG.headers.items() %}
{% if 'Header' in header %}
{{ header }} {{ value }}
{% else %}
Header set {{ header }} {{ value }}
{% endif %}
{% endfor %}
</IfModule>
{% endif %}
{% if site.headers | length > 0 %}
# site-specific headers
<IfModule mod_headers.c>
{% for header, value in site.headers.items() %}
{% if 'Header' in header %}
{{ header }} {{ value }}
{% else %}
Header set {{ header }} {{ value }}
{% endif %}
{% endfor %}
</IfModule>
{% endif %}
# security config
{% if site.security.restrict_methods %}
<LimitExcept {% for method in apache_restricted_methods %}{{ method }} {% endfor %}>
deny from all
</LimitExcept>
{% endif %}
{% if site.security.limit_directory_access %}
<Directory />
Options None
Order deny,allow
Deny from all
</Directory>
{% endif %}
{% if site.security.disable_directory_access %}
<Directory "=">
Require all denied
</Directory>
{% endif %}
{% if site.mode == 'redirect' %}
# redirect-mode config
Redirect permanent / {{ site.redirect.target }}
{% if site.redirect.request_uri %}
RedirectMatch permanent ^/(.*)$ {{ site.redirect.target }}/$1
{% else %}
RedirectMatch permanent ^/(.*)$ {{ site.redirect.target }}
{% endif %}
{% elif site.mode == 'serve' %}
# serve-mode config
DocumentRoot {{ site.serve.path }}
# mode-specific security config
<Directory {{ site.serve.path }}>
{% if site.security.disable_ssi_cgi %}
Options -FollowSymLinks -ExecCGI -Includes
AllowOverride None
Require all granted
{% endif %}
{% if site.security.disable_root_index %}
Options -Indexes
{% endif %}
</Directory>
{% endif %}
{% if site.config_additions | length > 0 %}
# additional lines
{% endif %}
{% for line in site.config_additions %}
{{ line }}
{% endfor %}
</VirtualHost>
ServerName {{ site.domain }}

View File

@ -0,0 +1,7 @@
[Unit]
Description=Service to renew LetsEncrypt Certificates using certbot
[Service]
Type=oneshot
ExecStart=certbot renew -{{ APACHE_CONFIG.letsencrypt.verbosity }} --non-interactive --agree-tos --renew-with-new-domains --rsa-key-size {{ APACHE_CONFIG.letsencrypt.key_size }}
SuccessExitStatus=0

View File

@ -0,0 +1,10 @@
[Unit]
Description=Timer to renew LetsEncrypt Certificates using certbot
[Timer]
OnCalendar={{ APACHE_CONFIG.letsencrypt.renew_timer }}
Persistent=false
WakeSystem=false
[Install]
WantedBy=multi-user.target