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

Support for multiple server blocks #57

Open
NetForce1 opened this issue Apr 3, 2024 · 6 comments
Open

Support for multiple server blocks #57

NetForce1 opened this issue Apr 3, 2024 · 6 comments

Comments

@NetForce1
Copy link

Is your feature request related to a problem? Please describe

As variables set using js_var are global, it cannot be used to set njs_acme_server_names when multiple server blocks are used. Only one certificate would be generated, and on all but one server block the wrong certificate will be selected by acme.js_cert and acme.js_key.

What we need is a good way to handle multiple server definitions.

Describe the solution you'd like

The documentation and maybe examples need to be updated to address this problem. I don't know enough of nginx internals to know what the best solution would be.

Something I can think of is this:
Use $ssl_server_name for serving certificates, and $host for the auto mode. Like this:

server {
    listen 8000;
    server_name _default;

    set $njs_acme_server_names $host;
    js_var $njs_acme_account_email '[email protected]';

   # locations etc
}

server {
    listen 8443 ssl;
    server_name proxy.nginx.com;

    js_var $njs_acme_server_names $ssl_server_name;
    js_var $njs_acme_account_email '[email protected]';

   # locations etc
} 

That would mean though that js_periodic cannot be used.

This is still a suboptimal solution though, as it means approaches cannot be mixed in a single nginx install. It would fall apart when support for wildcard certificates is added.

So, maybe what's really needed is an alternative way of passing variables to njs functions. Set cannot be used, because the TLS stuff comes before set is handled.

@zsteinkamp
Copy link
Contributor

Heya @NetForce1 ... The way I do this is to use a single cert with multiple hostnames in it (SAN cert). I can then use that cert in as many server blocks as necessary. Example here: https://github.com/zsteinkamp/web-proxy/blob/nginx-plus/nginx.conf

This requests a cert with 5 hostnames, and uses that in 4 server blocks in the config.

Understood there are some limitations to this approach (i.e. runs in a single ACME "account", there may be limits on the number of hostnames a given provider supports), but it may be a decent solution for some.

I'll start a discussion with the njs team about supporting js_var scopes in different server blocks. That seems to be an important shortcoming to address.

@zsteinkamp
Copy link
Contributor

Someone filed an issue with njs yesterday on this topic. nginx/njs#700 I will add to it.

@NetForce1
Copy link
Author

Great, thanks for the support. Combining the domain names in a multi-SAN certificate is something I want to avoid. We are hosting on customer domains, and I'd rather not publicly mix them.

@zsteinkamp
Copy link
Contributor

zsteinkamp commented Apr 11, 2024

Hey @NetForce1 -- I'm doing some local testing and this is working for me using $server_name. It would work if your server blocks had a single hostname.

nginx.conf

  server {
    listen 80;
    listen 443 ssl;
    ...
    server_name proxy.nginx.com;
    ...
    js_var $njs_acme_server_names $server_name;
    ...
    location @acmePeriodicAuto {
      # Check certificate validity each minute
      js_periodic acme.clientAutoMode interval=1m;
    }
    location ~ "^/\.well-known/acme-challenge/[-_A-Za-z0-9]{22,128}$" {
      js_content acme.challengeResponse;
    }
    location / {
      ...
    }
  }
}

This could also be split between HTTP and HTTPS server blocks (with challenge response and periodic in the HTTP block), you just need to make sure to specify the server_name in each.

@NetForce1
Copy link
Author

Hi @zsteinkamp Thanks, I will test this one for a while. I think you can even use it with multiple hostnames, but only a single server_name directive.

@NetForce1
Copy link
Author

I think you can even use it with multiple hostnames, but only a single server_name directive.
Alas, you can't. I thought I had seen that work, but apparently not.

I have a work-around that uses js_set to specify the hostnames using javascript, and can use a lookup table to determine the alternative names based on $server_name. But, we deploy our config using Ansible, and quite often just update a single nginx-config-file (we have a file per customer).

What might work is a js_preload_object per server-block, with the first server_name as the key. Or, store the lookup table in a js_shared_dict_zone, and add a reload-endpoint that is called by our Ansible playbook.

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

No branches or pull requests

2 participants