nginx-proxy-manager icon indicating copy to clipboard operation
nginx-proxy-manager copied to clipboard

Add SSL to streams

Open jbowring opened this issue 1 year ago • 9 comments

Support for SSL for streams with TCP forwarding enabled. Uses the same web interface as creating a new Proxy Host for adding SSL.

Screenshot 2024-06-02 at 20 38 48 image

Details

An Nginx stream config created with this SSL feature looks like this:

# ------------------------------------------------------------
# 1883 TCP: 1 UDP: 1
# ------------------------------------------------------------

server {
  listen 1883 ssl;
  listen [::]:1883 ssl;

  # Let's Encrypt SSL
  include conf.d/include/ssl-cache-stream.conf;
  include conf.d/include/ssl-ciphers.conf;
  ssl_certificate /etc/letsencrypt/live/npm-9/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/npm-9/privkey.pem;

  proxy_pass google.com:443;

  # Custom
  include /data/nginx/custom/server_stream[.]conf;
  include /data/nginx/custom/server_stream_tcp[.]conf;
}

server {
  listen 1883 udp;
  listen [::]:1883 udp;

  proxy_pass google.com:443;

  # Custom
  include /data/nginx/custom/server_stream[.]conf;
  include /data/nginx/custom/server_stream_udp[.]conf;
}

Nginx doesn't allow stream and http blocks to share an SSL cache, so all streams have a separate SSL cache defined in ssl-cache-stream.conf:

ssl_session_cache shared:SSL_stream:50m;

Use a DNS Challenge is forced as streams cannot perform HTTP authentication for issuing certificates.

Streams do not have domain names associated with them in the database or displayed in the UI, as streams are not proxied by hostname but exclusively by port.

jbowring avatar Jun 02 '24 20:06 jbowring

Related: #3368 #2542 #1829 #795

jbowring avatar Jun 03 '24 12:06 jbowring

@jbowring Do you have plans to update this PR with future releases?

RobertPosluszny avatar Jul 17 '24 19:07 RobertPosluszny

@RobertPosluszny thanks for testing this PR and your feedback. I think I've found the issue you described and fixed it, please check and let me know!

I'll do some more testing myself and then update this PR with a pull from the develop branch.

jbowring avatar Jul 23 '24 07:07 jbowring

@jc21 what else needs to be done before this PR can be merged? Thanks.

jbowring avatar Jul 23 '24 07:07 jbowring

@jbowring looks like the issue of editing stream settings after creation was resolved! Thanks!

RobertPosluszny avatar Jul 25 '24 13:07 RobertPosluszny

When testing the linked CI image the following error is shown in the browser console when trying to create a stream

TypeError: e[t] is undefined
    r https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:1
    190 https://MY-DOMAIN/js/14.bundle.14.js?v=2.11.3:1
    190 https://MY-DOMAIN/js/14.bundle.14.js?v=2.11.3:1
    r https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:1
    showNginxStreamForm https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    promise callback*showNginxStreamForm https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    action https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    click ui.action@https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    dispatch https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    handle https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    add https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    xe https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    each https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:16
    each https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:16
    xe https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    on https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    delegate https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    delegateEvents https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    delegateEvents https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    setElement https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    setElement https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    _ensureElement https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    View https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    constructor https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    i https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showEmpty https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    onRender https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    promise callback*onRender https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    O https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    render https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    ae https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    show https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showChildView https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showAppContent https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:310
    showNginxStream https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showNginxStream https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    promise callback*showNginxStream https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    Ze https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:3
    i https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:3
    s https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:3
[main.bundle.js:1:1930](https://MY-DOMAIN/js/main.bundle.js?v=2.11.3)
    oe https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:1
    (Async: promise callback)
    showNginxStreamForm https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    action https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    click @ui.action https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    dispatch https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    handle https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    (Async: EventListener.handleEvent)
    add https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    xe https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    each https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:16
    each https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:16
    xe https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    on https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    delegate https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    delegateEvents https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    delegateEvents https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    setElement https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    setElement https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    _ensureElement https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    View https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    constructor https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    i https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showEmpty https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    onRender https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    (Async: promise callback)
    onRender https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    O https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    render https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    ae https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    show https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showChildView https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showAppContent https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:310
    showNginxStream https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showNginxStream https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    (Async: promise callback)
    showNginxStream https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    Ze https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:3
    i https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:3
    s https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:3
Uncaught (in promise) TypeError: e[t] is undefined
    r https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:1
    190 https://MY-DOMAIN/js/14.bundle.14.js?v=2.11.3:1
    190 https://MY-DOMAIN/js/14.bundle.14.js?v=2.11.3:1
    r https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:1
    showNginxStreamForm https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    promise callback*showNginxStreamForm https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    action https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    click ui.action@https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    dispatch https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    handle https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    add https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    xe https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    each https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:16
    each https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:16
    xe https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    on https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    delegate https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    delegateEvents https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    delegateEvents https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    setElement https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    setElement https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    _ensureElement https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    View https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    constructor https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    i https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showEmpty https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    onRender https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    promise callback*onRender https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    O https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    render https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    ae https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    show https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showChildView https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showAppContent https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:310
    showNginxStream https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showNginxStream https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    promise callback*showNginxStream https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    Ze https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:3
    i https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:3
    s https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:3
[main.bundle.js:1:356](https://MY-DOMAIN/js/main.bundle.js?v=2.11.3)
    oe https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:1
    (Async: promise callback)
    showNginxStreamForm https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    action https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    click @ui.action https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    dispatch https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    handle https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    (Async: EventListener.handleEvent)
    add https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    xe https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    each https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:16
    each https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:16
    xe https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    on https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    delegate https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    delegateEvents https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    delegateEvents https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    setElement https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    setElement https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    _ensureElement https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    View https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    constructor https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    i https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showEmpty https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    onRender https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    (Async: promise callback)
    onRender https://MY-DOMAIN/js/9.bundle.9.js?v=2.11.3:1
    O https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    render https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    ae https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    show https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showChildView https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showAppContent https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:310
    showNginxStream https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    showNginxStream https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    (Async: promise callback)
    showNginxStream https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:27
    Ze https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:3
    i https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:3
    s https://MY-DOMAIN/js/main.bundle.js?v=2.11.3:3
    

JanzenJohn avatar Sep 29 '24 11:09 JanzenJohn

@jbowring any way to update this PR to keep in line with the latest updates? Is there a way I can help keep that in line until this PR gets merged into develop or latest?

RobertPosluszny avatar Oct 16 '24 16:10 RobertPosluszny

This would be such a useful feature for anyone trying to secure their MQTT servers to the outside world, thanks for doing it, would be amazing to see this eventually merged to main.

aaron-neal avatar Oct 24 '24 07:10 aaron-neal

@RobertPosluszny sorry I've been unable to maintain this for a while. I've added you as a collaborator to my repository; feel free to keep it up to date and update this pull request.

jbowring avatar Oct 31 '24 21:10 jbowring

Hello, like a lot of people I'd really like to get SSL for MQTT working within NPM.

The dock image mentioned above at jc21/nginx-proxy-manager:github-pr-3789, it no longer there. I a way I can get this docker image?

Thanks in advance!

alphalove avatar Nov 28 '24 14:11 alphalove

The image built above (build 7) doesn’t actually work, there’s an issue with the schema somewhere. I’ll update it when I’ve fixed the problem. Please don’t use it in the meantime.

jbowring avatar Dec 01 '24 23:12 jbowring

@jbowring - thanks for your effort on integrating SSL for streams, its really appreciated. This will be a great feature for NPM. Given the number of open pull requests, looks like being the NPM maintainer might be a full time job!

alphalove avatar Dec 02 '24 05:12 alphalove

I am new in Nginx stream. I want get something like that: connect to sstp.example.com:443 and stream to the 192.168.0.60:443; connect to www.example.com:443 and proxy to the 192.168.0.50:443; Will it work?

oblom86 avatar Jan 11 '25 09:01 oblom86

I am new in Nginx stream. I want get something like that: connect to sstp.example.com:443 and stream to the 192.168.0.60:443; connect to www.example.com:443 and proxy to the 192.168.0.50:443; Will it work?

I use this for securing MQTT streams, along side a bunch of other web services, but I believe, what your asking for will work. The sstp. sub-domain can proxy to something different than www.

I really wish the excellent addition would be merged into the main branch!

alphalove avatar Jan 11 '25 13:01 alphalove

I use this for securing MQTT streams, along side a bunch of other web services, but I believe, what your asking for will work. The sstp. sub-domain can proxy to something different than www.

I really wish the excellent addition would be merged into the main branch!

What would the configuration look like? Do I need to set up a 'Proxy Hosts' to localhost and then reroute with 'Streams'? Like that? example

oblom86 avatar Jan 11 '25 14:01 oblom86

Will this be merged?

stuzer05 avatar Jan 28 '25 17:01 stuzer05

Will this be merged?

Let's hope so. Looks like a perfect addition to current NPM functionality. Unfortunately, I'm not sure how we persuade the NPM maintainer to add this PR over any others.

alphalove avatar Feb 01 '25 13:02 alphalove

Docker Image for build 13 is available on DockerHub as nginxproxymanager/nginx-proxy-manager-dev:pr-3789

Note: ensure you backup your NPM instance before testing this image! Especially if there are database changes Note: this is a different docker image namespace than the official image

I get the error SQLITE_ERROR: no such column: domain_names, I thought the schema is fixed in this version?

JanzenJohn avatar Feb 03 '25 16:02 JanzenJohn

Will this be merged?

Unfortunately, I'm not sure how we persuade the NPM maintainer to add this PR over any others.

Buy some coffee for the maintainer (see github homepage) and mention this feature. No guarantees but a little extra caffeine can't hurt, no?

mkerost avatar Feb 03 '25 16:02 mkerost

Docker Image for build 14 is available on DockerHub as nginxproxymanager/nginx-proxy-manager-dev:pr-3789

Note: ensure you backup your NPM instance before testing this image! Especially if there are database changes Note: this is a different docker image namespace than the official image

I've rebased this PR with latest changes and pushed to the stream-ssl branch.

You may test with this docker image:

nginxproxymanager/nginx-proxy-manager-dev:stream-ssl

but beware, this contains a database migration and you won't be able to go back to the latest code very easily, so you really should back up your database, data and letsencrypt folders before using this image.

I would like to write an integration test for this before merging but I can't seem to think of a simple use case. I may just have to use openssl to test the ciphers but it won't be a full end to end test.

jc21 avatar Feb 04 '25 08:02 jc21

I've rebased this PR with latest changes and pushed to the stream-ssl branch.

You may test with this docker image:

nginxproxymanager/nginx-proxy-manager-dev:stream-ssl

but beware, this contains a database migration and you won't be able to go back to the latest code very easily, so you really should back up your database, data and letsencrypt folders before using this image.

I would like to write an integration test for this before merging but I can't seem to think of a simple use case. I may just have to use openssl to test the ciphers but it won't be a full end to end test.

That's awesome news, I will update and test. Thank you very much!

alphalove avatar Feb 04 '25 08:02 alphalove

Closing this PR and about to merge #4344

jc21 avatar Feb 05 '25 08:02 jc21

Thanks for merging this @jc21 and thank you for your efforts in maintaining NPM!

jbowring avatar Feb 12 '25 20:02 jbowring