From 750e31fd01838a33057731a892934d10bd40ddc3 Mon Sep 17 00:00:00 2001 From: srwadleigh <7832+srwadleigh@users.noreply.github.com> Date: Sat, 4 May 2024 00:33:42 +0000 Subject: [PATCH] intial commit for nginx role --- LICENSE | 20 ++++++++++ README.md | 62 ++++++++++++++++++++++++++++++ defaults/main.yml | 17 ++++++++ handlers/main.yml | 8 ++++ meta/main.yml | 37 ++++++++++++++++++ tasks/main.yml | 30 +++++++++++++++ tasks/nginx.yml | 25 ++++++++++++ tasks/packages.yml | 11 ++++++ tasks/ssl.yml | 29 ++++++++++++++ templates/basic-proxy.conf.j2 | 52 +++++++++++++++++++++++++ templates/server-name-hash.conf.j2 | 3 ++ templates/websocket-proxy.conf.j2 | 57 +++++++++++++++++++++++++++ vars/Archlinux.yml | 16 ++++++++ vars/RedHat.yml | 15 ++++++++ vars/Ubuntu.yml | 19 +++++++++ 15 files changed, 401 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 defaults/main.yml create mode 100644 handlers/main.yml create mode 100644 meta/main.yml create mode 100644 tasks/main.yml create mode 100644 tasks/nginx.yml create mode 100644 tasks/packages.yml create mode 100644 tasks/ssl.yml create mode 100644 templates/basic-proxy.conf.j2 create mode 100644 templates/server-name-hash.conf.j2 create mode 100644 templates/websocket-proxy.conf.j2 create mode 100644 vars/Archlinux.yml create mode 100644 vars/RedHat.yml create mode 100644 vars/Ubuntu.yml diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5addd2c --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2024 Shane Wadleigh + +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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..159e286 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# ansible-roles-nginx + +This role is designed to provide a reverse proxy in conjuction with another role based deployment such as a metrics stack + + - https://nginx.org/ + + +## Task Configuration + +``` +- name: Setup nginx + hosts: somehost + become: true + roles: + - role: nginx + nginx_sites: + - name: lmer2-frontend + url: lmer2.20c.com + upstream: http://localhost:8001 + ssl: false + - role: firewalld + firewalld_services: + - http + - https + firewalld_ports: + - 8100/tcp # dashboard insecure mode + firewalld_forwards: + - port: 80 + to: 8080 + - port: 443 + to: 8443 +``` + +`ssl` can have a few different meaningful values +- undfefined will skip ssl entirely including its inclusion in the templates +- true, lets encrypt will request a cert +- false, lets encrypt will not request a cert but the template will assume a cert exists + +`ssl_barrow` can be set to the url of an existing cert to piggy back on another existing cert + + +## Deployment and Removal + + +Sometimes you need to manually stop the running containers to get a clean run when re-deploying +Services must be stopped as the respecitve user or another means to aquire the correct user scope for systemd + +``` +systemctl --user stop container-nginx.service +``` + +Deploy + +``` +ansible-playbook -i hosts site.yml --tags=firewalld,nginx --limit=somehost +``` + +Remove + +``` +ansible-playbook -i hosts site.yml --tags=firewalld,nginx --extra-vars "container_state=absent firewall_action=remove" --limit=somehost +``` diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..ea03c17 --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,17 @@ +--- +nginx_packages_intall: true +nginx_packages: + - nginx + - certbot + - python3-certbot-nginx + +nginx_path: /etc/nginx/conf.d +nginx_default_template: basic-proxy + +nginx_proxy_connection_timeout: 10 +nginx_proxy_read_timeout: 200 +nginx_proxy_send_timeout: 200 + +nginx_ssl_path: /etc/letsencrypt/live +# define email for dyanmic certificates +# nginx_ssl_email: diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..eeefe02 --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,8 @@ +--- + +- name: Start/Enable nginx + listen: "restart nginx" + ansible.builtin.systemd: + name: nginx + state: restarted + enabled: true diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..2c7c3c5 --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,37 @@ +--- +dependencies: [] + +galaxy_info: + role_name: nginx + author: srw + description: An Ansible role for configuring ngix + company: "NMD, LLC" + license: "license (BSD, MIT)" + min_ansible_version: "2.10" + platforms: + - name: Fedora + versions: + - all + - name: Debian + versions: + - buster + - bullseye + - bookworm + - name: Ubuntu + versions: + - bionic + - focal + - jammy + - name: Alpine + version: + - all + - name: ArchLinux + versions: + - all + galaxy_tags: + - server + - system + - http + - nginx + - web + - proxy diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..539697c --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,30 @@ +--- +- name: Role for setting up a nginx reverse proxy + block: + + - name: gather local facts + set_fact: + local_user: "{{ lookup('env', 'USER') }}" + delegate_to: localhost + + - name: nginx checking packages + ansible.builtin.include_tasks: packages.yml + tags: + - nginx + - nginx-packages + + - name: nginx setup configs + ansible.builtin.include_tasks: nginx.yml + tags: + - nginx + - nginx-configs + + - name: nginx setup ssl + ansible.builtin.include_tasks: ssl.yml + tags: + - nginx + - nginx-ssl + + when: + - nginx_sites is defined + diff --git a/tasks/nginx.yml b/tasks/nginx.yml new file mode 100644 index 0000000..b64dc8c --- /dev/null +++ b/tasks/nginx.yml @@ -0,0 +1,25 @@ +--- +- name: Setup nginx configs + block: + + - name: Set selinux http can connect + ansible.builtin.command: setsebool -P httpd_can_network_connect 1 + when: + - ansible_os_family == "RedHat" + + - name: Template nginx server name hash config + ansible.builtin.template: + src: "templates/server-name-hash.conf.j2" + dest: "{{ nginx_path }}/server-name-hash.conf" + mode: 0644 + notify: "restart nginx" + when: + - nginx_server_name_hash is number + + - name: Template nginx configs + ansible.builtin.template: + src: "templates/{{ item.template | d(nginx_default_template) }}.conf.j2" + dest: "{{ nginx_path }}/{{ item.name }}.conf" + mode: 0644 + loop: "{{ nginx_sites }}" + notify: "restart nginx" diff --git a/tasks/packages.yml b/tasks/packages.yml new file mode 100644 index 0000000..90ea721 --- /dev/null +++ b/tasks/packages.yml @@ -0,0 +1,11 @@ +--- +- name: Setup nginx packages + block: + + - name: Install nginx packages + ansible.builtin.package: + name: "{{ item }}" + state: present + with_items: "{{ nginx_packages }}" + when: + - nginx_packages_intall diff --git a/tasks/ssl.yml b/tasks/ssl.yml new file mode 100644 index 0000000..e0180cb --- /dev/null +++ b/tasks/ssl.yml @@ -0,0 +1,29 @@ +--- +- name: Setup ssl certificates + block: + + - name: Request ssl certificates + ansible.builtin.command: certbot --nginx --agree-tos -n -m "{{ nginx_ssl_email }}" -d "{{ site.url }}" + args: + creates: + - "{{ nginx_ssl_path }}/{{ site.url }}/cert.pem" + - "{{ nginx_ssl_path }}/{{ site.url }}/chain.pem" + - "{{ nginx_ssl_path }}/{{ site.url }}/fullchain.pem" + - "{{ nginx_ssl_path }}/{{ site.url }}/privkey.pem" + vars: + ssl: true + when: + - site.ssl is defined + - site.ssl + loop: "{{ nginx_sites }}" + loop_control: + loop_var: site + + - name: Add cron job for ssl renewal + ansible.builtin.cron: + name: "Renew ssl certs" + minute: "{{ 59 | random }}" + hour: "*/12" + job: "/usr/bin/certbot renew --nginx --quiet" + when: + - ssl is defined diff --git a/templates/basic-proxy.conf.j2 b/templates/basic-proxy.conf.j2 new file mode 100644 index 0000000..a6fd2e5 --- /dev/null +++ b/templates/basic-proxy.conf.j2 @@ -0,0 +1,52 @@ +# template: basic-proxy generated via ansible by {{ local_user }} at {{ ansible_date_time.time }} {{ ansible_date_time.date }} + +{% if item.ssl is defined %} +server { + if ($host = {{ item.url }}) { + return 301 https://$host$request_uri; + } + + listen {% if item.listen is defined %}{{ item.listen }}:{% endif %}80; + server_name {{ item.url }}; + return 404; +} +{% endif %} + +server { + listen {% if item.listen is defined %}{{ item.listen }}:{% endif %}{% if item.ssl is defined %}443 ssl http2{% else %}80{% endif %}; + server_name {{ item.url }}; + access_log /var/log/nginx/http-access-{{ item.name }}.log; + +{% if item.ssl is defined %} +{% if item.ssl_barrow is defined %} + ssl_certificate /etc/letsencrypt/live/{{ item.ssl_barrow }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{ item.ssl_barrow }}/privkey.pem; +{% else %} + ssl_certificate /etc/letsencrypt/live/{{ item.url }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{ item.url }}/privkey.pem; +{% endif %} + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; +{% endif %} + +{% if item.configs is defined %} +{% for config in item.configs %} + {{ config }}; +{% endfor -%} +{% endif -%} + + location / { + proxy_pass {{ item.upstream }}; + proxy_pass_header Server; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-NginX-Proxy true; + proxy_redirect off; + proxy_read_timeout {{ item.read_timeout | d(nginx_proxy_read_timeout) }}; + proxy_send_timeout {{ item.send_timeout | d(nginx_proxy_send_timeout) }}; + proxy_connect_timeout {{ item.connection_timeout | d(nginx_proxy_connection_timeout) }}; + } +} diff --git a/templates/server-name-hash.conf.j2 b/templates/server-name-hash.conf.j2 new file mode 100644 index 0000000..a8a3748 --- /dev/null +++ b/templates/server-name-hash.conf.j2 @@ -0,0 +1,3 @@ +# template: server-name-hash generated via ansible by {{ local_user }} at {{ ansible_date_time.time }} {{ ansible_date_time.date }} + +server_names_hash_bucket_size {{ nginx_server_name_hash }}; diff --git a/templates/websocket-proxy.conf.j2 b/templates/websocket-proxy.conf.j2 new file mode 100644 index 0000000..442a63f --- /dev/null +++ b/templates/websocket-proxy.conf.j2 @@ -0,0 +1,57 @@ +# template: websocket-proxy generated via ansible by {{ local_user }} on {{ ansible_date_time.time }} {{ ansible_date_time.date }} + +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +{% if item.ssl is defined %} +server { + if ($host = {{ item.url }}) { + return 301 https://$host$request_uri; + } + + listen {% if item.listen is defined %}{{ item.listen }}:{% endif %}80; + server_name {{ item.url }}; + return 404; +} +{% endif %} + +server { + listen {% if item.listen is defined %}{{ item.listen }}:{% endif %}{% if item.ssl is defined %}443 ssl http2{% else %}80{% endif %}; + server_name {{ item.url }}; + access_log /var/log/nginx/http-access-{{ item.name }}.log; + +{% if item.ssl is defined %} +{% if item.ssl_barrow is defined %} + ssl_certificate_key /etc/letsencrypt/live/{{ item.ssl_barrow }}/privkey.pem; +{% else %} + ssl_certificate /etc/letsencrypt/live/{{ item.url }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{ item.url }}/privkey.pem; +{% endif %} + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; +{% endif %} + +{% if item.configs is defined %} +{% for config in item.configs %} + {{ config }}; +{% endfor -%} +{% endif -%} + + location / { + proxy_pass {{ item.upstream }}; + 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_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + proxy_redirect off; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_read_timeout {{ item.read_timeout | d(nginx_proxy_read_timeout) }}; + proxy_send_timeout {{ item.send_timeout | d(nginx_proxy_send_timeout) }}; + proxy_connect_timeout {{ item.connection_timeout | d(nginx_proxy_connection_timeout) }}; + } +} diff --git a/vars/Archlinux.yml b/vars/Archlinux.yml new file mode 100644 index 0000000..d58d9a0 --- /dev/null +++ b/vars/Archlinux.yml @@ -0,0 +1,16 @@ +--- +promtail_journal: /run/log/journal + +podman_packages: + - podman + - cni-plugins + - fuse-overlayfs + - slirp4netns + +docker_packages: + - docker + +nginx_packages: + - nginx + - certbot + - certbot-nginx diff --git a/vars/RedHat.yml b/vars/RedHat.yml new file mode 100644 index 0000000..bce3a05 --- /dev/null +++ b/vars/RedHat.yml @@ -0,0 +1,15 @@ +--- +promtail_journal: /run/log/journal + +podman_packages: + - podman + - systemd-container + +docker_packages: + - docker-ce + +nginx_packages: + - nginx + - httpd-tools + - certbot + - python3-certbot-nginx diff --git a/vars/Ubuntu.yml b/vars/Ubuntu.yml new file mode 100644 index 0000000..59e249e --- /dev/null +++ b/vars/Ubuntu.yml @@ -0,0 +1,19 @@ +--- +promtail_journal: /var/log/journal + +podman_packages: + - podman + - systemd-container + - acl + +docker_packages: + - docker-ce + - docker-ce-cli + - containerd.io + - docker-compose + - sysdocker-compose-plugin + +nginx_packages: + - nginx + - certbot + - python3-certbot-nginx