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

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.

Related

HAproxy with standalone mod_security routed to multiple web servers

I want to have one server with HAproxy and a standalone mod_security installed which routes every packets to mod_security first and check by its rules.
Then if there wasn't anything suspicious in packets (SQL Injection, DOS Attacks, ...) pass them back from mod_security to haproxy and haproxy routes them to multiple servers with different webservers.
Therefore I don't need to install and config mod_security on all my webservers.
This is technically possible, possibly with running 2 instances of HAProxy. However, you will need a webserver to run underneath ModSec, typically Apache or nginx, and this kind of negates the advantage of not having to install ModSec on all your webservers.
The standard setup is: haproxy -> reverse-proxies with ModSec -> application-servers
Just to answer this old, but still valid, question:
The solution should be to use HAProxies Stream Processing Offload Engine (SPOE) through the Stream Processing Offload Protocol (SPOP) to talk a Stream Processing Offload Agent (SPOA) which is a standalone modsecurity daemon.
HAProxy example config from their github repo
frontend my-front
...
filter spoe engine modsecurity config spoe-modsecurity.conf
...
enter code here
backend spoe-modsecurity
mode tcp
balance roundrobin
timeout connect 5s
timeout server 3m
server modsec1 127.0.0.1:12345
# Block potential malicious requests with returncode < 0
http-request deny if { var(txn.modsec.code) -m int gt 0 }
There's also a Github project where the daemon has been made available as Docker container
Offical HAProxy blog post

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!

rtorrent through a proxy

It's not that much of a question, rather a confirmation that what I did is right or not and if it is safe or not.
Until now what I have found googling around is that you cannot run rtorrent through a proxy. You can either put the http request through a proxy, or tsocks, in both cases either the actual transfers are done directly or not done at all. Therefore until now the only proposed viable solution is a VPN which I wanted to avoid.
What I did was use an http proxy for the http part and a port forwarding for the actual download part. For example, lets assume the following:
192.168.1.10 --> Local machine with the actual rtorrent
remote.machine.com --> The remote machine used as a proxy
Procedure:
I created 2 ssh tunnels
ssh -N -D 9090 user#remote.machine.com
ssh -R 9091:localhost:9091 user#remote.machine.com
From the local machine I installed polipo as the html proxy and configured it to use a socks proxy in the remote.machine.com.
I edited the following lines in /etc/polipo/config so that I can get the socks proxy.
socksParentProxy = "localhost:9090"
socksProxyType = socks5
I also changed the html proxy port for extra security, again in /etc/polipo/config
proxyPort = 9080
On the local machine I changed the ~/.rtorrent.rc as following:
#Proxy of the http requests through polipo
http_proxy=localhost:9080
# The ip address reported to the tracker.
#Really important, in order to get connections for downloads
ip = remote.machine.com
# The ip address the listening socket and outgoing connections is
# bound to.
bind = 192.168.1.10
# Port range to use for listening.
port_range = 9091-9091
# Start opening ports at a random position within the port range.
port_random = no
The system seems to work. I connect to the trackers and I have up and down traffic. So the questions are:
Am I safe that all the traffic concerning rtorrent is done through the remote.machine.com?
Did I miss something?
Are there any problems or concerns regarding this method?
As far as I see, you have covered inbound connections, as well as outgoing HTTP traffic, but any outbound peer-to-peer connections will be created directly, not through any tunnel. Currently, rtorrent does not appear to support passing outbound P2P connections through a tunnel or proxy of any kind, so in order to handle these, you'll need some other mechanism.
You mentioned tsocks and that it does not work – not even in addition to the rtorrent configuration you have set up above? (Although with tsocks you should be able to drop the HTTP proxy part.)
If that fails, there are alternatives to tsocks mentioned on the tsocks project page. A slightly more involved alternative would be to create a new loopback interface (lo:1 with IP 127.0.0.2), bind your rtorrent to that one and use something like sshuttle to direct all traffic originating on that interface through an SSH tunnel. Unfortunately, sshuttle doesn't let you restrict its operation to a specific interface at the moment, though, so you'd have to fiddle with the iptables rules it creates to make them match your needs. I assume a patch adding this feature to sshuttle would be welcome.
As a side note, you can create multiple port forwards and SOCKS proxies in a single SSH connection, like this:
ssh -N -D 9090 -R 0.0.0.0:9091:localhost:9091 myself#my.example.com

F5 iRule to manage HTTP proxy CONNECT requests

I have a F5 Virtual Server configured with client-side https encryption, in front of a web server.
I would like this VS to manage also HTTP CONNECT requests, so that clients can request it either as a web server, or as a proxy.
That is to say, the VS should decrypt all TCP connections, but if the first TCP packet starts with "CONNECT", it should first respond HTTP 200, then wait for the next packet (that must be "client hello") and process the SSL handshake.
It is certainly possible with some iRule, but I can't easily get a solution, and I can't find any help on Internet, since F5 doc is not open. Does anyboy know how to do it ?
I finally found a solution.
The VS default behavour is to process the SSL decryption right from the first TCP packet :
so one must look at the first TCP packet, and, if it starts with CONNECT,
disable SSL decryption,
respond with HTTP 200,
then reenable SSL decryption for the "client hello" that should come right after
It works in both context :
in TCP context, before SSL decryption, to detect CONNECT request
and in HTTP context, to properly respond to the CONNECT request
when CLIENT_ACCEPTED { # TCP CONTEXT
TCP::collect 7 # look at the first 7 bytes of TCP stream
}
when CLIENT_DATA {
if { [TCP::payload] starts_with "CONNECT" } {
SSL::disable # disable SSL decryption
}
}
when HTTP_REQUEST { # HTTP CONTEXT
if { [HTTP::method] eq "CONNECT" } {
HTTP::respond 200 # send HTTP 200
SSL::enable # re-enable SSL decryption for next "client hello"
}
}

Deploy pyramid app with uwsgi and apache

I have a pyramid app in a virtualenv, and I want to deploy it using apache+uwsgi, but I don't figure how to connect apache, uwsgi and pyramid. Can anyone provide an apache.conf and production.ini example?
In many examples you should create an init script to manage the uWSGI process, but can apache start the uwsgi process?
I can't help with the Apache side of things, but here's my uwsgi config.ini that would work in your case. Apache does not support the native uwsgi protocol, so you will need to configure uwsgi to speak http as below. Then, Apache will need to be configured to proxy requests to a backend http server.
http = 127.0.0.1:33932 ;also could use unix file socket
master = 1
workers = 5 ;number of cpus + 1
harakiri = 30 ;seconds = request timeout length
buffer-size = 32768 ; found experimentally. Your app may work well with less or more.
chdir = /path/to/your/uwsgi_webapp
module = your_module
callable = your_wsgi_app
virtualenv = /path/to/your/uwsgi_webapp/env
daemonize = /path/to/your/uwsgi_webapp/log/uwsgi.log
pidfile = /path/to/your/uwsgi_webapp/uwsgi_fuzzy.pid
Note that using Apache as a front-end webserver is not a recommended configuration. Starting up a complete Apache process just to proxy to another http server will result in fairly poor performance. For that reason, a lightweight server like nginx or a http proxy like Varnish is preferred.