From 580794479df4c7029ca8365f620b7235b46afaa1 Mon Sep 17 00:00:00 2001 From: Viktor Berke Date: Fri, 17 Nov 2023 17:09:20 +0100 Subject: [PATCH] Initial version --- .ansible-lint | 15 +++++ .gitattributes | 1 + .githooks/pre-commit | 4 ++ .github/workflows/almalinux-8.yml | 25 +++++++ .github/workflows/almalinux-9.yml | 25 +++++++ .github/workflows/fedora-38.yml | 25 +++++++ .github/workflows/fedora-39.yml | 25 +++++++ .github/workflows/lint.yml | 23 +++++++ .github/workflows/ubuntu-18.04.yml | 25 +++++++ .github/workflows/ubuntu-20.04.yml | 25 +++++++ .github/workflows/ubuntu-22.04.yml | 25 +++++++ .yamllint | 10 +++ LICENSE | 21 ++++++ README.md | 77 ++++++++++++++++++++++ handlers/main.yml | 4 ++ meta/main.yml | 9 +++ meta/requirements.yml | 2 + requirements.yml | 2 + tasks/config.yml | 38 +++++++++++ tasks/host.yml | 47 ++++++++++++++ tasks/install.yml | 15 +++++ tasks/main.yml | 8 +++ templates/dos.conf.j2 | 14 ++++ templates/host.conf.j2 | 90 ++++++++++++++++++++++++++ templates/nginx.conf.j2 | 52 +++++++++++++++ templates/noobient-nginx.cil.j2 | 6 ++ templates/noobient-nginx_custom.cil.j2 | 8 +++ templates/php.conf.j2 | 10 +++ templates/ssl.conf.j2 | 30 +++++++++ tests/main.yml | 48 ++++++++++++++ vars/main.yml | 36 +++++++++++ 31 files changed, 745 insertions(+) create mode 100644 .ansible-lint create mode 100644 .gitattributes create mode 100755 .githooks/pre-commit create mode 100644 .github/workflows/almalinux-8.yml create mode 100644 .github/workflows/almalinux-9.yml create mode 100644 .github/workflows/fedora-38.yml create mode 100644 .github/workflows/fedora-39.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/ubuntu-18.04.yml create mode 100644 .github/workflows/ubuntu-20.04.yml create mode 100644 .github/workflows/ubuntu-22.04.yml create mode 100644 .yamllint create mode 100644 LICENSE create mode 100644 README.md create mode 100644 handlers/main.yml create mode 100644 meta/main.yml create mode 100644 meta/requirements.yml create mode 100644 requirements.yml create mode 100644 tasks/config.yml create mode 100644 tasks/host.yml create mode 100644 tasks/install.yml create mode 100644 tasks/main.yml create mode 100644 templates/dos.conf.j2 create mode 100644 templates/host.conf.j2 create mode 100644 templates/nginx.conf.j2 create mode 100644 templates/noobient-nginx.cil.j2 create mode 100644 templates/noobient-nginx_custom.cil.j2 create mode 100644 templates/php.conf.j2 create mode 100644 templates/ssl.conf.j2 create mode 100644 tests/main.yml create mode 100644 vars/main.yml diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 0000000..9f3d841 --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,15 @@ +--- +skip_list: + - git-latest + - package-latest + - fqcn[action] + - fqcn[action-core] + - yaml[comments] # TODO how do I restrict this to just require-starting-space? + - yaml[line-length] + - name # this is covered by yamllint with exceptions on include_role + - risky-shell-pipe + - schema[tasks] # TODO ansible-lint randomly complains, e.g. it/tftpd + - role-name + - schema[meta] # TODO ansible-lint believes the official dependency syntax is wrong + - meta-no-info # we leave platform empty + - var-naming[no-role-prefix] # warns even for include_role vars diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..38957dd --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.yml linguist-detectable=true diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..af9e9c9 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,4 @@ +#!/bin/bash + +yamllint --strict . +ansible-lint --strict --offline diff --git a/.github/workflows/almalinux-8.yml b/.github/workflows/almalinux-8.yml new file mode 100644 index 0000000..d991c72 --- /dev/null +++ b/.github/workflows/almalinux-8.yml @@ -0,0 +1,25 @@ +--- +name: AlmaLinux 8 + +on: # yamllint disable-line rule:truthy + push: + branches: + - main + - master + pull_request: + branches: [] + +jobs: + ansible-ci: + runs-on: ubuntu-latest + steps: + - name: Obtain sources + uses: actions/checkout@v3 + - name: Test Galaxy role + run: > + CONT_ID=$(podman run --rm -v ${{ github.workspace }}:/repo -v /sys/fs/cgroup:/sys/fs/cgroup:ro + --tmpfs /tmp --tmpfs /run --privileged --detach bviktor/ansible-systemd-almalinux:8) && + podman exec ${CONT_ID} /bin/bash -c + "if [ -f requirements.yml ]; then ansible-galaxy role install --force -r requirements.yml -p ..; fi && + ANSIBLE_ROLES_PATH=.. ANSIBLE_FORCE_COLOR=true ansible-playbook tests/main.yml" && + podman stop ${CONT_ID} diff --git a/.github/workflows/almalinux-9.yml b/.github/workflows/almalinux-9.yml new file mode 100644 index 0000000..196acff --- /dev/null +++ b/.github/workflows/almalinux-9.yml @@ -0,0 +1,25 @@ +--- +name: AlmaLinux 9 + +on: # yamllint disable-line rule:truthy + push: + branches: + - main + - master + pull_request: + branches: [] + +jobs: + ansible-ci: + runs-on: ubuntu-latest + steps: + - name: Obtain sources + uses: actions/checkout@v3 + - name: Test Galaxy role + run: > + CONT_ID=$(podman run --rm -v ${{ github.workspace }}:/repo -v /sys/fs/cgroup:/sys/fs/cgroup:ro + --tmpfs /tmp --tmpfs /run --privileged --detach bviktor/ansible-systemd-almalinux:9) && + podman exec ${CONT_ID} /bin/bash -c + "if [ -f requirements.yml ]; then ansible-galaxy role install --force -r requirements.yml -p ..; fi && + ANSIBLE_ROLES_PATH=.. ANSIBLE_FORCE_COLOR=true ansible-playbook tests/main.yml" && + podman stop ${CONT_ID} diff --git a/.github/workflows/fedora-38.yml b/.github/workflows/fedora-38.yml new file mode 100644 index 0000000..1c250c7 --- /dev/null +++ b/.github/workflows/fedora-38.yml @@ -0,0 +1,25 @@ +--- +name: Fedora 38 + +on: # yamllint disable-line rule:truthy + push: + branches: + - main + - master + pull_request: + branches: [] + +jobs: + ansible-ci: + runs-on: ubuntu-latest + steps: + - name: Obtain sources + uses: actions/checkout@v3 + - name: Test Galaxy role + run: > + CONT_ID=$(podman run --rm -v ${{ github.workspace }}:/repo -v /sys/fs/cgroup:/sys/fs/cgroup:ro + --tmpfs /tmp --tmpfs /run --privileged --detach bviktor/ansible-systemd-fedora:38) && + podman exec ${CONT_ID} /bin/bash -c + "if [ -f requirements.yml ]; then ansible-galaxy role install --force -r requirements.yml -p ..; fi && + ANSIBLE_ROLES_PATH=.. ANSIBLE_FORCE_COLOR=true ansible-playbook tests/main.yml" && + podman stop ${CONT_ID} diff --git a/.github/workflows/fedora-39.yml b/.github/workflows/fedora-39.yml new file mode 100644 index 0000000..db9ace4 --- /dev/null +++ b/.github/workflows/fedora-39.yml @@ -0,0 +1,25 @@ +--- +name: Fedora 39 + +on: # yamllint disable-line rule:truthy + push: + branches: + - main + - master + pull_request: + branches: [] + +jobs: + ansible-ci: + runs-on: ubuntu-latest + steps: + - name: Obtain sources + uses: actions/checkout@v3 + - name: Test Galaxy role + run: > + CONT_ID=$(podman run --rm -v ${{ github.workspace }}:/repo -v /sys/fs/cgroup:/sys/fs/cgroup:ro + --tmpfs /tmp --tmpfs /run --privileged --detach bviktor/ansible-systemd-fedora:39) && + podman exec ${CONT_ID} /bin/bash -c + "if [ -f requirements.yml ]; then ansible-galaxy role install --force -r requirements.yml -p ..; fi && + ANSIBLE_ROLES_PATH=.. ANSIBLE_FORCE_COLOR=true ansible-playbook tests/main.yml" && + podman stop ${CONT_ID} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..f8f9a45 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,23 @@ +--- +name: Lint + +on: # yamllint disable-line rule:truthy + push: + branches: + - main + - master + pull_request: + branches: [] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Obtain sources + uses: actions/checkout@v3 + - name: Lint sources + run: > + podman run --rm -v ${{ github.workspace }}:/repo bviktor/ansible-systemd-lint:latest /bin/bash -c + "git config --global --add safe.directory /repo && + yamllint --strict . && + ansible-lint --strict" diff --git a/.github/workflows/ubuntu-18.04.yml b/.github/workflows/ubuntu-18.04.yml new file mode 100644 index 0000000..ae7454a --- /dev/null +++ b/.github/workflows/ubuntu-18.04.yml @@ -0,0 +1,25 @@ +--- +name: Ubuntu 18.04 + +on: # yamllint disable-line rule:truthy + push: + branches: + - main + - master + pull_request: + branches: [] + +jobs: + ansible-ci: + runs-on: ubuntu-latest + steps: + - name: Obtain sources + uses: actions/checkout@v3 + - name: Test Galaxy role + run: > + CONT_ID=$(podman run --rm -v ${{ github.workspace }}:/repo -v /sys/fs/cgroup:/sys/fs/cgroup:ro + --tmpfs /tmp --tmpfs /run --privileged --detach bviktor/ansible-systemd-ubuntu:18.04) && + podman exec ${CONT_ID} /bin/bash -c + "if [ -f requirements.yml ]; then ansible-galaxy role install --force -r requirements.yml -p ..; fi && + ANSIBLE_ROLES_PATH=.. ANSIBLE_FORCE_COLOR=true ansible-playbook tests/main.yml" && + podman stop ${CONT_ID} diff --git a/.github/workflows/ubuntu-20.04.yml b/.github/workflows/ubuntu-20.04.yml new file mode 100644 index 0000000..0f6b8b7 --- /dev/null +++ b/.github/workflows/ubuntu-20.04.yml @@ -0,0 +1,25 @@ +--- +name: Ubuntu 20.04 + +on: # yamllint disable-line rule:truthy + push: + branches: + - main + - master + pull_request: + branches: [] + +jobs: + ansible-ci: + runs-on: ubuntu-latest + steps: + - name: Obtain sources + uses: actions/checkout@v3 + - name: Test Galaxy role + run: > + CONT_ID=$(podman run --rm -v ${{ github.workspace }}:/repo -v /sys/fs/cgroup:/sys/fs/cgroup:ro + --tmpfs /tmp --tmpfs /run --privileged --detach bviktor/ansible-systemd-ubuntu:20.04) && + podman exec ${CONT_ID} /bin/bash -c + "if [ -f requirements.yml ]; then ansible-galaxy role install --force -r requirements.yml -p ..; fi && + ANSIBLE_ROLES_PATH=.. ANSIBLE_FORCE_COLOR=true ansible-playbook tests/main.yml" && + podman stop ${CONT_ID} diff --git a/.github/workflows/ubuntu-22.04.yml b/.github/workflows/ubuntu-22.04.yml new file mode 100644 index 0000000..72340a4 --- /dev/null +++ b/.github/workflows/ubuntu-22.04.yml @@ -0,0 +1,25 @@ +--- +name: Ubuntu 22.04 + +on: # yamllint disable-line rule:truthy + push: + branches: + - main + - master + pull_request: + branches: [] + +jobs: + ansible-ci: + runs-on: ubuntu-latest + steps: + - name: Obtain sources + uses: actions/checkout@v3 + - name: Test Galaxy role + run: > + CONT_ID=$(podman run --rm -v ${{ github.workspace }}:/repo -v /sys/fs/cgroup:/sys/fs/cgroup:ro + --tmpfs /tmp --tmpfs /run --privileged --detach bviktor/ansible-systemd-ubuntu:22.04) && + podman exec ${CONT_ID} /bin/bash -c + "if [ -f requirements.yml ]; then ansible-galaxy role install --force -r requirements.yml -p ..; fi && + ANSIBLE_ROLES_PATH=.. ANSIBLE_FORCE_COLOR=true ansible-playbook tests/main.yml" && + podman stop ${CONT_ID} diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..2c89427 --- /dev/null +++ b/.yamllint @@ -0,0 +1,10 @@ +--- +extends: default + +rules: + braces: + max-spaces-inside: 1 + comments: + min-spaces-from-content: 1 + require-starting-space: false + line-length: disable diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bb9fb34 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Viktor Berke + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4094031 --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# noobient.nginx + +## Synopsys + +This role installs nginx and configures hosts. + +## Parameters + +| Name | Required | Example | Description | +|---|---|---|---| +| `domain` | yes | `foo.com` | Domain to host. | +| `mode` | yes | `wordpress` | Hosting mode. Possible values are `dirlist`, `php`, `proxy`, `redirect`, `static`, `wordpress`. | +| `path` | no | `/var/www/html/noobient.com` | Document root. Defaults to `/var/www/html/` for `php`, `static`, and `wordpress`, ignored otherwise. | +| `www_mode` | no | `redirect` | Possible values are `redirect` and `serve`, to redirect `www.` requests to `` or serve them as is, respectively. Defaults to `redirect`. Ignored when `mode` is set to `redirect`. | +| `new_domain` | no | `foobar.com` | New domain to redirect to. Mandatory for `redirect`, ignored otherwise. | +| `ssl_disabled` | no | `true` | `true` or `false`. Defaults to `false`. | +| `ssl_key` | no | `/etc/acme/noobient.com/noobient.com.key` | Full path to SSL key file. Defaults to `/etc/acme//.key`. Ignored if `ssl_disabled` is `true`. | +| `ssl_cert` | no | `/etc/acme/noobient.com/fullchain.cer` | Full path to SSL full chain file. Defaults to `/etc/acme//fulllchain.cer`. Ignored if `ssl_disabled` is `true`. | +| `host_port` | no | `8888` | Listen on custom port. Defaults to `80` when `ssl_disabled` is `true`, or `443` otherwise. | +| `proxy_port` | no | `9999` | Port of the app being proxied, when `mode` is set to `proxy`, ignored otherwise. Defaults to `8080`. | + +## Examples + +```yml +# Barebones +- include_role: + name: noobient.nginx + vars: + domain: foo.com + mode: wordpress + +# Serve static pages both on foo.com and www.foo.com, with custom content path +- include_role: + name: noobient.nginx + vars: + domain: foo.com + mode: static + path: /data/content/foo.com + www_mode: serve + +# Redirect foo.com and www.foo.com to bar.com without SSL +- include_role: + name: noobient.nginx + vars: + domain: foo.com + mode: redirect + new_domain: bar.com + ssl_disabled: true + +# Proxy the app running on port 9999, use custom SSL paths, and serve on port 8888 +- include_role: + name: noobient.nginx + vars: + domain: foo.com + mode: proxy + ssl_key: /opt/acme/privkey.pem + ssl_cert: /opt/acme/fullchain.pem + host_port: 8888 + proxy_port: 9999 +``` + +## Return Values + +N/A + +## Support + +| Platform | Support | Status | +|---|---|---| +| Linter | ✅ | [![Lint](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/lint.yml/badge.svg)](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/lint.yml) | +| AlmaLinux 8 | ✅ | [![AlmaLinux 8](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/almalinux-8.yml/badge.svg)](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/almalinux-8.yml) | +| AlmaLinux 9 | ✅ | [![AlmaLinux 9](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/almalinux-9.yml/badge.svg)](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/almalinux-9.yml) | +| Fedora 38 | ✅ | [![Fedora 38](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/fedora-38.yml/badge.svg)](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/fedora-38.yml) | +| Fedora 39 | ✅ | [![Fedora 39](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/fedora-39.yml/badge.svg)](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/fedora-39.yml) | +| Ubuntu 18.04 | ✅ | [![Ubuntu 18.04](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/ubuntu-18.04.yml/badge.svg)](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/ubuntu-18.04.yml) | +| Ubuntu 20.04 | ✅ | [![Ubuntu 20.04](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/ubuntu-20.04.yml/badge.svg)](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/ubuntu-20.04.yml) | +| Ubuntu 22.04 | ✅ | [![Ubuntu 22.04](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/ubuntu-22.04.yml/badge.svg)](https://github.com/noobient/ansible-galaxy-nginx/actions/workflows/ubuntu-22.04.yml) | diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..970f2b3 --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,4 @@ +--- +- name: Reload nginx # noqa no-changed-when + shell: + cmd: nginx -t && systemctl restart nginx.service diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..679f032 --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,9 @@ +--- +galaxy_info: + role_name: nginx + author: Viktor Berke + description: Install and configure nginx + license: license (MIT) + min_ansible_version: '2.10' + platforms: [] + galaxy_tags: [] diff --git a/meta/requirements.yml b/meta/requirements.yml new file mode 100644 index 0000000..7604758 --- /dev/null +++ b/meta/requirements.yml @@ -0,0 +1,2 @@ +--- +- name: noobient.selinux_cil diff --git a/requirements.yml b/requirements.yml new file mode 100644 index 0000000..10b1ddd --- /dev/null +++ b/requirements.yml @@ -0,0 +1,2 @@ +--- +- src: noobient.selinux_cil diff --git a/tasks/config.yml b/tasks/config.yml new file mode 100644 index 0000000..399307c --- /dev/null +++ b/tasks/config.yml @@ -0,0 +1,38 @@ +--- +# dsaparam: https://security.stackexchange.com/questions/95178/diffie-hellman-parameters-still-calculating-after-24-hours +- name: Create dhparam file + command: + cmd: "openssl dhparam -dsaparam -out {{ nginx_root }}/dh4096.pem 4096" + creates: "{{ nginx_root }}/dh4096.pem" + notify: Reload nginx + +- name: Deploy nginx config files + template: + src: "{{ ng_conf_item }}.j2" + dest: "{{ nginx_root }}/{{ ng_conf_item }}" + owner: root + group: root + mode: '0644' + backup: true + loop: + - dos.conf + - nginx.conf + - php.conf + - ssl.conf + loop_control: + loop_var: ng_conf_item + notify: Reload nginx + +- name: Create enabled host folder + file: + path: "{{ nginx_root }}/conf.d-enabled" + state: directory + owner: root + group: root + mode: '0755' + +- include_role: + name: noobient.selinux_cil + vars: + module: noobient-nginx + register: noobient_nginx_configured diff --git a/tasks/host.yml b/tasks/host.yml new file mode 100644 index 0000000..08a2c29 --- /dev/null +++ b/tasks/host.yml @@ -0,0 +1,47 @@ +--- +- name: "Deploy {{ domain }} host config" + template: + src: host.conf.j2 + dest: "{{ nginx_root }}/conf.d/{{ domain }}.conf" + owner: root + group: root + mode: '0644' + backup: true + notify: Reload nginx + +- name: Check for SELinux port definition conflicts + shell: + cmd: "semanage port -l | grep ' tcp ' | grep -P ' {{ se_conflict_item }}(,| |$)' | awk '{print $1}'" + changed_when: false + failed_when: false + loop: + - "{{ eff_host_port }}" + - "{{ eff_proxy_port }}" + loop_control: + loop_var: se_conflict_item + register: se_conflict_check + when: (eff_proxy_port | string not in stock_http_cache_ports | string) or (eff_host_port | string not in stock_http_ports | string) + +- debug: + msg: "{% if se_conflict_output.stdout | length and se_conflict_output.stdout != 'http_port_t' and se_conflict_output.stdout != 'http_cache_port_t' %}Warning: port {{ se_conflict_output.se_conflict_item }} seems to conflict with the definition of '{{ se_conflict_output.stdout }}', module installation will likely fail.{% else %}No SELinux port conflict detected.{% endif %}" + loop: "{{ se_conflict_check.results }}" + loop_control: + loop_var: se_conflict_output + label: "{{ se_conflict_output.se_conflict_item }}" + when: not se_conflict_check.skipped + +- include_role: + name: noobient.selinux_cil + vars: + module: "noobient-nginx_{{ domain }}" + custom_src: noobient-nginx_custom + when: (eff_proxy_port | string not in stock_http_cache_ports | string) or (eff_host_port | string not in stock_http_ports | string) + +- name: "Enable {{ domain }} host" + file: + src: "{{ nginx_root }}/conf.d/{{ domain }}.conf" + dest: "{{ nginx_root }}/conf.d-enabled/{{ domain }}.conf" + state: link + owner: root + group: root + notify: Reload nginx diff --git a/tasks/install.yml b/tasks/install.yml new file mode 100644 index 0000000..0e72982 --- /dev/null +++ b/tasks/install.yml @@ -0,0 +1,15 @@ +--- +- name: Install nginx and OpenSSL + package: + name: "{{ packages }}" + state: latest + vars: + packages: + - nginx + - openssl + +- name: Enable nginx service + systemd: + name: nginx.service + enabled: true + register: noobient_nginx_installed diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..98172d5 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,8 @@ +--- +- include_tasks: install.yml + when: noobient_nginx_installed is not defined + +- include_tasks: config.yml + when: noobient_nginx_configured is not defined + +- include_tasks: host.yml diff --git a/templates/dos.conf.j2 b/templates/dos.conf.j2 new file mode 100644 index 0000000..934934d --- /dev/null +++ b/templates/dos.conf.j2 @@ -0,0 +1,14 @@ +geo $whitelist +{ + default 0; +} + +map $whitelist $limit +{ + 0 $binary_remote_addr; + 1 ""; +} + +# 10m is enough for around 160k requests +limit_req_zone $limit zone=one:20m rate=100r/m; +limit_req zone=one burst=100 nodelay; diff --git a/templates/host.conf.j2 b/templates/host.conf.j2 new file mode 100644 index 0000000..580afd1 --- /dev/null +++ b/templates/host.conf.j2 @@ -0,0 +1,90 @@ +{% if mode == 'redirect' or eff_www_mode == 'redirect' %} +server +{ +{% if not eff_ssl_disabled | bool %} + listen {{ eff_host_port }} ssl http2; + listen [::]:{{ eff_host_port }} ssl http2; + include ssl.conf; + ssl_certificate {{ eff_ssl_cert }}; + ssl_certificate_key {{ eff_ssl_key }}; +{% else %} + listen {{ eff_host_port }}; + listen [::]:{{ eff_host_port }}; +{% endif %} + +{% if mode == 'redirect' %} + server_name {{ domain }} www.{{ domain }}; + return 301 http{% if not eff_ssl_disabled | bool %}s{% endif %}://{{ new_domain }}$request_uri; +{% else %} + server_name www.{{ domain }}; + return 301 http{% if not eff_ssl_disabled | bool %}s{% endif %}://{{ domain }}$request_uri; +{% endif %} +} + +{% endif %} +{% if mode == 'proxy' %} +upstream appserver +{ + server 127.0.0.1:{{ eff_proxy_port }} fail_timeout=0; +} + +{% endif %} +{% if mode != 'redirect' %} +server +{ +{% if not eff_ssl_disabled | bool %} + listen {{ eff_host_port }} ssl http2; + listen [::]:{{ eff_host_port }} ssl http2; + include ssl.conf; + ssl_certificate {{ eff_ssl_cert }}; + ssl_certificate_key {{ eff_ssl_key }}; +{% else %} + listen {{ eff_host_port }}; + listen [::]:{{ eff_host_port }}; +{% endif %} + + server_name {{ domain }}{% if eff_www_mode == 'serve' %} www.{{ domain }}{% endif %}; + root {{ eff_path }}; + +{% if mode == 'php' or mode == 'wordpress' %} + include php.conf; +{% if mode == 'wordpress' %} + # wordpress-specific + if (!-e $request_filename) { + rewrite ^.*$ /index.php last; + } +{% endif %} + +{% elif mode == 'proxy' %} + location / + { + proxy_read_timeout 3600; + + # websocket proxy + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://appserver; + + proxy_max_temp_file_size 0; + client_body_buffer_size 64k; + } + +{% elif mode == 'static' %} + index index.html index.htm; + +{% elif mode == 'dirlist' %} + # yeah it's stupid, but there's no other way to disable + index WE_DONT_NEED_NO_INDEX; + autoindex on; + +{% endif %} + # this allows us to upload and download files without limiting their size + client_max_body_size 0; +} +{% endif %} diff --git a/templates/nginx.conf.j2 b/templates/nginx.conf.j2 new file mode 100644 index 0000000..63b788d --- /dev/null +++ b/templates/nginx.conf.j2 @@ -0,0 +1,52 @@ +# avoid root, it's unnecessary +user {% if ansible_pkg_mgr == 'dnf' %}nginx{% else %}www-data{% endif %}; +# start processes according to the number of cores +worker_processes auto; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events +{ + worker_connections 1024; +} + +http +{ + include /etc/nginx/mime.types; + default_type application/octet-stream; + + map $remote_addr $remote_addr_ip_proto + { + default "IPv4"; + ~: "IPv6"; + } + + log_format main '$time_local | ' + '$remote_addr_ip_proto | ' + '$server_protocol | ' + '$ssl_protocol | ' + '$ssl_cipher | ' + '$status | ' + '$remote_addr | ' + '$body_bytes_sent | ' + '$request_method "$scheme://$host$request_uri" | ' + '"$http_referer" | ' + '"$http_user_agent"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + keepalive_timeout 65; + server_tokens off; + types_hash_max_size 4096; + proxy_buffers 32 32k; + proxy_buffer_size 32k; + + # speed up the traffic, we have enough CPU power + gzip on; + + include dos.conf; + include /etc/nginx/conf.d-enabled/*.conf; +} diff --git a/templates/noobient-nginx.cil.j2 b/templates/noobient-nginx.cil.j2 new file mode 100644 index 0000000..9887924 --- /dev/null +++ b/templates/noobient-nginx.cil.j2 @@ -0,0 +1,6 @@ +; Allow httpd_t to serve requests at all +(allow httpd_t http_port_t (tcp_socket (name_connect))) +; Allow httpd_t to connect to MySQL +(allow httpd_t mysqld_port_t (tcp_socket (name_connect))) +; Allow httpd_t to proxy upstream servers +(allow httpd_t http_cache_port_t (tcp_socket (name_connect))) diff --git a/templates/noobient-nginx_custom.cil.j2 b/templates/noobient-nginx_custom.cil.j2 new file mode 100644 index 0000000..1b81797 --- /dev/null +++ b/templates/noobient-nginx_custom.cil.j2 @@ -0,0 +1,8 @@ +{% if eff_host_port | string not in stock_http_ports | string %} +; let nginx listen on our custom port +(portcon tcp {{ eff_host_port }} (system_u object_r http_port_t ((s0) (s0)))) +{% endif %} +{% if eff_proxy_port | string not in stock_http_cache_ports | string %} +; let nginx proxy our custom port +(portcon tcp {{ eff_proxy_port }} (system_u object_r http_cache_port_t ((s0) (s0)))) +{% endif %} diff --git a/templates/php.conf.j2 b/templates/php.conf.j2 new file mode 100644 index 0000000..656e36d --- /dev/null +++ b/templates/php.conf.j2 @@ -0,0 +1,10 @@ +index index.php; + +location ~ \.php$ +{ + try_files $uri =404; + fastcgi_pass unix:/run/php-fpm/www.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; +} diff --git a/templates/ssl.conf.j2 b/templates/ssl.conf.j2 new file mode 100644 index 0000000..b67bc7e --- /dev/null +++ b/templates/ssl.conf.j2 @@ -0,0 +1,30 @@ +# check https://cipherli.st/ for updates +# or https://developers.cloudflare.com/ssl/ssl-tls/cipher-suites +ssl_protocols TLSv1.3; +ssl_session_cache builtin:1000 shared:SSL:10m; +ssl_prefer_server_ciphers on; +#ssl_ciphers ECDHE-RSA-AES256-GCM-SHA51:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384; +ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; + +ssl_dhparam dh4096.pem; + +ssl_stapling on; +ssl_stapling_verify on; +resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001] valid=300s; +resolver_timeout 30s; + +# make sure we don't send duplicate headers +proxy_hide_header Strict-Transport-Securty; +proxy_hide_header X-Content-Type-Options; +proxy_hide_header X-XSS-Protection; +proxy_hide_header Content-Security-Policy; +proxy_hide_header Referrer-Policy; +proxy_hide_header X-Frame-Options; + +# for preload, make sure to submit your root domain at https://hstspreload.org/ +add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"; +add_header X-Content-Type-Options "nosniff"; +add_header X-XSS-Protection "1; mode=block"; +add_header Content-Security-Policy "default-src https: 'unsafe-inline' 'unsafe-eval'; connect-src https: wss:; img-src data: https://*"; +add_header Referrer-Policy "strict-origin-when-cross-origin"; +add_header X-Frame-Options "SAMEORIGIN"; diff --git a/tests/main.yml b/tests/main.yml new file mode 100644 index 0000000..4281807 --- /dev/null +++ b/tests/main.yml @@ -0,0 +1,48 @@ +--- +- hosts: 127.0.0.1 + tasks: + - include_role: + name: "{{ playbook_dir.split('/')[:-1] | last }}" + vars: + domain: foo1.com + ssl_disabled: true + mode: wordpress + + - include_role: + name: "{{ playbook_dir.split('/')[:-1] | last }}" + vars: + domain: foo2.com + ssl_disabled: true + mode: static + path: /data/content/foo2.com + www_mode: serve + + - include_role: + name: "{{ playbook_dir.split('/')[:-1] | last }}" + vars: + domain: foo3.com + ssl_disabled: true + mode: redirect + new_domain: bar.com + + - name: Create cert directory + file: + path: /opt/acme + state: directory + owner: root + group: root + mode: '0700' + + - name: Generate self-signed certificate # noqa no-changed-when + command: + cmd: openssl req -x509 -newkey rsa:4096 -keyout /opt/acme/foo.com.key -out /opt/acme/foo.com.cert -sha256 -days 3650 -nodes -subj "/C=XX/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=foo.com" + + - include_role: + name: "{{ playbook_dir.split('/')[:-1] | last }}" + vars: + domain: foo.com + mode: proxy + ssl_key: /opt/acme/foo.com.key + ssl_cert: /opt/acme/foo.com.cert + host_port: 7777 + proxy_port: 8888 diff --git a/vars/main.yml b/vars/main.yml new file mode 100644 index 0000000..48bcf2f --- /dev/null +++ b/vars/main.yml @@ -0,0 +1,36 @@ +--- +nginx_root: /etc/nginx +eff_www_mode: "{% if www_mode is defined and www_mode | length %}{{ www_mode }}{% else %}redirect{% endif %}" +eff_path: "{% if path is defined and path | length %}{{ path }}{% else %}/var/www/html/{{ domain }}{% endif %}" +eff_ssl_disabled: "{% if ssl_disabled is defined and ssl_disabled | string | length %}{{ ssl_disabled }}{% else %}false{% endif %}" +eff_ssl_key: "{% if ssl_key is defined and ssl_key | length %}{{ ssl_key }}{% else %}/etc/acme/{{ domain }}/{{ domain }}.key{% endif %}" +eff_ssl_cert: "{% if ssl_cert is defined and ssl_cert | length %}{{ ssl_cert }}{% else %}/etc/acme/{{ domain }}/fullchain.cer{% endif %}" +eff_host_port: "{% if host_port is defined and host_port | string | length %}{{ host_port }}{% elif not eff_ssl_disabled | bool %}443{% else %}80{% endif %}" +eff_proxy_port: "{% if proxy_port is defined and proxy_port | string | length %}{{ proxy_port }}{% else %}8080{% endif %}" + +# Check with: +# semanage port -l | grep '^http_port_t ' +# semanage port -l | grep '^http_cache_port_t ' +stock_http_ports: + - 80 + - 81 + - 443 + - 488 + - 8008 + - 8009 + - 8443 + - 9000 +stock_http_cache_ports: + - 8080 + - 8118 + - 8123 + - 10001 + - 10002 + - 10003 + - 10004 + - 10005 + - 10006 + - 10007 + - 10008 + - 10009 + - 10010