init
This commit is contained in:
commit
4875e6ef1f
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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']
|
||||
|
|
@ -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)
|
||||
|
|
@ -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'
|
||||
|
|
@ -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
|
||||
|
|
@ -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'
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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]
|
||||
|
|
@ -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'
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
|
||||
- name: Apache | Processing debian config
|
||||
ansible.builtin.import_tasks: debian/main.yml
|
||||
when: "ansible_distribution|lower in ['debian', 'ubuntu']"
|
||||
|
|
@ -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>
|
||||
|
|
@ -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 }}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue