What's the canonical way to handle port 80 and port 443?


#1

Actual goal

I want to be able to access both

http://pihole2.mydomain.tld/admin
https://pihole2.mydomain.tld/admin

I am doing this

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
        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/
        # An alternative list with additional directives can be obtained from
        #  https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy
        ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
        ssl-default-bind-options no-sslv3

listen  stats   
        bind            192.168.1.223:1936
        mode            http
        log             global

        maxconn 10

        stats enable
        stats hide-version
        stats refresh 30s
        stats show-node
        stats auth notadmin:notistherealpassword
        stats uri  /haproxy?stats

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

frontend http
        bind *:80
        option forwardfor

        acl acl_pihole2_http hdr(host) -i pihole2.mydomain.tld
        use_backend pihole2_http if acl_pihole2_http

frontend https   
        bind *:443
        option forwardfor

        acl acl_pihole2_https hdr(host) -i pihole2.mydomain.tld
        use_backend pihole2_https if acl_pihole2_https

backend pihole2_http
        server web 192.168.1.228:80 check

backend pihole2_https
        server web 192.168.1.228:443 check

It’s working well, even certbot running on the ,228.

I ask you a kindle opinion about configuration. It’s my first days in HAProxy and I’d like to start with right direction.

To be more precise: do I need a separate frontend and backend section? Cannot I simple listen to 80 and 443 ina single frontend and use a single backend ?

Also, I forced a 301 redirect from http to https in the server. Is it right or must I do it here at HAProxy level?

Thanks in advance


#2

Right or wrong really depends on what you are looking for. What is it that you want to achieve exactly?

I doubt the https works though, you cannot match a HTTP header when you are passing TLS transparently to the backend, because it is encrypted.


#3

I doubt the https works though, you cannot match a HTTP header when you are passing TLS transparently to the backend, because it is encrypted

Could you explain this, please?

I just tried accessing from my pc (same lan segment of pihole and of the haproxy)

https://pihole2.mydomain.tld/admin/

and it works.

BuT: it do not works when loaded from outside the lan, I didn’t tried this before reading your post. If I use my phone network connection, the ip is resolved, but than I got a protocol error.


#4

I updated question posting the FULL configuration file. I thinked originally to post only relevant (for me) sections, sorry


#5

I assume pihole2.mydomain.tld resolves to 192.168.1.228, bypassing haproxy, which is why this appears to work from the LAN.

To pass encrypted traffic from frontend to the backend, you need to use TCP mode or terminate TLS at haproxy. You also cannot access HTTP headers, when passing through TLS - because it is encrypted.

I still don’t know what you are trying to achieve. Do you just want to forward 2 TCP ports? Explain what your goal here looks like.


#6

The ultimate goal is to handle 3 webservers running at different internal ips, both on port 80 and port 443 for ssl.

This is why I am using http mode; i read that I cannot discriminate traffic based on domain when using tcp mode.

But to start I just want that my domain can be accessed from internet using both 80 and 443 port, for http and https. Sorry, I confess I thinked it was just a matter of 2 redirects

- http//pihole2.mydomain.tld   => 192.168.1.228:80
- https//pihole2.mydomain.tld   => 192.168.1.228:443

I read this on Stack Overflow: https://stackoverflow.com/questions/41663450/haproxy-port-80-and-443-to-backend80-and-backend443

frontend http-https-in
bind    35.154.100.100:80
bind    35.154.100.100:443

use_backend http_nginx_pool    if !{ ssl_fc }
use_backend https_nginx_pool   if { ssl_fc }

backend http_nginx_pool
    mode http
    server nginx2 10.233.32.143:80 check

backend https_nginx_pool
    mode http
    server nginx2 10.233.32.143:443 check

Is it wrong?


#7

I’m restudying the theory. I started from SSL termination

I’ve SSL certificate files into pihole2 (IP ending with 228)

root@pihole2:/etc/letsencrypt/live/pihole2.mydomain.tls# ls -l
totale 8
lrwxrwxrwx 1 www-data root   49 ago 22 02:09 cert.pem -> ../../archive/pihole2.mydomain.tld/cert1.pem
lrwxrwxrwx 1 www-data root   50 ago 22 02:09 chain.pem -> ../../archive/pihole2.mydomain.tld/chain1.pem
lrwxrwxrwx 1 www-data root   54 ago 22 02:09 fullchain.pem -> ../../archive/pihole2.mydomain.tld/fullchain1.pem
lrwxrwxrwx 1 www-data root   52 ago 22 02:09 privkey.pem -> ../../archive/pihole2.mydomain.tld/privkey1.pem
-rw-r--r-- 1 www-data root  543 ago 22 02:09 README

I created a combined.pem in the same folder

cat /etc/letsencrypt/live/pihole2.mydomain.tld/privkey.pem \
    /etc/letsencrypt/live/pihole2.mydomain.tld/cert.pem | \
tee /etc/letsencrypt/live/pihole2.mydomain.tld/combined.pem

root@pihole2:/etc/letsencrypt/live/pihole2.mydomain.tld# cat combined.pem 
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDWPrm1oUuKMQIj
... [cut] ...
hwEw5NJBVKXItCgwAFjXKjM+
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIGHDCCBQSgAwIBAgISA7mTzPkUWtlYaqGS/Jj5wIDDMA0GCSqGSIb3DQEBCwUA
... [cut] ...
zZHXjpBkSUTmUViiHKDEBs3WER9tnEL3eWRzNVc1jf0=
-----END CERTIFICATE-----

I copied combined.pem from pihole2 (228) to haproxy (223) as /etc/ssl/private/pihole2.mydomain.tld.pem

root@haproxy1:/etc/ssl/private# ls -la
totale 12
drwx------ 2 root root 4096 ago 23 01:25 .
drwxr-xr-x 4 root root 4096 ago 12 17:08 ..
-rw-r--r-- 1 root root 3883 ago 23 01:25 pihole2.mydomain.tld.pem

Then I rewrote the haproxy config in this way:

frontend web
        
        bind *:80
        bind *:443 ssl crt /etc/ssl/private/pihole2.mydomain.tld.pem

        redirect scheme https if !{ ssl_fc }

        mode http

        option forwardfor
        reqadd X-Forwarded-Proto:\ https

        acl acl_pihole2_https hdr(host) -i pihole2.mydomain.tld
        use_backend pihole2_https if acl_pihole2_https

backend pihole2_https
        server web 192.168.1.228:443 check

What I expect now is to gain https access to pihole2 from outside the lan, both using http and https

http://pihole2.mydmain.tld ->  https://pihole2.mydmain.tld -> https://192.168.1.228:443

and

https://pihole2.mydmain.tld -> https://192.168.1.228:443

From a non-LAN device, i run

curl -sSL https://pihole2.mydonot.tld

This is actual error

SSL certificate problem: unable to get local issuer certificate


#8

Going on, I Googled for the previous error.

I came to this server fault question

The answer made me think

  1. I need to change order of inclusion in the combined pem
  2. I need to generate a certificate for the intermediat host, haproxy.

Something like this

-----BEGIN PRIVATE KEY-----
[private key of target gost]
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
[Certificate of target host]
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
[Intermidate (HAProxy) certificate]
-----END CERTIFICATE-----

I am trying this way. I’ll update you asap.

Also, I used this online SSL checker and got the same information

### The hostname (pihole2.mydomain.tld) is correctly listed in the certificate.
### The certificate is not trusted in all web browsers. You may need to install 
an Intermediate/chain certificate to link it to a trusted root certificate

#9

Changed chaining

cat /etc/letsencrypt/live/pihole2.mydomain.tld/privkey.pem 
     /etc/letsencrypt/live/pihole2.mydomain.tld/fullchain.pem | 
tee /etc/letsencrypt/live/pihole2.mydomain.tld/combined.pem

Now the online checker approve it

pihole2.mydomain.tld resolves to <ip>
 	
The certificate should be trusted by all major web browsers (all the correct intermediate certificates are installed).
 	
The certificate will expire in 88 days.	
 	
The hostname (pihole2.mydomain.tld) is correctly listed in the certificate.

SERVER
Common name: pihole2.mydomain.tld
SANs: pihole2.mydomain.tld
Valid from August 21, 2018 to November 19, 2018
Serial Number: 03b993ccf9145ad9586aa192fc98f9c080c3
Signature Algorithm: sha256WithRSAEncryption
Issuer: Let's Encrypt Authority X3	

CHAIN
Common name: Let's Encrypt Authority X3
Organization: Let's Encrypt
Location: US
Valid from March 17, 2016 to March 17, 2021
Serial Number: 0a0141420000015385736a0b85eca708
Signature Algorithm: sha256WithRSAEncryption
Issuer: DST Root CA X3	

Somthing with SSL dialog/forward/whatelse is still broken.

realtebo@vmi81507:~$ curl -verbose https://pihole2.mydomain.tld
* Rebuilt URL to: https://pihole2.mydomain.tld/
*   Trying <ip>...
* Connected to pihole2.mydomain.tld (<ip>) port 443 (#0)
* found 148 certificates in /etc/ssl/certs/ca-certificates.crt
* found 596 certificates in /etc/ssl/certs
* ALPN, offering http/1.1
* SSL connection using TLS1.2 / ECDHE_RSA_AES_256_GCM_SHA384
* 	 server certificate verification OK
* 	 server certificate status verification SKIPPED
* 	 common name: pihole2.mydomain.tld (matched)
* 	 server certificate expiration date OK
* 	 server certificate activation date OK
* 	 certificate public key: RSA
* 	 certificate version: #3
* 	 subject: CN=pihole2.mydomain.tld
* 	 start date: Tue, 21 Aug 2018 23:09:42 GMT
* 	 expire date: Mon, 19 Nov 2018 23:09:42 GMT
* 	 issuer: C=US,O=Let's Encrypt,CN=Let's Encrypt Authority X3
* 	 compression: NULL
* ALPN, server did not agree to a protocol
> GET / HTTP/1.1
> Host: pihole2.mydomain.tld
> User-Agent: curl/7.47.0
> Accept: */*
> Referer: rbose
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 502 Bad Gateway
< Cache-Control: no-cache
< Connection: close
< Content-Type: text/html
< 
<html><body><h1>502 Bad Gateway</h1>
The server returned an invalid or incomplete response.
</body></html>

* Closing connection 0

I’m loosing hopes…


#10

Everything on the frontend looks good now, you just need to fix the backend: change it fromport 443 to port 80, since you are already doing TLS termination on haproxy (and you need to speak HTTP).


#11

Done !

I disabled auto elevation of http to https on lighttpd configuration.
Just for future readers, I commented out these rows:

# Redirect HTTP to HTTPS
# $HTTP["scheme"] == "http" {
#  $HTTP["host"] =~ ".*" {
#    url.redirect = (".*" => "https://%0$0")
#  }
#}

And changed backend from 443 to 80 as you suggeste

This is my final haproxy configuration. I help it could be for others in the future

As commented, I used mozilla SSL config generator

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

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

        # See https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy-1.8%2C8&openssl=1.0.1e&hsts=no&profile=modern
        ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
        ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
        ssl-default-server-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
        ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets


listen  stats   
        bind            192.168.1.223:1936
        mode            http
        log             global
        
        maxconn 10

        timeout connect 5000
        timeout client  50000
        timeout server  50000

        stats enable
        stats hide-version
        stats refresh 30s
        stats show-node
        stats auth notadmin:notistherealpassword
        stats uri  /haproxy?stats

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull

        timeout connect 10s
        timeout client  30s
        timeout server  30s
        timeout check   5s

        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

frontend web

        mode http
        
        bind *:80
        bind *:443 ssl crt /etc/ssl/private/pihole2.mydomain.tld.pem alpn h2,http/1.1

        reqadd X-Forwarded-Proto:\ https
        redirect scheme https if !{ ssl_fc }

        option forwardfor
        
        acl acl_pihole2_https hdr(host) -i pihole2.mydomain.tld
        use_backend pihole2 if acl_pihole2_https

backend pihole2
        # Why port 80?
        # See https://discourse.haproxy.org/t/whats-the-canonical-way-to-handle-port-80-and-port-443/2903/10?u=realtebo
        server web 192.168.1.228:80 check