RTSP stream over SSL with nginx - ssl

for security purposes I want to encrypt the RTSP stream of a camera before sending the data in the world.
For this I have set up a NGINX server with a SSL encryption:
nginx.conf:
stream {
upstream rtsp_backend {
server 10.0.1.1:554; #my dahua camera
#server 184.72.239.149:554; #big buck bunny stream
}
server {
listen 12345 ssl;
proxy_pass rtsp_backend;
ssl_certificate /etc/nginx/dev_nginx.chain.crt;
ssl_certificate_key /etc/nginx/dev_nginx.key;
}
I test my configuration with ffplay command:
ffplay -rtsp_transport tcp rtsps://user:password#127.0.0.1:12345/live
This configuration works fine with the buck bunny stream - the stream is encrypted between nginx and ffplay. And it works also fine when I create a rtsp stream by myself with ffmpeg/ffserver.
BUT: the problem is now my real use case with the camera Dahua (IPC-HFW1431S).
FFPLAY tells me:
[tls # 0x7f7f18002e80] No default whitelist set
[tcp # 0x7f7f18003420] No default whitelist set
[rtsp # 0x7f7f18000b80] method OPTIONS failed: 404 Not Found
So the request for the options (SDP) fails with file not found.
When I remove the ssl statements of nginx, everything is fine.
What am I missing? Everything works fine without SSL.
The only difference between the camera and the other RTSP streams is the authentication. But this seems to be ok, because this is done before OPTIONS request and it succeeds.

Related

Varnish 6.0lts won't handle secure websockets on a remote proxy?

I'm having a hard time with this setup. I have a node.js box serving HTTP on 3000, websockets on 3001, and secure websockets on 3002. Out in front of that I have a remote Hitch/Varnish caching proxy on its own server that's listening on 443/80 and connecting the first server as its default backend via 3000. A user who visits the site URL https://foo.tld hits the varnish proxy and sees the site, where some javascript on the site tells their browser to connect to wss://foo.tld:3002 for secure websockets.
My problem is getting websockets to pass transparently through to the backend. In the VCL I have the standard
if (req.http.upgrade ~ "(?i)websocket") {
return (pipe);
}
and
sub vcl_pipe {
#Declare pipe handler for websockets
if (req.http.upgrade) {
set bereq.http.upgrade = req.http.upgrade;
set bereq.http.connection = req.http.connection;
}
}
Which doesn't work in this case. To list what I have tried so far with no success:
1: Creating a second backend in VCL named "websockets" that is the same backend IP but on either port 3001 or 3002 and adding "set req.backend_hint = websockets;" before the pipe summon in the first snippet above.
2: Turning off HTTPS and trying to connect it over pure HTTP.
3: Modifying varnish.service to try and make varnish listen on ports other than, or in addition to, -a :80 and -a :8443,proxy, in which cases Varnish simply refuses to start. One attempt was to simply use HTTP only and attempt to run varnish on 3001 to get ws:// working without SSL but varnish refuses to start.
4: Most recently I attempted the following in VCL to try and pick up client connections coming in on 3001:
if (std.port(server.ip) == 3001) {
set req.backend_hint = websockets;
}
My goal is for the Varnish box to pick up secure websocket traffic (wss://) on 3002 (via hitch at 443 using the normal secure websocket connection protocol) and have that passed transparently to the backend websocket server, whether SSL encrypted across that leg of the connection or not. I have set up other, smaller servers like this before and getting websockets working is trivial if Varnish and the backend service are either on the same machine or behind a regulating CDN like Cloudflare, so it has been extra frustrating trying to figure out just what this remote proxy setup needs. I feel like part of the solution is having Varnish or Hitch (not sure) listening on 3002 to accept the connections at which point the normal req.http.upgrade and pipe functions would come into play, but the software refuses to cooperate.
--------Update--
I have broken down the problem into the simplest form I can. The main server (backend) is now serving plain HTTP on 8080 and WS:// on 6081. I have removed hitch and TLS from the equation entirely, but even in this simplified form it is impossible to connect to websockets through Varnish. I can verify that the Websocket server is working correctly on the backend. Connecting to the backend IP address with a browser shows websockets functioning perfectly there. It's Varnish that's the problem.
My current hitch.conf (not relevant here but provided per request):
frontend = "[*]:443"
frontend = "[*]:3001"
backend = "[127.0.0.1]:8443" # 6086 is the default Varnish PROXY port.
workers = 4 # number of CPU cores
daemon = on
# We strongly recommend you create a separate non-privileged hitch
# user and group
user = "redacted"
group = "redacted"
# Enable to let clients negotiate HTTP/2 with ALPN. (default off)
# alpn-protos = "h2, http/1.1"
# run Varnish as backend over PROXY; varnishd -a :80 -a localhost:6086,PROXY ..
write-proxy-v2 = on # Write PROXY header
syslog = on
log-level = 1
# Add pem files to this directory
# pem-dir = "/etc/pki/tls/private"
pem-file = "/redacted/hitch-bundle.pem"
Current default.vcl (stripped down to almost nothing just for testing this. The backend is NOT running on the same machine, it is remote):
# Marker to tell the VCL compiler that this VCL has been adapted to the
# new 4.0 format.
vcl 4.0;
# Default backend definition. Set this to point to your content server.
backend default {
.host = "remote.server.ip";
.port = "8080";
}
backend websockets {
.host = "remote.server.ip";
.port = "6081";
}
sub vcl_recv {
# Happens before we check if we have this in cache already.
#
# Typically you clean up the request here, removing cookies you don't need,
# rewriting the request, etc.
#Allow websockets to pass through the cache (summons pipe handler below)
if (req.http.Upgrade ~ "(?i)websocket") {
set req.backend_hint = websockets;
return (pipe);
} else {
set req.backend_hint = default;
}
}
sub vcl_pipe {
if (req.http.upgrade) {
set bereq.http.upgrade = req.http.upgrade;
set bereq.http.connection = req.http.connection;
}
return (pipe);
}
Varnish's systemd exec parameters:
ExecStart=/usr/sbin/varnishd \
-a http=:80 \
-a proxy=localhost:8443,PROXY \
-a ws=:6081 \
-p feature=+http2 \
-f /etc/varnish/default.vcl \
-s malloc,256m \
-p pipe_timeout=1800
Working in plain HTTP and insecure websockets like this, it should be very simple to get a working model. I don't understand what could possibly be going wrong.
Varnish Cache, the open source version of Varnish, doesn't support backend connections over TLS.
While you can offload TLS using Hitch, the connection to your websocket server will not be encrypted.
Basic VCL example
Here's a very basic VCL example where web & websocket requests are split and sent to separate backends:
vcl 4.1;
backend web {
.port = "3000";
}
backend ws {
.port = "3001";
}
sub vcl_recv {
if (req.http.Upgrade ~ "(?i)websocket") {
set req.backend_hint = ws;
return (pipe);
} else {
set req.backend_hint = web;
}
}
sub vcl_pipe {
if (req.http.upgrade) {
set bereq.http.upgrade = req.http.upgrade;
}
return (pipe);
}
Need more input
However, I'm probably missing a lot of context. I also didn't specify a .host parameter in the backends, so the assumption is that all services are hosted locally.
Please add your full VCL, your Hitch config and the varnishd runtime parameters to your question. This will add context and allows me to come up with a better solution.
What about Hitch?
If you terminate TLS in Hitch, both HTTPS & secure websockets will be handled by Hitch where the plain-text HTTP & websockets will still be directly handeled by Varnish.
See https://www.varnish-software.com/developers/tutorials/terminate-tls-varnish-hitch for a Hitch tutorial that also explains how Varnish should be configured.
I'm a big advocate of using the PROXY protocol in Varnish. The hitch tutorial has a specific section about this: https://www.varnish-software.com/developers/tutorials/terminate-tls-varnish-hitch/#enable-the-proxy-protocol-in-varnish
Custom ports
The standard ports to access the service are 80 for HTTP and insecure websockets and 443 for HTTPS and secure websockets.
If you want to use custom ports for the websockets, it is possible to configure them in Hitch and Varnish.
Let's say you want to main ports 3001 and 3002 for your websockets. This means you need 2 frontends in Hitch:
One for HTTPs on 443
One for secure WS on 3002
See https://www.varnish-software.com/developers/tutorials/terminate-tls-varnish-hitch/#listening-address for more information about the frontend config.
Varnish on the other hand needs to have 3 listening addresses:
One for HTTP on port 80 (-a http=:80)
One for offloaded HTTPS & secure WS with PROXY support on port 8443 (-a proxy=:8443,PROXY)
One for insecure WS on port 3001 (-a ws=:3001)
Next steps
Please use the information and see if this helps to find a solution. If not, please share your VCL file, your Hitch config and varnishd runtime.
Update
Now that you provided more input, the picture starts to become more clear. The fact that you eliminated the TLS part for now will make it a lot easier to debug.
Assuming the names of your listening interfaces for varnishd are http and ws (as mentioned in your systemd unit file), we can use the following varnishlog commands to debug:
varnishlog -g request -q "ReqStart[3] eq 'http'"
This command will show logs for all log transactions where the http listening interface is used.
If you want to make it more granular, you can also add the request URL as a filtering criterium. This will narrow down the number of transactions:
varnishlog -g request -q "ReqStart[3] eq 'http' and ReqUrl eq '/'"
Please add a complete log transaction for one of the failed requests. This will help us understand why requests are failing.
You can do the same for requests on the ws listening interface by using the commands below:
varnishlog -g request -q "ReqStart[3] eq 'ws'"
varnishlog -g request -q "ReqStart[3] eq 'ws' and ReqUrl eq '/'"
I'm assuming you're successful at starting the varnishd program but unsuccessful at getting decent output out of Varnish. The varnishlog program will provide the insight we need. Please add the logging output to your question so I can look into it.

Realm Object Server - SSL configuration causes ROS to fail start

Ubuntu 16.04 / ROS v1.3.0
I am attempting to configure my ROS to use secure SSL connections.
If I do not make any changes to the configuration.yml - ROS is fine. I can sync and use the dashboard as I would expect.
I have obtained an SSL cert from Letsencrypt. I used the CertBot in standalone mode so that I did not have to install or configure Nginx. (My preference is to not install yet another tech/layer - keep it clean!)
I have the following certificates/key stored in this folder:
/etc/letsencrypt/live/data.mydomain.net/cert.pem
/etc/letsencrypt/live/data.mydomain.net/chain.pem
/etc/letsencrypt/live/data.mydomain.net/fullchain.pem
/etc/letsencrypt/live/data.mydomain.net/privkey.pem
As soon as I enable HTTPS in the configuration.yml I am unable to launch ROS.
There are no error messages written to:
/var/log/realm-object-server.log
Here is a copy of the proxy section of configuration.yml.
http:
## Whether or not to enable the HTTP proxy module. It enables multiplexing requests
## by forwarding incoming requests on a single port to all services.
# enable: true
## The address/interface on which the HTTP proxy module should listen. This defaults
## to 127.0.0.1. If you wish to listen on all available interfaces,
## uncomment the following line.
# listen_address: '::'
## The port that the HTTP proxy module should bind to.
# listen_port: 9080
https:
## Whether or not to enable the HTTPS proxy module. It enables multiplexing requests
## by forwarding incoming requests on a single port to all services.
## Note that even if it enabled, the HTTPS proxy will only start if supplied
## with a valid pair of certificates through certificate_path and private_key_path below.
enable: true
## The path to the certificate and private keys (in PEM format) that will be used
## to set up the HTTPS server accepting connections.
## These configuration options are MANDATORY to start the HTTPS proxy module.
certificate_path: '/etc/letsencrypt/live/data.mydomain.net/fullchain.pem'
private_key_path: '/etc/letsencrypt/live/data.mydomain.net/privkey.pem'
## The address/interface on which the HTTPS proxy module should listen. This defaults
## to 127.0.0.1. If you wish to listen on all available interfaces,
## uncomment the following line.
# listen_address: '::'
## The port that the HTTPS proxy module should bind to.
listen_port: 9443
As I mention. The issue appears to be that as soon as I configure HTTPS the ROS server fails to start. If I disable the HTTPS then the ROS server starts without issue.
The reason I believe ROS is failing to start is - if I attempt curl 127.0.0.1:9080 or curl 127.0.0.1:9443 from the terminal I get the message curl: (7) Failed to connect to 127.0.0.1 port 9443: Connection refused
I'd love to hear your ideas/thoughts/suggestions on how I can get this to work. Cheers. Ian
Thanks to user #Radu - the answer was Permissions.
The realm user did not have permission to read the .pem files.
I picked up the answer from this answer.
Https Proxy for Realm Object Server not working
#Radu - is the man!

Yaws with SSL gives the error "SSL accept failed: timeout"

I used certbot to generate a Let's encrypt certificate for my website, but Yaws gives me an SSL accept failed: timeout error when I try to connect to it (after it times out of course). Interestingly it works when I redirect example.com to the local ip address of the server in the hosts file on my machine and connect to example.com:8080, but not when I connect to example.com without editing the hosts file or when I connect from my phone over 4G. Here's my webserver's configuration file (it is the only configuration file in conf.d):
<server www.example.com>
port = 8080
listen = 0.0.0.0
docroot = /usr/share/yaws
<ssl>
keyfile = /etc/letsencrypt/live/example.com/privkey.pem
certfile = /etc/letsencrypt/live/example.com/fullchain.pem
</ssl>
</server>
I made sure that the keyfile and the certificate are both readable by the yaws user. Next to the keyfiles is a README that contains the following:
`privkey.pem` : the private key for your certificate.
`fullchain.pem`: the certificate file used in most server software.
`chain.pem` : used for OCSP stapling in Nginx >=1.3.7.
`cert.pem` : will break many server configurations, and should not be used
without reading further documentation (see link below).
We recommend not moving these files. For more information, see the Certbot
User Guide at https://certbot.eff.org/docs/using.html#where-are-my-certificates.
So I'm relatively sure I've used the right file (the other ones gave me errors like badmatch and {tls_alert,"decrypt error"}). I also tried trivial things like writing https:// before the URL, but it didn't fix the issue, also, everything works fine when the server is running without SSL. The version of Erlang running on my server is Erlang/OTP 19. Also, if it's unclear, the domain isn't actually example.com.
Also, example.com is redirected via cname to examplecom.duckdns.org, if that matters.
UPDATE:
My server was listening on port 8080, that was forwarded from the external port 80, for https connections, when the default https port is port 443. My other mistake was connecting to http://example.com instead of https://example.com. Forwarding the external port 443 to the internal port 8443 and configuring yaws to listen on port 8443 fixed everything.
Just to be sure to understand, when you do something like curl -v https://example.com:8080, you get a timeout, that's it ? (here https protocol and port 8080 are mandatory of course)
SSL timeout during accept can be triggered when an unencrypted request is received on a SSL vhost.
Could you also provide the output of the following command:
echo -e "HEAD / HTTP/1.0\r\n\r\n" | openssl s_client -connect mysite.com:8080 -ign_eof
And finally, which version of Yaws are you running ? on which OS ?

Stream with Wowza over SSL, Connection Refused or invalid response

I installed Wowza and is Streaming by this links:
HTTP:
http ://[my-ip]:1935/myapp/definst/mp4:00.Intro.mp4/manifest.mpd
and also on
http ://[my-subdomain]:1935/myapp/definst/mp4:00.Intro.mp4/manifest.mpd
When is config Wowza to be able to stream on port 80, it works again on these links:
http ://[my-ip]/myapp/definst/mp4:00.Intro.mp4/manifest.mpd
http ://[my-subdomain]/myapp/definst/mp4:00.Intro.mp4/manifest.mpd
but we must stream over SSL protocol.
means: HTTPS:
https ://[my-subdomain]/myapp/definst/mp4:00.Intro.mp4/manifest.mpd
We installed a wildcard SSL on our server and everything is working great. In general, port 1935 does not work over HTTPS! even when we add port 80 to Wowza, HTTPS connection is refused and we can't have streaming over https.
How can we stream over SSL on wowza? even with or without port 1935
Thanks
Yes, Wowza server supports streaming with SSL using StreamLock or your own SSL certificate.
You will need to set up a different port number for HTTPS. It could be that another process is using port 80. Port 443 is typically used.
From the Server tab, click Edit.
Click Add Host Port and fill in fields.
Check Enable SSL/StreamLock.
Save and re-start Wowza server.
Look in [install-dir]/logs/wowzastreamingengine_access.log for errors. It will give a clue as to whether there is a problem with the certificate, password or other.
I was recommend place a LB infront of my Wowza for SSL offloading so you can load the m3u8 over SSL. I was also told you can do that quite easily using HA Proxy for example. It is explained how to accomplish this here for RTMP but the same can obviously done with HTTP:
https://github.com/arut/nginx-rtmp-module/issues/457#issuecomment-250783255
Note, I have not tried this yet and I am unclear on exactly the proper use scenario. Nor, have I successfully enable StreamLock with my own cert nor the cert provided through Wowza. If I manage to do so I will update this thread. Hope this is helpful.

Client-side SSL not working with AWS API Gateway

I generated a client-side SSL Certificate on API Gateway and added it to my nginx configuration as below:
listen *:443;
ssl on;
server_name api.xxxx.com;
ssl_certificate /etc/letsencrypt/live/api.xxxx.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.xxxx.com/privkey.pem;
ssl_verify_client on;
ssl_client_certificate /etc/nginx/ssl/awsapigateway.crt;
location /home/ubuntu/api {
# if ($ssl_client_verify != SUCCESS) { return 403; }
# proxy_pass http://my.http.public.endpoint.com;
# proxy_set_header X-Client-Verify $ssl_client_verify;
}
The client certificate doesn't work after testing via the AWS API gateway test console. It ends up with Error 400 - No required SSL certificate was sent. API Gateway should be sending its client cert to my server with each request, so that I can validate that requests are genuinely coming from API Gateway.
I believe the reason it is not working is I am adding the PEM-encoded public key from the AWS API gateway console directly to awsapigateway.crt. Is that correct?
Additionally, does nginx support self-signed client SSL certificates, which is what AWS is providing us?
Api Gateway team here.
It looks like the nginx configuration is correct. And for our simple test case we use a node server and simply write the PEM certificate from the console directly to the crt file that is set as the ca, or in this case the ssl_client_certificate.
I'd also test using the actual deployed API if for some reason the test function in the console has an issue. Make sure to use the Stage settings to specify the cert.