Size: 3502
Comment:
|
Size: 4032
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 3: | Line 3: |
'''`haproxy(1)`''' is a TCP load balancer and reverse proxy. | '''`haproxy(1)`''' is a TCP load balancing and reverse proxy server. |
Line 13: | Line 13: |
Most Linux and BSD distributions offer a `haproxy` package. | Most [[Linux]] and [[BSD]] distributions offer a `haproxy` package. === Containers === A [[Docker]] container image is available for most releases. These are available from [[Docker/Hub|DockerHub]] as `docker.io/library/haproxy` (or simply `haproxy` when using `docker(1)` specifically). The image runs as the non-root user `haproxy`, so it cannot bind to a [[Linux/Networking|privileged port]]. There are workarounds, but the upstream recommendation is to [[Docker/BridgeNetworks|properly configure bridge networks and DNS]] and then to configure `haproxy(1)` to [[#Name_Resolution|resolve names at runtime]]. |
Line 22: | Line 30: |
Line 48: | Line 58: |
=== SSL Termination === | === Encryption === |
Line 50: | Line 60: |
`haproxy(1)` uses PEM-formatted certificates, which are fundamentally just the certificate and private key concatenated. | See [[HAProxy/SSL|here]] for details. |
Line 52: | Line 62: |
To terminate the encryption, the `bind` instruction requires some additional parameters. It is also important to set a minimum version of TLS as a configuration option. | === Authentication === See [[HAProxy/Authentication|here]] for details. === Web Sockets === When a connection is upgraded to the websocket protocol, `haproxy(1)` implicitly drops to tunnel mode. Generally no other configuration is necessary. If you are redirecting HTTP traffic to HTTPS in the frontend, you will need to make that redirect further conditional on not being a websocket connection. |
Line 55: | Line 77: |
global ssl-default-bind-options ssl-min-ver TLSv1.2 frontend https_frontend bind *:443 ssl crt /path/to/the/pem/certificate alpn h2, http1.1 |
acl is_websocket hdr(Upgrade) -i WebSocket http-request redirect scheme https if !{ ssl_fc } !is_websocket |
Line 64: | Line 83: |
=== Conditional Routing === | |
Line 65: | Line 85: |
==== Using with Let's Encrypt Certificates ==== [[Encryption/LetsEncrypt|Let's Encrypt]] doesn't automatically generate a PEM-formatted certificate. The following script could be deployed as either a `cron(8)` job or a `certbot(1)` [[Encryption/Certbot#Hooks|post-installation hook]]. {{{ domain="example.com" dir="/etc/letsencrypt/live/${domain}" cat "${dir}/fullchain.pem" "${dir}/privkey.pem" > "${dir}/${domain}.pem" }}} === SSL Passthrough === If SSL/TSL certificates will not be handled by `haproxy(1)`, then configuration is much the same as with unencrypted traffic. The exception is that `mode` must be set to `tcp`, as HTTP headers will not be available for inspection. === Thorough Configuration === HTTP headers can be inspected to logically route traffic. Consider the below frontend: |
HTTP headers can be inspected to logically route traffic using '''access control lists''' ('''ACLs'''). Consider the below frontend: |
Line 99: | Line 98: |
#upgrade traffic to HTTPS http-request redirect scheme https unless { ssl_fc } |
|
Line 120: | Line 116: |
* Order matters! Don't upgrade traffic to HTTPS before handling ACME challenge traffic | === Name Resolution === `haproxy(1)` can be configured to resolve `server` addresses at runtime. First, configure a '''resolvers''': {{{ resolvers docker_dns nameserver docker1 127.0.0.11:53 }}} Second, configure servers to defer name resolution and provide instructions on how to resolve names. {{{ backend www_backend server www1 nginx:80 check resolvers docker_dns }}} Third, configure `haproxy(1)` to run regardless of name resolution errors. (The default behavior is to exit if any servers are unreachable.) {{{ defaults default-server init-addr none }}} === Stick Tables === See [[HAProxy/StickTable|here]] for details. |
HAProxy
haproxy(1) is a TCP load balancing and reverse proxy server.
Contents
Installation
Most Linux and BSD distributions offer a haproxy package.
Containers
A Docker container image is available for most releases. These are available from DockerHub as docker.io/library/haproxy (or simply haproxy when using docker(1) specifically).
The image runs as the non-root user haproxy, so it cannot bind to a privileged port. There are workarounds, but the upstream recommendation is to properly configure bridge networks and DNS and then to configure haproxy(1) to resolve names at runtime.
Configuration
BSD distributions will look for configuration files in /usr/local/etc.
Basic Configuration
A minimal configuration file looks like:
global user haproxy group haproxy daemon backend www_backend server www1 127.0.0.1:80 frontend http_in bind *:80 mode http default_backend www_backend
A backend instructs haproxy(1) where traffic for a particular service should be routed to. Multiple servers can be defined in a backend and an algorithm for load balancing (i.e. roundrobin, leastconn, etc.) can be specified.
A frontend instructs haproxy(1) to listen on an address and/or port. In the above, http_in binds to any name on port 80 and handles HTTP traffic. A frontend also provides logic for routing traffic to the correct backend.
Encryption
See here for details.
Authentication
See here for details.
Web Sockets
When a connection is upgraded to the websocket protocol, haproxy(1) implicitly drops to tunnel mode. Generally no other configuration is necessary.
If you are redirecting HTTP traffic to HTTPS in the frontend, you will need to make that redirect further conditional on not being a websocket connection.
acl is_websocket hdr(Upgrade) -i WebSocket http-request redirect scheme https if !{ ssl_fc } !is_websocket
Conditional Routing
HTTP headers can be inspected to logically route traffic using access control lists (ACLs). Consider the below frontend:
frontend http_frontend bind *:80 bind *:443 ssl crt /path/to/the/pem/certificate alpn h2, http1.1 mode http default_backend www_backend #route ACME challenge from Let's Encrypt to the certbot temporary server acl acme path_beg /.well-known/acme-challenge use_backend letsencrypt_backend if acme #route API requests acl api hdr(host) -i api.example.com #for webgit, split CGI requests from file requests acl webgit hdr(host) -i git.example.com acl cdn path_beg /js acl cdn path_beg /css use_backend www_backend if webgit ! cdn use_backend cdn_backend if cdn
This demonstrates:
- ACLs are defined and calculated based on HTTP headers, including host names and URI paths
- Re-using an ACL name is equivalent to a logical OR
- ACL names can be chained, acting as a logical AND
ACLs can be negated by prefixing with an exclamation mark (!)
Name Resolution
haproxy(1) can be configured to resolve server addresses at runtime.
First, configure a resolvers:
resolvers docker_dns nameserver docker1 127.0.0.11:53
Second, configure servers to defer name resolution and provide instructions on how to resolve names.
backend www_backend server www1 nginx:80 check resolvers docker_dns
Third, configure haproxy(1) to run regardless of name resolution errors. (The default behavior is to exit if any servers are unreachable.)
defaults default-server init-addr none
Stick Tables
See here for details.