Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example of ssl termination with nginx reverse proxy #7

Open
VilleMiekkoja opened this issue May 24, 2020 · 20 comments
Open

Example of ssl termination with nginx reverse proxy #7

VilleMiekkoja opened this issue May 24, 2020 · 20 comments
Labels
documentation Improvements or additions to documentation

Comments

@VilleMiekkoja
Copy link

Is your feature request related to a problem? Please describe.
Kind of. I wish to terminate ssl with nginx, and simultaneously apply it as reverse proxy. I do not see an example of this in the docs, even I think it is pretty common scenario.

Describe the solution you'd like
Just an example configuration on how to terminate ssl with reverse proxy

Describe alternatives you've considered
I've considered applying nginx reverse proxy with ssl termination without this role. Like just manually configuring nginx, if it is too hard with this.

@xTrekStorex
Copy link

You can take the examples and expand them with the dict objects taken from the templates defaults: https://github.com/nginxinc/ansible-role-nginx/blob/master/defaults/main/template.yml

Something like this:

nginx_http_template_enable: true
nginx_http_template:
  jira_mydomain_net:
    conf_file_name: jira_mydomain_net.conf
    servers:
      first:
        listen:
          listen_public:
            ip: 0.0.0.0
            port: 443
            ssl: true
        server_name: jira.mydomain.net
        ssl:
          cert: /etc/ssl/certs/jira.mydomain.net.crt
          key: /etc/ssl/private/jira.mydomain.net.key
        access_log:
          - name: combined
            location: /var/log/nginx/jira.mydomain.net_access.log
        error_log:
          location: /var/log/nginx/jira.mydomain.net_error.log
          level: warn
        reverse_proxy:
          locations:
            default:
              location: /
              proxy_pass: http://jira01.local.mydomain.net:8080

I left out ssl ciphers, dhparam, proxy_set_header etc since I set them in the main config, but you can just take them from the examples or defaults. Same for using upstreams.

@alessfg
Copy link
Collaborator

alessfg commented May 25, 2020

@xTrekStorex is totally right. At some stage I would also like to have working examples for most use cases covered in Molecule playbooks, including SSL (using self-signed certs) if possible.

@alessfg alessfg transferred this issue from nginxinc/ansible-role-nginx Aug 19, 2020
@alessfg alessfg added the documentation Improvements or additions to documentation label Sep 2, 2020
@vietcgi
Copy link

vietcgi commented Mar 4, 2021

I am trying to generate this config below with no luck. I wonder if its supported.

location /app1/ {
proxy_pass http://localhost:6000;
}
location /app2/ {
proxy_pass http://localhost:5000;
}

@alessfg
Copy link
Collaborator

alessfg commented Mar 5, 2021

Should definitely work. Are you using the latest release or main? If on main, check https://github.com/nginxinc/ansible-role-nginx-config/blob/main/molecule/default/converge.yml#L153-L290 -- you'll see there's two location blocks with proxy_pass. You can delete all the other variables that are being tested and set the values you mentioned for location and proxy_pass. (Side note: might want to create a new issue, your question is not related to ssl as far as I can tell.)

@ghomem
Copy link

ghomem commented Mar 23, 2021

I am also unable to configure a simple reverse proxy. In the example above I don't recognize the

reverse_proxy:

section.

When I try to use proxy_pass instead of root on a server that I have defined I get

TemplateAssertionError: no test named 'boolean'

A working example would be appreciated.

@alessfg
Copy link
Collaborator

alessfg commented Mar 23, 2021

The reverse_proxy section no longer exists. It got refactored into the proxy dictionaries you can now find on main (and 0.4.0). Did you check the molecule examples in the previous comment? In addition, proxy_pass is not allowed within a server context. It always has to be inside a location https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass.

@ghomem
Copy link

ghomem commented Mar 23, 2021

Example:

nginx_config_http_template_enable: true
    nginx_config_http_template:
      - template_file     : http/default.conf.j2
        conf_file_name    : '{{ my_server_name }}.conf'
        conf_file_location: /etc/nginx/conf.d/
        servers:
          - listen:
              - port: 443
                ssl : true
                opts: []
            server_name: '{{ my_server_name }}'
            ssl:
              cert                 : '/etc/ssl/certs/{{ my_ssl_prefix }}.nginx.bundle.crt'
              key                  : '/etc/ssl/private/{{ my_ssl_prefix }}.key'
              protocols            : '{{ NGINX_PROTOCOLS }}'
              prefer_server_ciphers: true
              ciphers              : '{{ NGINX_CIPHERS   }}'
              stapling             : true
              stapling_verify      : true
            autoindex : false
            locations :
              - location: /
                root    : '{{ my_root_folder }}'
              - location: /backend
                proxy_pass: http://127.0.0.1:8080/
                proxy:
                  set_header:
                     - field: Host
                       value: $host
                     - field: X-Real-IP
                       value: $remote_addr
                     - field: X-Forwarded-For
                       value: $proxy_add_x_forwarded_for
                     - field: X-Forwarded-Proto
                       value: $scheme

This results in

failed: [testing-snap05.MYDOMAIN.TLD] (item={'template_file': 'http/default.conf.j2', 'conf_file_name': 'testing-snap05.staging.MYDOMAIN.TLD.conf', 'conf_file_location': '/etc/nginx/conf.d/', 'servers': [{'listen': [{'port': 443, 'ssl': True, 'opts': []}], 'server_name': 'testing-snap05.staging.MYDOMAIN.TLD', 'ssl': {'cert': '/etc/ssl/certs/star.staging.MYDOMAIN.TLD.nginx.bundle.crt', 'key': '/etc/ssl/private/star.staging.MYDOMAIN.TLD.key', 'protocols': 'TLSv1.3', 'prefer_server_ciphers': True, 'ciphers': 'HIGH:!aNULL:!MD5', 'stapling': True, 'stapling_verify': True}, 'autoindex': False, 'locations': [{'location': '/', 'root': '/usr/share/nginx/html'}, {'location': '/backend', 'proxy_pass': 'http://127.0.0.1:8080/', 'proxy': {'set_header': [{'field': 'Host', 'value': '$host'}, {'field': 'X-Real-IP', 'value': '$remote_addr'}, {'field': 'X-Forwarded-For', 'value': '$proxy_add_x_forwarded_for'}, {'field': 'X-Forwarded-Proto', 'value': '$scheme'}]}}]}]}) => {"ansible_loop_var": "item", "changed": false, "item": {"conf_file_location": "/etc/nginx/conf.d/", "conf_file_name": "testing-snap05.staging.MYDOMAIN.TLD.conf", "servers": [{"autoindex": false, "listen": [{"opts": [], "port": 443, "ssl": true}], "locations": [{"location": "/", "root": "/usr/share/nginx/html"}, {"location": "/backend", "proxy": {"set_header": [{"field": "Host", "value": "$host"}, {"field": "X-Real-IP", "value": "$remote_addr"}, {"field": "X-Forwarded-For", "value": "$proxy_add_x_forwarded_for"}, {"field": "X-Forwarded-Proto", "value": "$scheme"}]}, "proxy_pass": "http://127.0.0.1:8080/"}], "server_name": "testing-snap05.staging.MYDOMAIN.TLD", "ssl": {"cert": "/etc/ssl/certs/star.staging.MYDOMAIN.TLD.nginx.bundle.crt", "ciphers": "HIGH:!aNULL:!MD5", "key": "/etc/ssl/private/star.staging.MYDOMAIN.TLD.key", "prefer_server_ciphers": true, "protocols": "TLSv1.3", "stapling": true, "stapling_verify": true}}], "template_file": "http/default.conf.j2"}, "msg": "TemplateAssertionError: no test named 'boolean'"}

@alessfg
Copy link
Collaborator

alessfg commented Mar 24, 2021

Right. Try to update to the latest version of Jinja2. The templates require Jinja2 2.11.x to properly work. This is most likely related to the errors you were finding on #94.

@ghomem
Copy link

ghomem commented Mar 24, 2021

Thank you. Does it exist as a package for Ubuntu 20.04? Otherwise what is the recommended update method?

@alessfg
Copy link
Collaborator

alessfg commented Mar 24, 2021

Jinja2 is a Python package. You can try running pip install -U Jinja2. You can read more here https://jinja.palletsprojects.com/en/2.11.x/intro/#installation

@pavelkim
Copy link

pavelkim commented Apr 15, 2021

Hello,

Having the same issue on python 2.7 and ansible 2.9.6:
"TemplateAssertionError: no test named 'boolean'"

On python 3.6 and ansible 2.9.6 everything works fine.

Variables:

nginx_config_http_template_enable: true
nginx_config_http_template:
  - template_file: http/default.conf.j2
    conf_file_name: 50_example.com.conf
    conf_file_location: /etc/nginx/conf.d/

    servers:
      - listen:
          - ip: 0.0.0.0
            port: 80

          - ip: 0.0.0.0
            port: 443
            ssl: true
        
        ssl:    
          cert: /etc/pki/tls/certs/example.com.crt
          key: /etc/pki/tls/private/example.com.key

        server_name: example.com
        error_page: /usr/share/nginx/html
        autoindex: false
        http_demo_conf: false

        access_log:
          - name: json
            location: /var/log/nginx/example.com-access.json.log
        error_log:
          level: warn
          location: /var/log/nginx/example.com-error.log

        locations:
          - location: /
            proxy_pass: http://127.0.0.1
            proxy:
              bind: false
              set_header:
                - field: Host
                  value: $host
                - field: X-Forwarded-For
                  value: $proxy_add_x_forwarded_for
                - field: X-Real-IP
                  value: $remote_addr
                - field: REMOTE_ADDR
                  value: $remote_addr

Results to:

+#
+# Ansible managed
+#
+
+
+
+
+server {
+    listen 0.0.0.0:80;
+    listen 0.0.0.0:443 ssl;
+    server_name example.com;
+    ssl_certificate /etc/pki/tls/certs/example.com.crt;
+    ssl_certificate_key /etc/pki/tls/private/example.com.key;
+    location / {
+        proxy_bind off;
+        proxy_set_header Host $host;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header REMOTE_ADDR $remote_addr;
+
+        proxy_pass http://127.0.0.1;
+
+    }
+    # redirect server error pages to the static page /50x.html
+    #
+    error_page   500 502 503 504  /50x.html;
+    location = /50x.html {
+        root   /usr/share/nginx/html;
+    }
+    access_log  /var/log/nginx/example.com-access.json.log  json;
+    error_log /var/log/nginx/example.com-error.log warn;
+}

@alessfg
Copy link
Collaborator

alessfg commented Apr 15, 2021

Try updating Jinja2. You are not using the latest release (v2.11).

@pavelkim
Copy link

@alessfg Thanks!

There was no red output, so I didn't notice that I'm actually having troubles updating Jinja2 locally:

bash-3.2$ pip install -U Jinja2
Collecting Jinja2
  Using cached https://files.pythonhosted.org/packages/7e/c2/1eece8c95ddbc9b1aeb64f5783a9e07a286de42191b7204d67b7496ddf35/Jinja2-2.11.3-py2.py3-none-any.whl
Requirement not upgraded as not directly required: MarkupSafe>=0.23 in /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages (from Jinja2) (0.23)
Installing collected packages: Jinja2
  Found existing installation: Jinja2 2.10.1
    Uninstalling Jinja2-2.10.1:
      Successfully uninstalled Jinja2-2.10.1
  Rolling back uninstall of Jinja2

You are using pip version 10.0.0, however version 20.3.4 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

In a clean virtualenv with Python 2.7 and Jinja2 2.11.3 everything works fine:

(py27) bash-3.2$ pip freeze 
ansible==2.9.6
cffi==1.14.5
cryptography==3.3.2
enum34==1.1.10
ipaddress==1.0.23
Jinja2==2.11.3
MarkupSafe==1.1.1
pycparser==2.20
PyYAML==5.4.1
six==1.15.0

@Kariton
Copy link

Kariton commented Nov 13, 2021

Just for anyone looking for an currently working example:
v 0.4.0

  - template_file: http/default.conf.j2
    deployment_location: /etc/nginx/conf.d/name.conf
    config:

      upstreams:
        - name: name_example_com
          least_conn: true
          servers:
            - address: '172.16.20.55:80'
              weight: 5

      servers:
        - core:
            listen:
              - port: 80
            server_name:
              - name.example.com

          locations:
            - location: '/robots.txt'
              log_not_found: false  # wont get set
              access:
                allow: 'all'
              log:
                access: false
            - location: '/'
              rewrite:
                return:
                  code: 301
                  url: 'https://$server_name:443$request_uri'


        - core:
            listen:
              - port: 443
                ssl: true
                http2: true
            server_name:
              - name.example.com

          ssl:
            certificate:
              - /etc/pki/tls/certs/name.example.com-rsa/name.example.com-rsa-fullchain.pem
              - /etc/pki/tls/certs/name.example.com-p-384/name.example.com-p-384-fullchain.pem
            certificate_key:
              - /etc/pki/tls/private/name.example.com-rsa/name.example.com-rsa.key
              - /etc/pki/tls/private/name.example.com-p-384/name.example.com-p-384.key
            trusted_certificate: '/etc/pki/tls/certs/name.example.com-p-384/name.example.com-p-384-chain.pem'
            dhparam: '/etc/pki/tls/misc/dhparam.crt'
            protocols:
              - TLSv1.2
              - TLSv1.3
            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
            ecdh_curve:
              - secp521r1
              - secp384r1
            prefer_server_ciphers: false
#            stapling: true
#            stapling_verify: true
            session_cache:
              shared:
                name: 'SSL'
                size: '10m'
            session_tickets: true
            session_timeout: '1d'

          access:
            allow:
              - 172.16.0.0/12
              - 10.255.255.0/24
            deny: 'all'

          headers:
            add_headers:
              - name: 'Strict-Transport-Security'
                value: '"max-age=63072000"'
                always: true
              - name: 'X-Frame-Options'
                value: '"SAMEORIGIN"'
                always: false
            add_trailers:
              - name: 'Strict-Transport-Security'
                value: '"max-age=63072000"'
                always: true
              - name: 'X-Frame-Options'
                value: '"SAMEORIGIN"'
                always: false

          log:
            access:
              - path: '/var/log/nginx/name_access.log'
                format: 'main'
            error:
              - /var/log/nginx/name_error.log

          locations:
            - location: '/'
              proxy:
                pass: 'http://name_example_com'
                connect_timeout: '1s'
                bind: false
                set_header:
                  - field: 'Host'
                    value: '$host'
                  - field: 'X-Forwarded-For'
                    value: '$proxy_add_x_forwarded_for'
                  - field: 'X-Real-IP'
                    value: '$remote_addr'
                  - field: 'REMOTE_ADDR'
                    value: '$remote_addr'

@Kariton
Copy link

Kariton commented Nov 13, 2021

But how do i get an nested location block like this:
can anyone help me out? (is that even the best way do configure such location blocks?)

    location ^~ /some/dir/ {
        location ~ \.json$ {
          if ($http_referer != "") {
            return 301 https://app.example.com:443/#HTTP_request('GET','https://$server_name:443$request_uri','','Cross-Origin%20Resource%20Sharing',false);
          }
          proxy_bind off;
          proxy_connect_timeout 1s;
          proxy_pass http://data_example_com;
          proxy_set_header Host $host;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header REMOTE_ADDR $remote_addr;

          allow 172.16.0.0/12;
          allow 10.255.255.0/24;
          deny all;
        }

        proxy_bind off;
        proxy_connect_timeout 1s;
        proxy_pass http://data_example_com;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header REMOTE_ADDR $remote_addr;

        allow 172.16.0.0/12;
        allow 10.255.255.0/24;
        deny all;
    }

@alessfg
Copy link
Collaborator

alessfg commented Nov 13, 2021

The role in its current iteration does not allow for nested location blocks. You can probably achieve what you want with a little bit of code duplication by creating two different location blocks. Alternatively, and given that you are also using an if statement (which is also not supported by the role), maybe check out the custom_directives parameter? You could potentially include your whole nested location block using that parameter.

@Kariton
Copy link

Kariton commented Nov 13, 2021

Thanks for that reply.

I already utilize custom_directives for map and geo type of configuration.

But i dont came up with the idea to use them for the entire location block.

This did the trick!
THANKS! :)

@anarcat
Copy link

anarcat commented Sep 28, 2023

Just for anyone looking for an currently working example: v 0.4.0

That's pretty useful!

I feel there's truly a need to have a simple sample configuration here. I tried the above and it just did zlit here. I have the following playbook:

---
- name: Prometheus installation
  hosts: prometheus
  tasks:
    - name: Install NGINX
      ansible.builtin.include_role:
        name: nginxinc.nginx
      vars:
        nginx_manage_repo: false
        nginx_install_from: "os_repository"
    - name: Configure NGINX
      ansible.builtin.include_role:
        name: nginxinc.nginx_core.nginx_config
      vars:
        nginx_config_debug_output: false
        nginx_config_cleanup: false
        nginx_config_http_template_enable: true
        nginx_config_http_template:
          - template_file: http/default.conf.j2
            conf_file_name: prometheus.debian.net.conf
            conf_file_location: /etc/nginx/conf.d/
            backup: true
            servers:
              - core:
                  listen:
                    - port: 80
                    - port: 443
                      ssl: true
                  server_name:
                    - prometheus.debian.net
                
                ssl:
                  certificate:
                    - /etc/letsencrypt/live/prometheus.debian.net/fullchain.pem
                  certificate_key:
                    - /etc/letsencrypt/live/prometheus.debian.net/privkey.pem
        
                server_name: prometheus.debian.net
                error_page: /usr/share/nginx/html
                autoindex: false
                http_demo_conf: false
                log:
                  access:
                    - path: /var/log/nginx/prometheus.debian.net_access.log
                      format: combined
                  error:
                    - /var/log/nginx/prometheus.debian.net_error.log

                locations:
                  - location: /
                    proxy:
                      pass: http://localhost:9090

this doesn't even create a prometheus.debian.net.conf file anywhere I could find.
I've been pouring through https://github.com/nginxinc/ansible-role-nginx-config/blob/main/molecule/plus/converge.yml and https://github.com/nginxinc/ansible-role-nginx-config/blob/main/molecule/default/converge.yml and those are very verbose, it's quite hard to find my way in there.

So could we please have an up-to-date, minimalist configuration people could derive from? It's really nice to have the full details of all the possible configuration items, but we get totally lost in that YAML file there...

@anarcat
Copy link

anarcat commented Sep 28, 2023

actually, this works now:

---
- name: Prometheus installation
  hosts: prometheus
  tasks:
    - name: Install NGINX
      ansible.builtin.include_role:
        name: nginxinc.nginx
      vars:
        nginx_manage_repo: false
        nginx_install_from: "os_repository"
    - name: Configure NGINX
      ansible.builtin.include_role:
        name: nginxinc.nginx_core.nginx_config
      vars:
        nginx_config_http_template_enable: true
        nginx_config_http_template:
          - template_file: http/default.conf.j2
            deployment_location: /etc/nginx/conf.d/prometheus.debian.net.conf
            config:
              servers:
                - core:
                    listen:
                      - port: 80
                      - port: 443
                        ssl: true
                    server_name:
                      - prometheus.debian.net
                  
                  ssl:
                    certificate:
                      - /etc/letsencrypt/live/prometheus.debian.net/fullchain.pem
                    certificate_key:
                      - /etc/letsencrypt/live/prometheus.debian.net/privkey.pem
          
                  server_name: prometheus.debian.net
                  log:
                    access:
                      - path: /var/log/nginx/prometheus.debian.net_access.log
                        format: combined
                    error:
                      - /var/log/nginx/prometheus.debian.net_error.log
  
                  locations:
                    - location: /
                      proxy:
                        pass: http://localhost:9090

that's as minimal as it gets, and i think that something like this should be one of an example directly linked from the readme, if not directly in the readme file...

@alessfg
Copy link
Collaborator

alessfg commented Sep 28, 2023

I agree! Working on/improving how some use cases are documented is one of the next items in my bucket list (I want to adapt the model used in the core nginx role). I can't promise an exact date but hopefully it'll be within the next month 😁

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

8 participants