/ansible_nginx

This nginx ansible role aims to provide both, configuration for reverseproxies as well as the terminating server.

Primary LanguageHTMLMIT LicenseMIT

About this ansible role

This nginx ansible role aims to provide both, configuration for reverseproxies as well as the terminating server.

It enables advanced configuration scenarios, such as complex location to upstream configs, uwsgi and unix socket support as well as TLS client authentication (optionally enforcing a specific CN presented by the client). A server may listen on multiple domains, optionally redirecting to the primary domain. HTTP basic authentication may be configured on a per-server basis.

This role is currently based on debian stable.

Note: A server is one server block in the nginx config.

Note: I found this role not to work with ansible 2.7.7 (current debian buster) as the default handling in jinja is different. Use ansible from buster-backports (> 2.9.16).

Configuration Files

This role expects to be the only entity configuring nginx on the respective host. It will disable all other sites_available (those that are not configured through this role), although it will not delete any configuration or files associated.

Letsencrypt

The role can retrieve, configure and maintain letsencrypt certificates. One certificate per server is retrieved, containing all additional_domains of that server.

If a server does not need its letsencrypt certificate anymore, this role will delete the certificate from certbot so it is not renewed. Only certificates created by this role will ever be possibly deleted.

If the domains associated with a server change, the role will acquire new certificates for the respective server.

Example Config

nginx_workers: auto
nginx_user: www-data
nginx_revproxy_sites:
  - fqdn_: site.example.com: # FQDN of primary domain (the domain this server responds on primarily)
    location_to_upstream:
      - {protocol: 'http', location: /, addrs: ['192.168.1.1:3000']}
      - {protocol: 'https', location: /mult, addrs: ['192.168.1.1:3000', '192.168.1.2:3000'], upstream_location: '/prox', additional_options: ['proxy_set_header X-Graylog-Server-URL https://$server_name/;']}
      - {protocol: 'http', location: /asd, socket_path: '/lol/test'}
      - {protocol: 'uwsgi', location: /asd2, socket_path: '/lol/uwsgi'}
      - {protocol: 'uwsgi', location: /asd3, addrs: ['192.168.1.10:3000'], uwsgi_param_location: '/etc/nginx/specialparams'}
      - {location: /static, webroot: '/var/www/static', client_max_body_size: '100M'}
    additional_domains:
      - site2.example.com
      - site3.example.com
    redirect_to_primary: true
    port: {http: 80, https: 443}
    ssl: {enabled: false, pem_path: "", key_path: ""}
    ssl_client_auth: {enabled: true, ca_cert: '/etc/ssl/certs/ca.crt', force_cn: 'server.example.com'}
    letsencrypt: {enabled: false, email: 'admin@example.com'}
    hsts: {max_age: 63072000, includeSubDomains: true}
    basic_auth: {user: 'user', password: 'pass', message: 'AUTHENTICATE'}
    log: {syslog: false, access_fmt: 'combined', error_fmt: 'error'}
    error_pages:
      - { codes: ['502', '503'], location: "/usr/share/error", site: "/unavailable.html" }

Example Minimal Config

nginx_revproxy_sites:
  - fqdn_: site.example.com:
    location_to_upstream:
      - {protocol: 'http', location: /grafana, addrs: ['127.0.0.1:3000'], additional_options: ['proxy_set_header Authorization ""'], pass_normalized_uri: true}
      - {protocol: 'http', location: /prometheus, addrs: ['127.0.0.1:9090'], upstream_location: /prometheus, pass_normalized_uri: true}
    letsencrypt: {enabled: true, email: 'admin@site.com'}

Example Minimal Config including catchall default site

nginx_revproxy_sites:
  - fqdn_: server3.example.com
    serves_static_default: true
    letsencrypt: {enabled: true, email: 'admin@site.com'}
  - fqdn_: site.example.com:
    location_to_upstream:
      - {protocol: 'http', location: /grafana, addrs: ['127.0.0.1:3000'], additional_options: ['proxy_set_header Authorization ""']}
      - {protocol: 'http', location: /prometheus, addrs: ['127.0.0.1:9090'], upstream_location: /prometheus}
    letsencrypt: {enabled: true, email: 'admin@site.com'}

Example using listen addresses

This example enforces authentication for external connections while allowing connections without authentication on localhost.

nginx_revproxy_sites:
  - fqdn_: site.example.com:
    listen: {ipv4: 192.168.10.1} # not specifying ipv6 will listen globally ([::])
    location_to_upstream:
      - {protocol: 'http', location: /grafana, addrs: ['127.0.0.1:3000'], additional_options: ['proxy_set_header Authorization ""']}
      - {protocol: 'http', location: /prometheus, addrs: ['127.0.0.1:9090'], upstream_location: /prometheus}
    letsencrypt: {enabled: true, email: 'admin@site.com'}
    basic_auth: {user: 'user', password: 'pass, message: 'AUTHENTICATE'}
    log: {syslog: true, access_fmt: 'graylog2_json' , error_fmt: 'error'}
  - fqdn_: site.example.com_: # trailing r'_+' will be stripped. This allows multiple servers with the same server name
    listen: {ipv4: 127.0.0.1, ipv6: false} # ipv6 disabled
    location_to_upstream:
      - {protocol: 'http', location: /grafana, addrs: ['127.0.0.1:3000'], additional_options: ['proxy_set_header Authorization ""']}
      - {protocol: 'http', location: /prometheus, addrs: ['127.0.0.1:9090'], upstream_location: /prometheus}
    log: {syslog: true, access_fmt: 'graylog2_json' , error_fmt: 'error'}

Example using TLS client authentication

You can configure upstream TLS, optionally with mutual authentication. The role does not take care of copying certificates to the servers.

nginx_revproxy_sites:
  - fqdn_: site1.example.com
    location_to_upstream:
      - {protocol: 'https', location: /, addrs: ['backend.example.com:443']}
    # Presents a client certificate to the upstream, accepts upstream server certs of the specified CA
    tls_to_upstream: {client_auth: true, cert: '/etc/ssl/web/site1.example.com.crt', key: '/etc/ssl/web/site1.example.com.key', cacert: '/etc/ssl/web/examplecom_webca.crt'}
    letsencrypt: {enabled: true, email: 'admin@example.com'}
    hsts: {max_age: 63072000, includeSubDomains: true}
    log: {syslog: true, access_fmt: 'splunk_kv' , error_fmt: 'error'}
  - fqdn_: site2.example.com
    location_to_upstream:
      - {protocol: 'https', location: /, addrs: ['10.0.0.3:8000']}
    # Does not present a client certificate to the upstream, accepts upstream server certs of the specified CA
    tls_to_upstream: {client_auth: false, cacert: '/etc/ssl/web/examplecom_webca.crt'}
    letsencrypt: {enabled: true, email: 'admin@example.com'}
    hsts: {max_age: 63072000, includeSubDomains: true}
    log: {syslog: true, access_fmt: 'splunk_kv' , error_fmt: 'error'}

Example with alias

You can configure some resources to be served locally by either using the above webroot statements, or alias statements.

nginx_revproxy_sites:
  - fqdn_: site1.example.com
    location_to_upstream:
      - {location: "/mailman3/static/favicon.ico", alias: "/var/lib/mailman3/web/static/postorius/img/favicon.ico", no_trail_slash: true}
      - {location: "/mailman3/static", alias: "/var/lib/mailman3/web/static"}
      - {protocol: 'https', location: /, addrs: ['backend.example.com:443']}
    # Presents a client certificate to the upstream, accepts upstream server certs of the specified CA
    tls_to_upstream: {client_auth: true, cert: '/etc/ssl/web/site1.example.com.crt', key: '/etc/ssl/web/site1.example.com.key', cacert: '/etc/ssl/web/examplecom_webca.crt'}
    letsencrypt: {enabled: true, email: 'admin@example.com'}
    hsts: {max_age: 63072000, includeSubDomains: true}
    log: {syslog: true, access_fmt: 'splunk_kv' , error_fmt: 'error'}