major cleanup
This commit is contained in:
parent
76f918111a
commit
aeef456223
26 changed files with 240 additions and 323 deletions
8
.ansible-lint
Normal file
8
.ansible-lint
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
skip_list:
|
||||||
|
- meta-runtime[unsupported-version]
|
||||||
|
- galaxy[no-changelog]
|
||||||
|
- galaxy[version-incorrect]
|
||||||
|
- name[casing]
|
||||||
|
- var-naming[no-role-prefix]
|
29
.forgejo/workflows/ansible-galaxy.yml
Normal file
29
.forgejo/workflows/ansible-galaxy.yml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
name: Ansible Galaxy
|
||||||
|
|
||||||
|
on: # noqa yaml[truthy]
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: docker
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set version in galaxy.yml
|
||||||
|
run: |
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/v}
|
||||||
|
sed -re "s/^version:.*$/version: ${VERSION}/" -i galaxy.yml
|
||||||
|
|
||||||
|
- name: Upload collection to Ansible Galaxy
|
||||||
|
env:
|
||||||
|
GALAXY_API_KEY: ${{ secrets.GALAXY_API_KEY }}
|
||||||
|
run: |
|
||||||
|
apt update; apt install --yes python3-pip
|
||||||
|
pip3 install --break-system-packages ansible
|
||||||
|
ansible-galaxy collection build
|
||||||
|
ansible-galaxy collection publish --api-key=${GALAXY_API_KEY} s3lph-webserver*tar.gz
|
17
.forgejo/workflows/ansible-lint.yml
Normal file
17
.forgejo/workflows/ansible-lint.yml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
name: Ansible Lint
|
||||||
|
on: [push, pull_request] # noqa yaml[truthy]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: docker
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
apt update; apt install --yes python3-pip
|
||||||
|
pip3 install --break-system-packages ansible-lint
|
||||||
|
ansible-lint
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
s3lph-webserver*.tar.gz
|
72
README.md
72
README.md
|
@ -1,3 +1,73 @@
|
||||||
# Ansible Collection - s3lph.webserver
|
# Ansible Collection - s3lph.webserver
|
||||||
|
|
||||||
WIP
|
Configure Apache2 and Certbot, with an auto-bootstrap mechanism.
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Multi-VHost Setup with Let's Encrypt Certificates
|
||||||
|
|
||||||
|
We start with the following playbook:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- hosts: webserver
|
||||||
|
roles:
|
||||||
|
- s3lph.webserver.apache2
|
||||||
|
- s3lph.webserver.certbot
|
||||||
|
```
|
||||||
|
|
||||||
|
To configure our VHosts, we create a hostvars file, e.g. `host_vas/web01.example.org/apache2.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apache2_sites:
|
||||||
|
|
||||||
|
# This simply serves /var/www/foo.example.org/html under the vhost foo.example.org.
|
||||||
|
foo.example.org:
|
||||||
|
documentroot: /var/www/foo.example.org/html
|
||||||
|
tls_certfile: /etc/letsencrypt/live/foo.example.org/fullchain.pem
|
||||||
|
tls_keyfile: /etc/letsencrypt/live/foo.example.org/privkey.pem
|
||||||
|
|
||||||
|
# A simple reverse-proxy example
|
||||||
|
bar.example.org:
|
||||||
|
aliases:
|
||||||
|
- baz.example.org
|
||||||
|
documentroot: /var/www/bar.example.org/html
|
||||||
|
tls_certfile: /etc/letsencrypt/live/bar.example.org/fullchain.pem
|
||||||
|
tls_keyfile: /etc/letsencrypt/live/bar.example.org/privkey.pem
|
||||||
|
# You can add any Apache2 config to the VHost config
|
||||||
|
additional_config: |
|
||||||
|
ProxyPass / http://localhost:8080/
|
||||||
|
ProxyPassReverse / http://localhost:8080/
|
||||||
|
```
|
||||||
|
|
||||||
|
To tell the certbot role which certificates to issue, create another hostvars file such as `host_vas/web01.example.org/certbot.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
certbot_certificates:
|
||||||
|
foo.example.org:
|
||||||
|
webroot_map:
|
||||||
|
foo.example.org: /var/www/foo.example.org/html
|
||||||
|
bar.example.org:
|
||||||
|
webroot_map:
|
||||||
|
bar.example.org: /var/www/bar.example.org/html
|
||||||
|
baz.example.org: /var/www/bar.example.org/html
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bootstrap
|
||||||
|
|
||||||
|
The bootstrap mechanism works in two steps:
|
||||||
|
|
||||||
|
- When the configured certificate files do not exist yet, the apache2 role instead uses Debian's default "snakeoil" certificate, resulting in a valid configuration, but using self-signed certificates.
|
||||||
|
- After the ACME challenge has been completed - which can be done with invalid certs - and the Apache2 role is applied a second time, it now configures the certificates issued by Let's Encrypt.
|
||||||
|
|
||||||
|
This can either be achieved by running the playbook from the previous example twice, or by invoking the Apache2 role twice in the same playbook (but in a second play):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- hosts: webserver
|
||||||
|
roles:
|
||||||
|
- s3lph.webserver.apache2
|
||||||
|
- s3lph.webserver.certbot
|
||||||
|
|
||||||
|
- hosts: webserver
|
||||||
|
roles:
|
||||||
|
- s3lph.webserver.apache2
|
||||||
|
```
|
||||||
|
|
15
galaxy.yml
15
galaxy.yml
|
@ -16,7 +16,7 @@ readme: README.md
|
||||||
# A list of the collection's content authors. Can be just the name or in the format 'Full Name <email> (url)
|
# A list of the collection's content authors. Can be just the name or in the format 'Full Name <email> (url)
|
||||||
# @nicks:irc/im.site#channel'
|
# @nicks:irc/im.site#channel'
|
||||||
authors:
|
authors:
|
||||||
- s3lph <account-gitlab-ideynizv@kernelpanic.lol>
|
- s3lph <s3lph@kabelsalat.ch>
|
||||||
|
|
||||||
|
|
||||||
### OPTIONAL but strongly recommended
|
### OPTIONAL but strongly recommended
|
||||||
|
@ -26,14 +26,14 @@ description: Webserver, Apache, Nginx, Lets Encrypt, ACME, Certbot
|
||||||
# Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only
|
# Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only
|
||||||
# accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file'
|
# accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file'
|
||||||
license:
|
license:
|
||||||
- MIT
|
- MIT
|
||||||
|
|
||||||
# A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character
|
# A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character
|
||||||
# requirements as 'namespace' and 'name'
|
# requirements as 'namespace' and 'name'
|
||||||
tags:
|
tags:
|
||||||
|
- application
|
||||||
- webserver
|
- webserver
|
||||||
- apache2
|
- apache2
|
||||||
- nginx
|
|
||||||
- letsencrypt
|
- letsencrypt
|
||||||
- certbot
|
- certbot
|
||||||
|
|
||||||
|
@ -45,20 +45,19 @@ dependencies:
|
||||||
community.general: ">=2.2.0"
|
community.general: ">=2.2.0"
|
||||||
|
|
||||||
# The URL of the originating SCM repository
|
# The URL of the originating SCM repository
|
||||||
repository: https://gitlab.com/s3lph/ansible-collection-webserver
|
repository: https://git.kabelsalat.ch/s3lph/ansible-collection-webserver
|
||||||
|
|
||||||
# The URL to any online docs
|
# The URL to any online docs
|
||||||
documentation: https://gitlab.com/s3lph/ansible-collection-webserver
|
documentation: https://git.kabelsalat.ch/s3lph/ansible-collection-webserver
|
||||||
|
|
||||||
# The URL to the homepage of the collection/project
|
# The URL to the homepage of the collection/project
|
||||||
homepage: https://gitlab.com/s3lph/ansible-collection-webserver
|
homepage: https://git.kabelsalat.ch/s3lph/ansible-collection-webserver
|
||||||
|
|
||||||
# The URL to the collection issue tracker
|
# The URL to the collection issue tracker
|
||||||
issues: https://gitlab.com/s3lph/ansible-collection-webserver/-/issues
|
issues: https://git.kabelsalat.ch/s3lph/ansible-collection-webserver/issues
|
||||||
|
|
||||||
# A list of file glob-like patterns used to filter any files or directories that should not be included in the build
|
# A list of file glob-like patterns used to filter any files or directories that should not be included in the build
|
||||||
# artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This
|
# artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This
|
||||||
# uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry',
|
# uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry',
|
||||||
# and '.git' are always filtered
|
# and '.git' are always filtered
|
||||||
build_ignore: []
|
build_ignore: []
|
||||||
|
|
||||||
|
|
52
meta/runtime.yml
Normal file
52
meta/runtime.yml
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
---
|
||||||
|
# Collections must specify a minimum required ansible version to upload
|
||||||
|
# to galaxy
|
||||||
|
requires_ansible: '>=2.15'
|
||||||
|
|
||||||
|
# Content that Ansible needs to load from another location or that has
|
||||||
|
# been deprecated/removed
|
||||||
|
# plugin_routing:
|
||||||
|
# action:
|
||||||
|
# redirected_plugin_name:
|
||||||
|
# redirect: ns.col.new_location
|
||||||
|
# deprecated_plugin_name:
|
||||||
|
# deprecation:
|
||||||
|
# removal_version: "4.0.0"
|
||||||
|
# warning_text: |
|
||||||
|
# See the porting guide on how to update your playbook to
|
||||||
|
# use ns.col.another_plugin instead.
|
||||||
|
# removed_plugin_name:
|
||||||
|
# tombstone:
|
||||||
|
# removal_version: "2.0.0"
|
||||||
|
# warning_text: |
|
||||||
|
# See the porting guide on how to update your playbook to
|
||||||
|
# use ns.col.another_plugin instead.
|
||||||
|
# become:
|
||||||
|
# cache:
|
||||||
|
# callback:
|
||||||
|
# cliconf:
|
||||||
|
# connection:
|
||||||
|
# doc_fragments:
|
||||||
|
# filter:
|
||||||
|
# httpapi:
|
||||||
|
# inventory:
|
||||||
|
# lookup:
|
||||||
|
# module_utils:
|
||||||
|
# modules:
|
||||||
|
# netconf:
|
||||||
|
# shell:
|
||||||
|
# strategy:
|
||||||
|
# terminal:
|
||||||
|
# test:
|
||||||
|
# vars:
|
||||||
|
|
||||||
|
# Python import statements that Ansible needs to load from another location
|
||||||
|
# import_redirection:
|
||||||
|
# ansible_collections.ns.col.plugins.module_utils.old_location:
|
||||||
|
# redirect: ansible_collections.ns.col.plugins.module_utils.new_location
|
||||||
|
|
||||||
|
# Groups of actions/modules that take a common set of options
|
||||||
|
# action_groups:
|
||||||
|
# group_name:
|
||||||
|
# - module1
|
||||||
|
# - module2
|
3
roles/apache2/README.md
Normal file
3
roles/apache2/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# s3lph.webserver.apache2
|
||||||
|
|
||||||
|
See collection readme for usage info.
|
|
@ -20,7 +20,7 @@ apache2_tls_noacme_fallback_keyfile: /etc/ssl/private/ssl-cert-snakeoil.key
|
||||||
# generated 2024-08-10, Mozilla Guideline v5.7, Apache 2.4.61, OpenSSL 3.0.13, intermediate configuration, no OCSP
|
# generated 2024-08-10, Mozilla Guideline v5.7, Apache 2.4.61, OpenSSL 3.0.13, intermediate configuration, no OCSP
|
||||||
# https://ssl-config.mozilla.org/#server=apache&version=2.4.61&config=intermediate&openssl=3.0.13&ocsp=false&guideline=5.7
|
# https://ssl-config.mozilla.org/#server=apache&version=2.4.61&config=intermediate&openssl=3.0.13&ocsp=false&guideline=5.7
|
||||||
apache2_tls_protocols: "all -SSLv3 -TLSv1 -TLSv1.1"
|
apache2_tls_protocols: "all -SSLv3 -TLSv1 -TLSv1.1"
|
||||||
apache2_tls_ciphersuite: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305"
|
apache2_tls_ciphersuite: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305" # noqa yaml[line-length]
|
||||||
apache2_tls_honor_cipher_order: false
|
apache2_tls_honor_cipher_order: false
|
||||||
apache2_tls_session_tickets: false
|
apache2_tls_session_tickets: false
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
- name: restart apache2
|
- name: Restart Apache2
|
||||||
service:
|
ansible.builtin.service:
|
||||||
name: apache2
|
name: apache2
|
||||||
state: restarted
|
state: restarted
|
||||||
|
|
||||||
- name: reload apache2
|
- name: Reload Apache2
|
||||||
service:
|
ansible.builtin.service:
|
||||||
name: apache2
|
name: apache2
|
||||||
state: reloaded
|
state: reloaded
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
- name: enable apache2 modules
|
- name: Enable Apache2 modules
|
||||||
community.general.apache2_module:
|
community.general.apache2_module:
|
||||||
name: "{{ item }}"
|
name: "{{ item }}"
|
||||||
loop: "{{ apache2_modules }}"
|
loop: "{{ apache2_modules }}"
|
||||||
notify: restart apache2
|
notify: Restart Apache2
|
||||||
|
|
||||||
- meta: flush_handlers
|
- name: Flush handlers
|
||||||
|
ansible.builtin.meta: flush_handlers
|
||||||
|
|
||||||
- name: check for tls keypair existence
|
- name: Check for TLS keypair existence
|
||||||
ansible.builtin.stat:
|
ansible.builtin.stat:
|
||||||
path: "{{ item }}"
|
path: "{{ item }}"
|
||||||
follow: yes
|
follow: true
|
||||||
loop: |
|
loop: |
|
||||||
{%- set files = [] -%}
|
{%- set files = [] -%}
|
||||||
{%- for name, site in apache2_sites.items() -%}
|
{%- for name, site in apache2_sites.items() -%}
|
||||||
|
@ -23,31 +24,33 @@
|
||||||
{{- files | unique | list -}}
|
{{- files | unique | list -}}
|
||||||
register: apache2_register_stat_tls_keypairs
|
register: apache2_register_stat_tls_keypairs
|
||||||
|
|
||||||
- name: create apache document roots
|
- name: Create Apache2 document roots
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ item.documentroot | default(apache2_vhost_documentroot) }}"
|
path: "{{ item.documentroot | default(apache2_vhost_documentroot) }}"
|
||||||
state: directory
|
state: directory
|
||||||
owner: "{{ item.documentroot_owner | default(apache2_vhost_documentroot_owner) }}"
|
owner: "{{ item.documentroot_owner | default(apache2_vhost_documentroot_owner) }}"
|
||||||
group: "{{ item.documentroot_group | default(apache2_vhost_documentroot_group) }}"
|
group: "{{ item.documentroot_group | default(apache2_vhost_documentroot_group) }}"
|
||||||
mode: 0755
|
mode: "0755"
|
||||||
loop: "{{ apache2_sites.values() }}"
|
loop: "{{ apache2_sites.values() }}"
|
||||||
|
|
||||||
- name: render apache site configs
|
- name: Render Apache2 site configs
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: etc/apache2/sites-available/site.conf.j2
|
src: etc/apache2/sites-available/site.conf.j2
|
||||||
dest: "/etc/apache2/sites-available/{{ item.key }}.conf"
|
dest: "/etc/apache2/sites-available/{{ item.key }}.conf"
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: 0644
|
mode: "0644"
|
||||||
vars:
|
vars:
|
||||||
name: "{{ item.key }}"
|
site_name: "{{ item.key }}"
|
||||||
site: "{{ item.value }}"
|
site: "{{ item.value }}"
|
||||||
certfile_exists: "{{ (apache2_register_stat_tls_keypairs.results | selectattr('item', 'equalto', (item.value.tls_certfile | default(apache2_tls_certfile)) ))[0].stat.exists }}"
|
certfile_name: "{{ item.value.tls_certfile | default(apache2_tls_certfile) }}"
|
||||||
keyfile_exists: "{{ (apache2_register_stat_tls_keypairs.results | selectattr('item', 'equalto', (item.value.tls_certfile | default(apache2_tls_keyfile)) ))[0].stat.exists }}"
|
keyfile_name: "{{ item.value.tls_certfile | default(apache2_tls_keyfile) }}"
|
||||||
|
certfile_exists: "{{ (apache2_register_stat_tls_keypairs.results | selectattr('item', 'equalto', certfile_name))[0].stat.exists }}"
|
||||||
|
keyfile_exists: "{{ (apache2_register_stat_tls_keypairs.results | selectattr('item', 'equalto', keyfile_name))[0].stat.exists }}"
|
||||||
loop: "{{ apache2_sites | dict2items }}"
|
loop: "{{ apache2_sites | dict2items }}"
|
||||||
notify: reload apache2
|
notify: Reload Apache2
|
||||||
|
|
||||||
- name: enable apache2 sites
|
- name: Enable Apache2 sites
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "/etc/apache2/sites-enabled/{{ item }}.conf"
|
path: "/etc/apache2/sites-enabled/{{ item }}.conf"
|
||||||
state: link
|
state: link
|
||||||
|
@ -55,4 +58,4 @@
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
loop: "{{ apache2_sites.keys() }}"
|
loop: "{{ apache2_sites.keys() }}"
|
||||||
notify: reload apache2
|
notify: Reload apache2
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
- name: install apache2 and related packages
|
- name: Install Apache2 and related packages
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
name:
|
name:
|
||||||
- apache2
|
- apache2
|
||||||
- ssl-cert # snakeoil cert used for optional tls bootstrapping
|
- ssl-cert # snakeoil cert used for optional tls bootstrapping
|
||||||
|
|
||||||
- name: install apache2 and related packages
|
- name: Install Apache2 and related packages
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
name: "{{ apache2_module_packages }}"
|
name: "{{ apache2_module_packages }}"
|
||||||
|
|
||||||
- name: start and enable apache2
|
- name: Start and enable Apache2
|
||||||
ansible.builtin.service:
|
ansible.builtin.service:
|
||||||
name: apache2
|
name: apache2
|
||||||
state: started
|
state: started
|
||||||
enabled: yes
|
enabled: true
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
- ansible.builtin.import_tasks: install.yml
|
- name: Install Apache2
|
||||||
|
ansible.builtin.import_tasks: install.yml
|
||||||
tags:
|
tags:
|
||||||
- "role::apache2"
|
- "role::apache2"
|
||||||
- "role::apache2:install"
|
- "role::apache2:install"
|
||||||
|
|
||||||
- ansible.builtin.import_tasks: config.yml
|
- name: Configure Apache2
|
||||||
|
ansible.builtin.import_tasks: config.yml
|
||||||
tags:
|
tags:
|
||||||
- "role::apache2"
|
- "role::apache2"
|
||||||
- "role::apache2:config"
|
- "role::apache2:config"
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<VirtualHost *:80>
|
<VirtualHost *:80>
|
||||||
|
|
||||||
ServerAdmin {{ site.serveradmin | default(apache2_vhost_serveradmin) }}
|
ServerAdmin {{ site.serveradmin | default(apache2_vhost_serveradmin) }}
|
||||||
ServerName {{ name }}
|
ServerName {{ site_name }}
|
||||||
{% for alias in site.aliases | default(apache2_vhost_serveraliases) %}
|
{% for alias in site.aliases | default(apache2_vhost_serveraliases) %}
|
||||||
ServerAlias {{ alias }}
|
ServerAlias {{ alias }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
CustomLog {{ site.accesslog | default(apache2_vhost_accesslog) }}
|
CustomLog {{ site.accesslog | default(apache2_vhost_accesslog) }}
|
||||||
|
|
||||||
{% if site.http_redirect_to_https | default(apache2_vhost_http_redirect_to_https) %}
|
{% if site.http_redirect_to_https | default(apache2_vhost_http_redirect_to_https) %}
|
||||||
Redirect permanent / https://{{ name }}/
|
Redirect permanent / https://{{ site_name }}/
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
DocumentRoot {{ site.documentroot | default(apache2_vhost_documentroot) }}
|
DocumentRoot {{ site.documentroot | default(apache2_vhost_documentroot) }}
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
<VirtualHost *:443>
|
<VirtualHost *:443>
|
||||||
|
|
||||||
ServerAdmin {{ site.serveradmin | default(apache2_vhost_serveradmin) }}
|
ServerAdmin {{ site.serveradmin | default(apache2_vhost_serveradmin) }}
|
||||||
ServerName {{ name }}
|
ServerName {{ site_name }}
|
||||||
{% for alias in site.aliases | default(apache2_vhost_serveraliases) %}
|
{% for alias in site.aliases | default(apache2_vhost_serveraliases) %}
|
||||||
ServerAlias {{ alias }}
|
ServerAlias {{ alias }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
3
roles/certbot/README.md
Normal file
3
roles/certbot/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# s3lph.webserver.certbot
|
||||||
|
|
||||||
|
See collection readme for usage info.
|
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
- name: install certbot
|
- name: Install certbot
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
name:
|
name:
|
||||||
- certbot
|
- certbot
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
- name: issue certificates
|
- name: Issue certificates
|
||||||
ansible.builtin.command: >-
|
ansible.builtin.command: >-
|
||||||
/usr/bin/certbot certonly
|
/usr/bin/certbot certonly
|
||||||
--server {{ cert.server | default(certbot_acme_server) }}
|
--server {{ cert.server | default(certbot_acme_server) }}
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
--email {{ cert.email | default(certbot_email) }}
|
--email {{ cert.email | default(certbot_email) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
--cert-name {{ name }}
|
--cert-name {{ cert_name }}
|
||||||
--rsa-key-size {{ cert.rsa_key_size | default(certbot_rsa_key_size) }}
|
--rsa-key-size {{ cert.rsa_key_size | default(certbot_rsa_key_size) }}
|
||||||
|
|
||||||
{% if cert.challenge | default(certbot_challenge) == 'webroot' %}
|
{% if cert.challenge | default(certbot_challenge) == 'webroot' %}
|
||||||
|
@ -20,20 +20,20 @@
|
||||||
--webroot-map '{{ cert.webroot_map | to_json }}'
|
--webroot-map '{{ cert.webroot_map | to_json }}'
|
||||||
{% else %}
|
{% else %}
|
||||||
--webroot {{ cert.webroot }}
|
--webroot {{ cert.webroot }}
|
||||||
{% for domain in cert.domains | default([name]) %}
|
{% for domain in cert.domains | default([cert_name]) %}
|
||||||
--domain {{ domain }}
|
--domain {{ domain }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
--{{ cert.challenge | default(certbot_challenge) }}
|
--{{ cert.challenge | default(certbot_challenge) }}
|
||||||
{{ cert.challenge_freeform_arguments }}
|
{{ cert.challenge_freeform_arguments }}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
args:
|
args:
|
||||||
creates: "/etc/letsencrypt/live/{{ name }}/fullchain.pem"
|
creates: "/etc/letsencrypt/live/{{ cert_name }}/fullchain.pem"
|
||||||
vars:
|
vars:
|
||||||
name: "{{ item.key }}"
|
cert_name: "{{ item.key }}"
|
||||||
cert: "{{ item.value }}"
|
cert: "{{ item.value }}"
|
||||||
loop: "{{ certbot_certificates | dict2items }}"
|
loop: "{{ certbot_certificates | dict2items }}"
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
- ansible.builtin.import_tasks: install.yml
|
- name: Install certbot
|
||||||
|
ansible.builtin.import_tasks: install.yml
|
||||||
tags:
|
tags:
|
||||||
- "role::certbot"
|
- "role::certbot"
|
||||||
- "role::certbot:install"
|
- "role::certbot:install"
|
||||||
|
|
||||||
- ansible.builtin.import_tasks: issue.yml
|
- name: Issue certificates
|
||||||
|
ansible.builtin.import_tasks: issue.yml
|
||||||
tags:
|
tags:
|
||||||
- "role::certbot"
|
- "role::certbot"
|
||||||
- "role::certbot:issue"
|
- "role::certbot:issue"
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
---
|
|
||||||
|
|
||||||
nginx_vhost_serveraliases: []
|
|
||||||
nginx_vhost_documentroot: /var/www/html
|
|
||||||
nginx_vhost_documentroot_owner: www-data
|
|
||||||
nginx_vhost_documentroot_group: www-data
|
|
||||||
|
|
||||||
nginx_vhost_http_enabled: true
|
|
||||||
nginx_vhost_https_enabled: true
|
|
||||||
nginx_vhost_http_redirect_to_https: true
|
|
||||||
|
|
||||||
nginx_tls_certfile: "/etc/letsencrypt/live/{{ inventory_hostname }}/fullchain.pem"
|
|
||||||
nginx_tls_keyfile: "/etc/letsencrypt/live/{{ inventory_hostname }}/privkey.pem"
|
|
||||||
nginx_tls_noacme_fallback_certfile: /etc/ssl/certs/ssl-cert-snakeoil.pem
|
|
||||||
nginx_tls_noacme_fallback_keyfile: /etc/ssl/private/ssl-cert-snakeoil.key
|
|
||||||
|
|
||||||
# generated 2022-02-26, Mozilla Guideline v5.6, nginx 1.18.0, OpenSSL 1.1.1k, intermediate configuration, no OCSP
|
|
||||||
# https://ssl-config.mozilla.org/#server=nginx&version=1.18.0&config=intermediate&openssl=1.1.1k&ocsp=false&guideline=5.6
|
|
||||||
nginx_ssl_protocols: "TLSv1.2 TLSv1.3"
|
|
||||||
nginx_ssl_ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
|
|
||||||
nginx_ssl_prefer_server_ciphers: false
|
|
||||||
nginx_ssl_session_tickets: false
|
|
||||||
|
|
||||||
nginx_modules: []
|
|
||||||
nginx_sites: {}
|
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
|
|
||||||
- name: restart nginx
|
|
||||||
service:
|
|
||||||
name: nginx
|
|
||||||
state: restarted
|
|
||||||
|
|
||||||
- name: reload nginx
|
|
||||||
service:
|
|
||||||
name: nginx
|
|
||||||
state: reloaded
|
|
|
@ -1,62 +0,0 @@
|
||||||
---
|
|
||||||
|
|
||||||
- name: enable nginx modules
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "/etc/nginx/modules-enabled/50-mod-{{ item }}.conf"
|
|
||||||
state: link
|
|
||||||
src: "/usr/share/nginx/modules-available/mod-{{ item }}.conf"
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
loop: "{{ nginx_modules }}"
|
|
||||||
notify: restart nginx
|
|
||||||
|
|
||||||
- ansible.builtin.meta: flush_handlers
|
|
||||||
|
|
||||||
- name: check for tls keypair existence
|
|
||||||
ansible.builtin.stat:
|
|
||||||
path: "{{ item }}"
|
|
||||||
follow: yes
|
|
||||||
loop: |
|
|
||||||
{%- set files = [] -%}
|
|
||||||
{%- for name, site in nginx_sites.items() -%}
|
|
||||||
{%- if site.https_enabled | default(nginx_vhost_https_enabled) -%}
|
|
||||||
{%- set _x = files.append(site.tls_certfile | default(nginx_tls_certfile)) -%}
|
|
||||||
{%- set _x = files.append(site.tls_keyfile | default(nginx_tls_keyfile)) -%}
|
|
||||||
{%- endif -%}
|
|
||||||
{%- endfor -%}
|
|
||||||
{{- files | unique | list -}}
|
|
||||||
register: nginx_register_stat_tls_keypairs
|
|
||||||
|
|
||||||
- name: create nginx document roots
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "{{ item.documentroot | default(nginx_vhost_documentroot) }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ item.documentroot_owner | default(nginx_vhost_documentroot_owner) }}"
|
|
||||||
group: "{{ item.documentroot_group | default(nginx_vhost_documentroot_group) }}"
|
|
||||||
mode: 0755
|
|
||||||
loop: "{{ nginx_sites.values() }}"
|
|
||||||
|
|
||||||
- name: render nginx site configs
|
|
||||||
ansible.builtin.template:
|
|
||||||
src: etc/nginx/sites-available/site.conf.j2
|
|
||||||
dest: "/etc/nginx/sites-available/{{ item.key }}.conf"
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: 0644
|
|
||||||
vars:
|
|
||||||
name: "{{ item.key }}"
|
|
||||||
site: "{{ item.value }}"
|
|
||||||
certfile_exists: "{{ (nginx_register_stat_tls_keypairs.results | selectattr('item', 'equalto', (item.value.tls_certfile | default(nginx_tls_certfile)) ))[0].stat.exists }}"
|
|
||||||
keyfile_exists: "{{ (nginx_register_stat_tls_keypairs.results | selectattr('item', 'equalto', (item.value.tls_certfile | default(nginx_tls_keyfile)) ))[0].stat.exists }}"
|
|
||||||
loop: "{{ nginx_sites | dict2items }}"
|
|
||||||
notify: reload nginx
|
|
||||||
|
|
||||||
- name: enable nginx sites
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "/etc/nginx/sites-enabled/{{ item }}.conf"
|
|
||||||
state: link
|
|
||||||
src: "/etc/nginx/sites-available/{{ item }}.conf"
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
loop: "{{ nginx_sites.keys() }}"
|
|
||||||
notify: reload nginx
|
|
|
@ -1,22 +0,0 @@
|
||||||
---
|
|
||||||
|
|
||||||
- name: install nginx and related packages
|
|
||||||
ansible.builtin.apt:
|
|
||||||
name:
|
|
||||||
- nginx
|
|
||||||
- ssl-cert # snakeoil cert used for optional tls bootstrapping
|
|
||||||
|
|
||||||
- name: deploy ffdhe2048 dhparams
|
|
||||||
ansible.builtin.template:
|
|
||||||
src: etc/nginx/dh_param
|
|
||||||
dest: /etc/nginx/dh_param
|
|
||||||
owner: root
|
|
||||||
group: root
|
|
||||||
mode: 0644
|
|
||||||
notify: reload nginx
|
|
||||||
|
|
||||||
- name: start and enable nginx
|
|
||||||
ansible.builtin.service:
|
|
||||||
name: nginx
|
|
||||||
state: started
|
|
||||||
enabled: yes
|
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
|
|
||||||
- ansible.builtin.import_tasks: install.yml
|
|
||||||
tags:
|
|
||||||
- "role::nginx"
|
|
||||||
- "role::nginx:install"
|
|
||||||
|
|
||||||
- ansible.builtin.import_tasks: config.yml
|
|
||||||
tags:
|
|
||||||
- "role::nginx"
|
|
||||||
- "role::nginx:config"
|
|
|
@ -1,8 +0,0 @@
|
||||||
-----BEGIN DH PARAMETERS-----
|
|
||||||
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
|
|
||||||
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
|
|
||||||
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
|
|
||||||
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
|
|
||||||
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
|
|
||||||
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
|
|
||||||
-----END DH PARAMETERS-----
|
|
|
@ -1,65 +0,0 @@
|
||||||
{{ ansible_managed | comment }}
|
|
||||||
|
|
||||||
user www-data;
|
|
||||||
worker_processes auto;
|
|
||||||
pid /run/nginx.pid;
|
|
||||||
include /etc/nginx/modules-enabled/*.conf;
|
|
||||||
|
|
||||||
events {
|
|
||||||
worker_connections 768;
|
|
||||||
# multi_accept on;
|
|
||||||
}
|
|
||||||
|
|
||||||
http {
|
|
||||||
|
|
||||||
##
|
|
||||||
# Basic Settings
|
|
||||||
##
|
|
||||||
|
|
||||||
sendfile on;
|
|
||||||
tcp_nopush on;
|
|
||||||
types_hash_max_size 2048;
|
|
||||||
# server_tokens off;
|
|
||||||
|
|
||||||
# server_names_hash_bucket_size 64;
|
|
||||||
# server_name_in_redirect off;
|
|
||||||
|
|
||||||
include /etc/nginx/mime.types;
|
|
||||||
default_type application/octet-stream;
|
|
||||||
|
|
||||||
##
|
|
||||||
# SSL Settings
|
|
||||||
##
|
|
||||||
|
|
||||||
ssl_dhparam /etc/nginx/dh_param;
|
|
||||||
ssl_protocols {{ nginx_ssl_protocols }};
|
|
||||||
ssl_ciphers {{ nginx_ssl_ciphers }};
|
|
||||||
ssl_prefer_server_ciphers {{ nginx_ssl_prefer_server_ciphers }};
|
|
||||||
|
|
||||||
##
|
|
||||||
# Logging Settings
|
|
||||||
##
|
|
||||||
|
|
||||||
access_log /var/log/nginx/access.log;
|
|
||||||
error_log /var/log/nginx/error.log;
|
|
||||||
|
|
||||||
##
|
|
||||||
# Gzip Settings
|
|
||||||
##
|
|
||||||
|
|
||||||
gzip on;
|
|
||||||
|
|
||||||
# gzip_vary on;
|
|
||||||
# gzip_proxied any;
|
|
||||||
# gzip_comp_level 6;
|
|
||||||
# gzip_buffers 16 8k;
|
|
||||||
# gzip_http_version 1.1;
|
|
||||||
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
|
||||||
|
|
||||||
##
|
|
||||||
# Virtual Host Configs
|
|
||||||
##
|
|
||||||
|
|
||||||
include /etc/nginx/conf.d/*.conf;
|
|
||||||
include /etc/nginx/sites-enabled/*;
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
{{ ansible_managed | comment }}
|
|
||||||
|
|
||||||
{% if site.http_enabled | default(nginx_vhost_http_enabled) %}
|
|
||||||
server {
|
|
||||||
listen 80{% if site.default_server | default(false) %} default_server{% endif %};
|
|
||||||
listen [::]:80{% if site.default_server | default(false) %} default_server{% endif %};
|
|
||||||
|
|
||||||
server_name {{ name }}{% for alias in site.aliases | default(nginx_vhost_serveraliases) %} {{ alias }}{% endfor %};
|
|
||||||
|
|
||||||
{% if site.http_redirect_to_https | default(nginx_vhost_http_redirect_to_https) %}
|
|
||||||
location / {
|
|
||||||
return 301 https://$host$request_uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
root {{ site.documentroot | default(nginx_vhost_documentroot) }};
|
|
||||||
|
|
||||||
{% if site.php_proxy is defined %}
|
|
||||||
location ~ \.php$ {
|
|
||||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
|
||||||
fastcgi_pass {{ site.php_proxy }};
|
|
||||||
fastcgi_index index.php;
|
|
||||||
include fastcgi_params;
|
|
||||||
}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{{ site.additional_config | indent(4) }}
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
{% if site.https_enabled | default(nginx_vhost_https_enabled) %}
|
|
||||||
server {
|
|
||||||
listen 443 ssl http2{% if site.default_server | default(false) %} default_server{% endif %};
|
|
||||||
listen [::]:443 ssl http2{% if site.default_server | default(false) %} default_server{% endif %};
|
|
||||||
|
|
||||||
server_name {{ name }}{% for alias in site.aliases | default(nginx_vhost_serveraliases) %} {{ alias }}{% endfor %};
|
|
||||||
|
|
||||||
{% if certfile_exists and keyfile_exists %}
|
|
||||||
ssl_certificate {{ site.tls_certfile | default(nginx_tls_certfile) }};
|
|
||||||
ssl_certificate_key {{ site.tls_keyfile | default(nginx_tls_keyfile) }};
|
|
||||||
{% else %}
|
|
||||||
ssl_certificate {{ nginx_tls_noacme_fallback_certfile }};
|
|
||||||
ssl_certificate_key {{ nginx_tls_noacme_fallback_keyfile }};
|
|
||||||
{% endif %}
|
|
||||||
ssl_session_timeout 1d;
|
|
||||||
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
|
|
||||||
ssl_session_tickets off;
|
|
||||||
|
|
||||||
root {{ site.documentroot | default(nginx_vhost_documentroot) }};
|
|
||||||
|
|
||||||
{% if site.php_proxy is defined %}
|
|
||||||
location ~ \.php$ {
|
|
||||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
|
||||||
fastcgi_pass {{ site.php_proxy }};
|
|
||||||
fastcgi_index index.php;
|
|
||||||
include fastcgi_params;
|
|
||||||
}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{{ site.additional_config | indent(4) }}
|
|
||||||
|
|
||||||
}
|
|
||||||
{% endif %}
|
|
Loading…
Reference in a new issue