{ config, lib, pkgs, ... }: with lib; let cfg = config.my.services.proxy; canonicalize = builtins.replaceStrings ["*" "." ":" "[" "]"] ["all" "_" "_" "" ""]; in { options.my.services.proxy = { enable = mkOption { default = false; description = "whether to enable proxy"; type = types.bool; }; proxyHosts = mkOption { type = types.listOf (types.submodule ({ options = { hostNames = mkOption { type = types.listOf types.str; default = [ ]; description = '' Proxy these hostNames. ''; }; proxyTo = mkOption { type = types.submodule ({ options = { host = mkOption { type = types.nullOr types.string; default = null; description = '' Host to forward traffic to. Any hostname may only be used once ''; }; httpPort = mkOption { type = types.int; default = 80; description = '' Port to forward http to. ''; }; httpsPort = mkOption { type = types.int; default = 443; description = '' Port to forward http to. ''; }; }; }); description = '' { host = /* ip or fqdn */; httpPort = 80; httpsPort = 443; } to proxy to ''; default = { }; }; matchArg = mkOption { type = types.str; default = ""; description = "Optional argument to HAProxy `req.ssl_sni -i`"; }; }; })); default = [ ]; example = [{ hostNames = [ "test.hq.c3d2.de" "test.c3d2.de" ]; proxyTo = { host = "172.22.99.99"; httpPort = 80; httpsPort = 443; }; }]; }; }; config = mkIf cfg.enable { services.haproxy = { enable = true; config = '' defaults timeout client 30000 timeout connect 5000 timeout check 5000 timeout server 30000 frontend http-in bind :::80 v4v6 option http-keep-alive default_backend proxy-backend-http backend proxy-backend-http mode http option http-server-close option forwardfor http-request set-header X-Forwarded-Proto http http-request set-header X-Forwarded-Port 80 ${ concatMapStrings ({ proxyTo, hostNames, matchArg }: optionalString (hostNames != [ ] && proxyTo.host != null) ( concatMapStrings (hostname: '' use-server ${canonicalize hostname}-http if { req.hdr(host) -i ${matchArg} ${hostname} } server ${canonicalize hostname}-http ${proxyTo.host}:${ toString proxyTo.httpPort } weight 1 '') hostNames ) ) cfg.proxyHosts } frontend https-in bind :::443 v4v6 tcp-request inspect-delay 5s tcp-request content accept if { req.ssl_hello_type 1 } ${concatMapStrings ({ proxyTo, hostNames, matchArg }: concatMapStrings (hostname: '' use_backend ${canonicalize proxyTo.host}-https if { req.ssl_sni -i ${matchArg} ${hostname} } '') hostNames ) cfg.proxyHosts} ${concatMapStrings ({ proxyTo, hostNames, matchArg }: '' backend ${canonicalize proxyTo.host}-https server ${canonicalize proxyTo.host}-https ${proxyTo.host}:${ toString proxyTo.httpsPort } weight 1 '') cfg.proxyHosts} ''; }; }; }