Merge pull request #262401 from vifino/haproxy-quic

haproxy: 2.9.2 -> 2.9.3; Enable QUIC support

h7x4 aeee68c7 aec709aa

Changed files
+125 -46
nixos
tests
pkgs
tools
networking
haproxy
+90 -19
nixos/tests/haproxy.nix
···
-
import ./make-test-python.nix ({ pkgs, ...}: {
+
import ./make-test-python.nix ({ lib, pkgs, ...}: {
name = "haproxy";
nodes = {
-
machine = { ... }: {
-
services.haproxy = {
+
server = { ... }: {
+
services.haproxy = {
enable = true;
config = ''
+
global
+
limited-quic
+
defaults
+
mode http
timeout connect 10s
+
timeout client 10s
+
timeout server 10s
+
+
log /dev/log local0 debug err
+
option logasap
+
option httplog
+
option httpslog
backend http_server
-
mode http
-
server httpd [::1]:8000
+
server httpd [::1]:8000 alpn http/1.1
frontend http
-
bind *:80
-
mode http
+
bind :80
+
bind :443 ssl strict-sni crt /etc/ssl/fullchain.pem alpn h2,http/1.1
+
bind quic4@:443 ssl strict-sni crt /etc/ssl/fullchain.pem alpn h3 allow-0rtt
+
+
http-after-response add-header alt-svc 'h3=":443"; ma=60' if { ssl_fc }
+
http-request use-service prometheus-exporter if { path /metrics }
use_backend http_server
+
+
frontend http-cert-auth
+
bind :8443 ssl strict-sni crt /etc/ssl/fullchain.pem verify required ca-file /etc/ssl/cacert.crt
+
bind quic4@:8443 ssl strict-sni crt /etc/ssl/fullchain.pem verify required ca-file /etc/ssl/cacert.crt alpn h3
+
+
use_backend http_server
'';
};
services.httpd = {
···
}];
};
};
+
networking.firewall.allowedTCPPorts = [ 80 443 8443 ];
+
networking.firewall.allowedUDPPorts = [ 443 8443 ];
+
};
+
client = { ... }: {
+
environment.systemPackages = [ pkgs.curlHTTP3 ];
};
};
testScript = ''
+
# Helpers
+
def cmd(command):
+
print(f"+{command}")
+
r = os.system(command)
+
if r != 0:
+
raise Exception(f"Command {command} failed with exit code {r}")
+
+
def openssl(command):
+
cmd(f"${pkgs.openssl}/bin/openssl {command}")
+
+
# Generate CA.
+
openssl("req -new -newkey rsa:4096 -nodes -x509 -days 7 -subj '/C=ZZ/ST=Cloud/L=Unspecified/O=NixOS/OU=Tests/CN=CA Certificate' -keyout cacert.key -out cacert.crt")
+
+
# Generate and sign Server.
+
openssl("req -newkey rsa:4096 -nodes -subj '/CN=server/OU=Tests/O=NixOS' -keyout server.key -out server.csr")
+
openssl("x509 -req -in server.csr -out server.crt -CA cacert.crt -CAkey cacert.key -days 7")
+
cmd("cat server.crt server.key > fullchain.pem")
+
+
# Generate and sign Client.
+
openssl("req -newkey rsa:4096 -nodes -subj '/CN=client/OU=Tests/O=NixOS' -keyout client.key -out client.csr")
+
openssl("x509 -req -in client.csr -out client.crt -CA cacert.crt -CAkey cacert.key -days 7")
+
cmd("cat client.crt client.key > client.pem")
+
+
# Start the actual test.
start_all()
-
machine.wait_for_unit("multi-user.target")
-
machine.wait_for_unit("haproxy.service")
-
machine.wait_for_unit("httpd.service")
-
assert "We are all good!" in machine.succeed("curl -fk http://localhost:80/index.txt")
-
assert "haproxy_process_pool_allocated_bytes" in machine.succeed(
-
"curl -fk http://localhost:80/metrics"
-
)
+
server.copy_from_host("fullchain.pem", "/etc/ssl/fullchain.pem")
+
server.copy_from_host("cacert.crt", "/etc/ssl/cacert.crt")
+
server.succeed("chmod 0644 /etc/ssl/fullchain.pem /etc/ssl/cacert.crt")
+
+
client.copy_from_host("cacert.crt", "/etc/ssl/cacert.crt")
+
client.copy_from_host("client.pem", "/root/client.pem")
+
+
server.wait_for_unit("multi-user.target")
+
server.wait_for_unit("haproxy.service")
+
server.wait_for_unit("httpd.service")
+
+
assert "We are all good!" in client.succeed("curl -f http://server/index.txt")
+
assert "haproxy_process_pool_allocated_bytes" in client.succeed("curl -f http://server/metrics")
+
+
with subtest("https"):
+
assert "We are all good!" in client.succeed("curl -f --cacert /etc/ssl/cacert.crt https://server/index.txt")
+
+
with subtest("https-cert-auth"):
+
# Client must succeed in authenticating with the right certificate.
+
assert "We are all good!" in client.succeed("curl -f --cacert /etc/ssl/cacert.crt --cert-type pem --cert /root/client.pem https://server:8443/index.txt")
+
# Client must fail without certificate.
+
client.fail("curl --cacert /etc/ssl/cacert.crt https://server:8443/index.txt")
+
+
with subtest("h3"):
+
assert "We are all good!" in client.succeed("curl -f --http3-only --cacert /etc/ssl/cacert.crt https://server/index.txt")
+
+
with subtest("h3-cert-auth"):
+
# Client must succeed in authenticating with the right certificate.
+
assert "We are all good!" in client.succeed("curl -f --http3-only --cacert /etc/ssl/cacert.crt --cert-type pem --cert /root/client.pem https://server:8443/index.txt")
+
# Client must fail without certificate.
+
client.fail("curl -f --http3-only --cacert /etc/ssl/cacert.crt https://server:8443/index.txt")
with subtest("reload"):
-
machine.succeed("systemctl reload haproxy")
+
server.succeed("systemctl reload haproxy")
# wait some time to ensure the following request hits the reloaded haproxy
-
machine.sleep(5)
-
assert "We are all good!" in machine.succeed(
-
"curl -fk http://localhost:80/index.txt"
-
)
+
server.sleep(5)
+
assert "We are all good!" in client.succeed("curl -f http://server/index.txt")
'';
})
+35 -27
pkgs/tools/networking/haproxy/default.nix
···
{ useLua ? true
, usePcre ? true
-
# QUIC "is currently supported as an experimental feature" so shouldn't be enabled by default
-
, useQuicTls ? false
, withPrometheusExporter ? true
+
, sslLibrary ? "quictls"
, stdenv
, lib
, fetchurl
, nixosTests
, zlib
, libxcrypt
-
, openssl ? null
-
, quictls ? null
-
, lua5_3 ? null
-
, pcre ? null
-
, systemd ? null
+
, wolfssl
+
, libressl
+
, quictls
+
, openssl
+
, lua5_4
+
, pcre2
+
, systemd
}:
-
assert useLua -> lua5_3 != null;
-
assert usePcre -> pcre != null;
-
assert useQuicTls -> quictls != null;
-
assert !useQuicTls -> openssl != null;
-
-
let sslPkg = if useQuicTls then quictls else openssl;
+
assert lib.assertOneOf "sslLibrary" sslLibrary [ "quictls" "openssl" "libressl" "wolfssl" ];
+
let
+
sslPkgs = {
+
inherit quictls openssl libressl;
+
wolfssl = wolfssl.override {
+
variant = "haproxy";
+
extraConfigureFlags = [ "--enable-quic" ];
+
};
+
};
+
sslPkg = sslPkgs.${sslLibrary};
in stdenv.mkDerivation (finalAttrs: {
pname = "haproxy";
-
version = "2.9.2";
+
version = "2.9.3";
src = fetchurl {
url = "https://www.haproxy.org/download/${lib.versions.majorMinor finalAttrs.version}/src/haproxy-${finalAttrs.version}.tar.gz";
-
hash = "sha256-hRrugw7CjBeRJGqf1EePZD0RWlY92Qf2YSzDgalSqzw=";
+
hash = "sha256-7VF8ZavYaUVBH2vLGMfsZXpwaTHLeB6igwY7oKdYWMA=";
};
buildInputs = [ sslPkg zlib libxcrypt ]
-
++ lib.optional useLua lua5_3
-
++ lib.optional usePcre pcre
+
++ lib.optional useLua lua5_4
+
++ lib.optional usePcre pcre2
++ lib.optional stdenv.isLinux systemd;
# TODO: make it work on bsd as well
···
];
buildFlags = [
+
"USE_ZLIB=yes"
"USE_OPENSSL=yes"
-
"SSL_LIB=${sslPkg}/lib"
-
"SSL_INC=${sslPkg}/include"
-
"USE_ZLIB=yes"
-
] ++ lib.optionals useQuicTls [
-
"USE_QUIC=1"
+
"SSL_INC=${lib.getDev sslPkg}/include"
+
"SSL_LIB=${lib.getDev sslPkg}/lib"
+
"USE_QUIC=yes"
+
] ++ lib.optionals (sslLibrary == "openssl") [
+
"USE_QUIC_OPENSSL_COMPAT=yes"
+
] ++ lib.optionals (sslLibrary == "wolfssl") [
+
"USE_OPENSSL_WOLFSSL=yes"
] ++ lib.optionals usePcre [
-
"USE_PCRE=yes"
-
"USE_PCRE_JIT=yes"
+
"USE_PCRE2=yes"
+
"USE_PCRE2_JIT=yes"
] ++ lib.optionals useLua [
"USE_LUA=yes"
"LUA_LIB_NAME=lua"
-
"LUA_LIB=${lua5_3}/lib"
-
"LUA_INC=${lua5_3}/include"
+
"LUA_LIB=${lua5_4}/lib"
+
"LUA_INC=${lua5_4}/include"
] ++ lib.optionals stdenv.isLinux [
"USE_SYSTEMD=yes"
"USE_GETADDRINFO=1"
···
tens of thousands of connections is clearly realistic with todays
hardware.
'';
-
maintainers = with lib.maintainers; [ ];
+
maintainers = with lib.maintainers; [ vifino ];
platforms = with lib.platforms; linux ++ darwin;
mainProgram = "haproxy";
};