HAProxy SSL passthrough and ACL rules not working - haproxy

I'm trying to get SSL passthrough working so only my backends need SSL and not the HAProxy frontends.
I also want to use ACL rules to only allow certain domains to get sent to the backend and those that do not match will get another backend.
So far I have this, but it seems to not be working:
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
maxconn 10240
nbproc 4
nbthread 1
cpu-map auto:1/1-4 0-3
defaults
log global
mode http
option httplog
option dontlognull
maxconn 2048
retries 3
timeout connect 10s
timeout client 30s
timeout server 30s
timeout http-request 10s
timeout http-keep-alive 2s
timeout queue 5s
timeout tunnel 2m
timeout client-fin 2s
timeout server-fin
frontend abc.com
bind AAA.BBB.CCC.DDD:80
bind AAA.BBB.CCC.DDD:443
acl ValidDomain hdr_dom(host) -i abc.com
use_backend abc.com if ValidDomain
default_backend fallback
backend abc.com
balance static-rr
server default ZZZ.YYY.XXX.WWW:443 ssl check verify none
backend fallback
balance static-rr
server fallback 127.0.0.1:8080
Can anyone figure out why it isn't working?

What you need is the mode tcp instead of mode http and the can your route based on SNI.
You can get a example solution in this answer Serving LDAPS lookups over HAProxy, unable to bind in testing

Related

Haproxy request gets timeout when doing ACL

I created one simple application and i'm running it in 4 different containers.
I managed to proxy all the ports to the bind 9991, but when i create acl to use different backends it does'nt work, only the default back-end.
Any tips for using haproxy will be very usefull!
haproxy.cfg :
frontend tests
bind *:9991
timeout client 60s
mode http
acl is_servers_2 path_end /app2
use_backend servers_2 if is_servers_2
acl is_servers_3 path_end /app3
use_backend servers_3 if is_servers_3
default_backend servers_2
backend servers_2
timeout connect 10s
timeout server 60s
mode http
server server3001 127.0.0.1:3001
server server3002 127.0.0.1:3002
backend servers_3
timeout connect 10s
timeout server 60s
mode http
server server3003 127.0.0.1:3003
server server3004 127.0.0.1:3004
haproxy version: 2.4.4
ubuntu: 18.04
Indeed my api was the problem.
It wasn't configured to handle /app2 and /app3 path urls, just /

How to configure HAProxy with Certbot/letsencrypt

I have one domain with several sub-domains. All of them point to my VPS. I use docker in swarm mode to run my web services and my (HA)proxy.
I also use Certbot/Let’s Encrypt in standalone to get the certificate for my domain.
Everything seems to be working fine except the HAproxy configuration for certbot.
Here is my HAProxy config:
defaults
mode http
log global
option httplog
option dontlognull
timeout check 5s
timeout connect 5s
timeout client 50s
timeout client-fin 50s
timeout server 50s
timeout tunnel 1h
resolvers docker
nameserver dns1 127.0.0.11:53
resolve_retries 3
timeout resolve 1s
timeout retry 1s
hold other 10s
hold refused 10s
hold nx 10s
hold timeout 10s
hold valid 10s
hold obsolete 10s
# HTTP(S) frontend
frontend web-in
bind *:80
bind *:443 ssl crt /etc/ssl/haproxy.pem
# test URI to see if its a letsencrypt request
acl letsencrypt-acl path_beg /.well-known/acme-challenge/
use_backend be-letsencrypt if letsencrypt-acl
# sub-domains
acl sub1 hdr(host) -i sub1.mydomain.com
acl sub2 hdr(host) -i sub2.mydomain.com
acl sub3 hdr(host) -i sub3.mydomain.com
# figure out which one to use
use_backend be-sub1 if sub1
use_backend be-sub2 if sub2
use_backend be-sub3 if sub3
# Default
default_backend maintenance
# Backend | LE
backend be-letsencrypt
server letsencrypt dockerhost:8888 check resolvers docker init-addr none
# Sub1
backend be-sub1
option forwardfor
http-request add-header X-Forwarded-Proto https
redirect scheme https code 301 if !{ ssl_fc }
server sub1-server webservice1:8080 check resolvers docker init-addr libc,none
# ... Sub2, Sub3 ...
# Default backend
backend maintenance
balance roundrobin
I expect all the following urls:
mydomain.com/.well-known/acme-challenge/
sub1.mydomain.com/.well-known/acme-challenge/
sub2.mydomain.com/.well-known/acme-challenge/
sub3.mydomain.com/.well-known/acme-challenge/
to resolve to dockerhost:8888. But instead I always get 503 Service Unavailable.
To test the configuration I run dockercloud/hello-world on the port 8888
docker run --rm -d -p 8888:80 dockercloud/hello-world
I know that dockerhost:8888 resolution work fine as I can test it with the following configuration:
# Sub1
backend be-sub1
server letsencrypt dockerhost:8888 check resolvers docker init-addr none
Meaning that when I hit my syb-domain sub1.mydomain.com I end up on the dockercloud/hello-world
"Hello world!" page.
So why path based resolution doesn't work ? Am I missing something ?
acl letsencrypt-acl path_beg /.well-known/acme-challenge/
use_backend be-letsencrypt if letsencrypt-acl
I had a very similar issue.
My verification failed with the same 503 error you describe. As I was wondering why that is since I saw the OpenPort of the certbot dockerimage on my machine and the redirects in the HAproxy logs -> I found out that since I was using HAproxy also in an docker Image and the backend server config was connecting to 127.0.0.1 within the HAproxy docker image which of corse cant work as the Port of the certbot was in another docker image I changed the server backend config to the IP address of the Host on which the docker image was running all worked well.
Maybe it helps someone ;) cheers

HAProxy, PGSQL with SSL and multiple clusters under single port

In my use case I'm using SSL to connect to the PG nodes, since I do not want to have SSL termination, I'm locked in to use TCP mode.
With TCP mode, I have no access to the header information, especially host. Because of this I can not use something like
# Primary - RW
frontend PGSQL_primary
bind *:5432
acl host_pglab hdr(host) -i pglab-db.local
acl host_stage hdr(host) -i stage-db.local
use_backend cluster_pglab-primary if host_pglab
use_backend cluster_stage-primary if host_stage
backend cluster_pglab-primary
option httpchk OPTIONS /master
http-check expect status 200
default-server inter 2s fall 2 rise 2 on-marked-down shutdown-sessions
server pglab-db-01 pglab-db-01.local:5432 maxconn 100 check check-ssl verify none port 8008
server pglab-db-02 pglab-db-02.local:5432 maxconn 100 check check-ssl verify none port 8008
backend cluster_stage-primary
option httpchk OPTIONS /master
http-check expect status 200
default-server inter 2s fall 2 rise 2 on-marked-down shutdown-sessions
server pglab-db-01 stage-db-01.local:5432 maxconn 100 check check-ssl verify none port 8008
server pglab-db-02 stage-db-02.local:5432 maxconn 100 check check-ssl verify none port 8008
From client connect to port 5432 and redirect the traffic to either pglab or stage cluster's primary node, depending on the hostname.
Is there some alternative to this, that I can avoid using new port for every cluster ?
I think you'll probably need a protocol-aware proxy like pgbouncer or pgpool.
Of the two I should think that pgbouncer is closer to haproxy in intention and usage.

How can I set the source ip as the client ip in haproxy tcp mode

I am using haproxy to proxy tcp flows to my tomcat backend. I would like to receive flows with the source ip address as the client ip (and not 127.0.0.1 as it is now). I've set the send-proxy option (I'm using haproxy 1.7.7) but it doesnt seem to make a difference. what am I doing wrong?
This is my haproxy config:
global
log 127.0.0.1 local4
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
# turn on stats unix socket
stats socket /var/lib/haproxy/stats
#------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
maxconn 3000
frontend https
mode tcp
option tcp-smart-accept
bind :::443 v4v6
acl from_sa src 10.203.239.135
use_backend tomcat-cp-events if from_sa
default_backend apache
backend apache
mode tcp
option tcp-smart-connect
server apache :50443 send-proxy
backend tomcat-cp-events
mode tcp
option tcp-smart-connect
server tomcat :54600 send-proxy

Layer4 "Connection refused" with haproxy

I need some advise on how to setup haproxy. I have two web-servers up and running. For testing they run a simple node server on port 8080.
Now on my haproxy server I start haproxy which gives me the following:
$> /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg
[WARNING] 325/202628 (16) : Server node-backend/server-a is DOWN, reason: Layer4 connection problem, info: "Connection refused", check duration: 0ms. 1 active and 0 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue.
[WARNING] 325/202631 (16) : Server node-backend/server-b is DOWN, reason: Layer4 timeout, check duration: 2001ms. 0 active and 0 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue.
[ALERT] 325/202631 (16) : backend 'node-backend' has no server available!
Just one note: If I do:
haproxy$> wget server-a:8080
I get the response from the node server.
Here is my haproxy.cfg:
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
log 127.0.0.1 local2
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
stats socket /var/lib/haproxy/stats
#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
mode tcp
log global
option tcplog
option dontlognull
option http-server-close
# option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
maxconn 3000
#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend www
bind *:80
default_backend node-backend
#---------------------------------------------------------------------
# round robin balancing between the various backends
#--------------------------------------------------------------------
backend node-backend
balance roundrobin
mode tcp
server server-a 172.19.0.2:8080 check
server server-b 172.19.0.3:8080 check
If I remove the check option it seems to work. Any suggestions how I can fix this checking mechanism of haproxy?
You need to get exact ip address of your server with the help of command
ifconfig
and correct the below address in your haproxy.cfg file:
172.19.0.2:8080
172.19.0.3:8080
or modify line like below
server server-a server-a:8080 check
server server-b server-b:8080 check
Remove "mode tcp" and change it to "mode http".
Im just guessing here but i suppose haproxy is doing a tcp check against your web server and the web server can not respond to it.
in "mode http" it checks the web server in http mode and expects a "response 200" for L4 check
and expects a string (whatever you defined) as a L7 check
eg. L4
backend node-backend
balance roundrobin
mode http #(NOT NEEDED IF DEFINED IN DEFAULTS)
option httpchk
server server-a 172.19.0.2:8080 check
server server-b 172.19.0.3:8080 check
eg. L7
backend node-backend
balance roundrobin
mode http #(NOT NEEDED IF DEFINED IN DEFAULTS)
option httpchk get /SOME_URI
http-check expect status 200
server server-a 172.19.0.2:8080 check
server server-b 172.19.0.3:8080 check
Another note related to #basickarl's comment on docker. If you are sending into a docker (docker-compose) instance (namely where you have multiple instances of service running) you likely need to define the docker resolver and use it for dns resolution on your backend:
resolver:
resolvers docker_resolver
nameserver dns 127.0.0.11:53
backend usage of resolver:
backend main
balance roundrobin
option http-keep-alive
server haproxyapp app:80 check inter 10s resolvers docker_resolver resolve-prefer ipv4
i tryied all this answers nothing works for me. only put the gateway IP of network work, for default bridge is 172.17.0.1.
In the servers put the : and with this haproxy connects with success.
My example of custom network with fixed ips and gateway:
----- haproxy config
backend be_pe_8545
mode http
balance roundrobin
server p1 172.20.0.254:18545 check inter 10s
server p2 172.20.0.254:28545 check inter 10s
----- docker app / network
docker_app: ...
networks:
public_network:
ipv4_address: 172.20.0.50
public_network:
name: public_network
driver: bridge
ipam:
driver: default
config:
- subnet: 172.20.0.0/24
gateway: 172.20.0.254