HAProxy community

Haproxy ssl termination and upstream forward proxy

Problem:

Iam trying to build a forward proxy with ssl termination, further it upstreams to my proxy servers eg: TOR. My upstream proxy services are non-https.

Client -> Network-Haproxy -> Uptstream-Proxy -> Internet

I could easily succeed in tcp mode of HAproxy without ssl termination, but when I terminate ssl and forward, things don’t work.

Steps Followed:

I followed the below steps to generate self-certified ssl certificates.

$ openssl req -new -newkey rsa:2048 -sha256 -days 365 -nodes -x509 -extensions v3_ca -keyout haproxy-ca-key.pem -out haproxy-ca-cert.pem -subj "/C=GB/ST=London/L=London/O=Global Security/OU=IT Department/CN=example.com"

combined them for creating final .pem file

$ cat haproxy-ca-cert.pem haproxy-ca-key.pem >> mysite.pem

The above file is used in my haproxy.cfg for ssl termination.

global
	log /dev/log	local0
	log /dev/log	local1 notice
	chroot /var/lib/haproxy
	stats socket /run/haproxy/admin.sock mode 660 level admin
	stats timeout 30s
	user haproxy
	group haproxy
	daemon

	# Default SSL material locations
	ca-base /etc/ssl/certs
	crt-base /etc/ssl/private

	# Default ciphers to use on SSL-enabled listening sockets.
	# For more information, see ciphers(1SSL). This list is from:
	#  https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
	ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256::RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
	ssl-default-bind-options no-sslv3

defaults
	log	global
	mode	http
	option	httplog
	option	dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000
	errorfile 400 /etc/haproxy/errors/400.http
	errorfile 403 /etc/haproxy/errors/403.http
	errorfile 408 /etc/haproxy/errors/408.http
	errorfile 500 /etc/haproxy/errors/500.http
	errorfile 502 /etc/haproxy/errors/502.http
	errorfile 503 /etc/haproxy/errors/503.http
	errorfile 504 /etc/haproxy/errors/504.http

	stats enable
	stats uri /stats
   	stats realm Haproxy\ Statistics
   	stats auth user:password


frontend www.mysite.com
    mode http
    bind 0.0.0.0:8443
    bind 0.0.0.0:443 ssl crt /home/ubuntu/haproxy/mysite.pem crt-ignore-err all
    redirect scheme https if !{ ssl_fc }
    default_backend web_servers

backend web_servers
    mode http
    balance roundrobin
    server server1 xx.xx.xx.xx:xxxx #my upstream server which is not ssl protected

When I try to curl from my client machine to use the above proxy I get following error.

$ curl -k --proxy https://my-haproxy-server:443 --cacert haproxy-ca-cert.pem  https://httpbin.org/ip -vvv
*   Trying my-haproxy-server...
* TCP_NODELAY set
* Connected to my-haproxy-server (my-haproxy-server) port 443 (#0)
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Unknown (8):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (OUT), TLS alert, Server hello (2):
* SSL certificate problem: self signed certificate
* Closing connection 0
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

If you have read the cfg file, you can see I have redirected :8443 to :443, so I can send request to non https proxy, but that too doesn’t work

$ curl -k --proxy http://my-haproxy-server:8443 --cacert haproxy-ca-cert.pem  https://httpbin.org/ip -vvv
*   Trying my-haproxy-server...
* TCP_NODELAY set
* Connected to my-haproxy-server (my-haproxy-server) port 8443 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to httpbin.org:443
> CONNECT httpbin.org:443 HTTP/1.1
> Host: httpbin.org:443
> User-Agent: curl/7.58.0
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.1 302 Found
< content-length: 0
< location: https://httpbin.org:443/
< cache-control: no-cache
< connection: close
< 
* Received HTTP code 302 from proxy after CONNECT
* CONNECT phase completed!
* Closing connection 0
curl: (56) Received HTTP code 302 from proxy after CONNECT

Any lead would be appreciated.

Extra Info:

When you instruct curl to use a HTTPS proxy, you need to use the correct curl arguments for the SSL parameters:

It’s --proxy-cacert instead of --cacert:

https://curl.haxx.se/docs/manpage.html#--proxy-cacert

And it’s --proxy-insecure instead of -k or --insecure:

https://curl.haxx.se/docs/manpage.html#--proxy-insecure

@lukastribus Thank you for your response.

that didn’t work, Iam getting error

$ curl --proxy https://my-haproxy-server:443 --proxy-cacert haproxy-ca-cert.pem https://httpbin.org/ip -vvv
*   Trying my-haproxy-server...
* TCP_NODELAY set
* Connected to my-haproxy-server (my-haproxy-server) port 443 (#0)
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: haproxy-ca-cert.pem
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Unknown (8):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (OUT), TLS alert, Server hello (2):
* SSL certificate problem: unsupported certificate purpose
* Closing connection 0
curl: (60) SSL certificate problem: unsupported certificate purpose
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

This too dint work

$ curl --proxy https://my-haproxy-server:443 --proxy-cacert haproxy-ca-cert.pem https://httpbin.org/ip -vvv --proxy-insecure
*   Trying my-haproxy-server...
* TCP_NODELAY set
* Connected to my-haproxy-server (my-haproxy-server) port 443 (#0)
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: haproxy-ca-cert.pem
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Unknown (8):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Client hello (1):
* TLSv1.3 (OUT), TLS Unknown, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server did not agree to a protocol
* Proxy certificate:
*  subject: CN=haproxy; O=haproxy
*  start date: Feb 29 11:55:39 2020 GMT
*  expire date: Mar  2 11:55:39 2023 GMT
*  issuer: CN=haproxy; O=haproxy
*  SSL certificate verify result: unsupported certificate purpose (26), continuing anyway.
* allocate connect buffer!
* Establish HTTP proxy tunnel to httpbin.org:443
* TLSv1.3 (OUT), TLS Unknown, Unknown (23):
> CONNECT httpbin.org:443 HTTP/1.1
> Host: httpbin.org:443
> User-Agent: curl/7.58.0
> Proxy-Connection: Keep-Alive
> 
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS Unknown, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS Unknown, Unknown (23):
< HTTP/1.0 400 Bad request
< Cache-Control: no-cache
< Content-Type: text/html
< 
* Received HTTP code 400 from proxy after CONNECT
* CONNECT phase completed!
* Closing connection 0
* TLSv1.3 (OUT), TLS Unknown, Unknown (21):
curl: (56) Received HTTP code 400 from proxy after CONNECT

Something is wrong with the certificate, but that’s probably not important (if you can ignore proxy SSL issues with --proxy-insecure).

I think it doesn’t make sense to use mode http here. CONNECT is probably interpreted by haproxy instead of your backend proxy server.

Why do you need HTTP mode? Can’t you just use SSL termination with TCP mode?

I think termination only works in HTTP mode. In TCP mode it directly forwards the connection right?

SSL termination works just fine with TCP mode. Just because you CAN passthrough SSL in tcp mode, doesn’t mean you cannot SSL terminate with TCP mode, and pass only the actual unencrypted payload to the backend.