From 75ea1b60d5646ca81d03731c5066492f5ac12014 Mon Sep 17 00:00:00 2001 From: Jamie McClelland Date: Thu, 25 Sep 2025 08:08:16 +0000 Subject: [PATCH] enable auto setting of SNI based on host header (#1129) With this change, setting targetSNI to 'auto' causes anubis to use the request host name as the SNI name, allowing multiple sites to use the same anubis instance and same backend, while still securely connecting to the backend via https. See https://github.com/TecharoHQ/anubis/issues/424 --- cmd/anubis/main.go | 25 +++++++++++++++---------- docs/docs/CHANGELOG.md | 1 + docs/docs/admin/installation.mdx | 2 +- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/cmd/anubis/main.go b/cmd/anubis/main.go index c1efe24..5ccdcc0 100644 --- a/cmd/anubis/main.go +++ b/cmd/anubis/main.go @@ -68,7 +68,7 @@ var ( slogLevel = flag.String("slog-level", "INFO", "logging level (see https://pkg.go.dev/log/slog#hdr-Levels)") stripBasePrefix = flag.Bool("strip-base-prefix", false, "if true, strips the base prefix from requests forwarded to the target server") target = flag.String("target", "http://localhost:3923", "target to reverse proxy to, set to an empty string to disable proxying when only using auth request") - targetSNI = flag.String("target-sni", "", "if set, the value of the TLS handshake hostname when forwarding requests to the target") + targetSNI = flag.String("target-sni", "", "if set, TLS handshake hostname when forwarding requests to the target, if set to auto, use Host header") targetHost = flag.String("target-host", "", "if set, the value of the Host header when forwarding requests to the target") targetInsecureSkipVerify = flag.Bool("target-insecure-skip-verify", false, "if true, skips TLS validation for the backend") targetDisableKeepAlive = flag.Bool("target-disable-keepalive", false, "if true, disables HTTP keep-alive for the backend") @@ -236,23 +236,28 @@ func makeReverseProxy(target string, targetSNI string, targetHost string, insecu if insecureSkipVerify || targetSNI != "" { transport.TLSClientConfig = &tls.Config{} - if insecureSkipVerify { - slog.Warn("TARGET_INSECURE_SKIP_VERIFY is set to true, TLS certificate validation will not be performed", "target", target) - transport.TLSClientConfig.InsecureSkipVerify = true - } - if targetSNI != "" { - transport.TLSClientConfig.ServerName = targetSNI - } + } + if insecureSkipVerify { + slog.Warn("TARGET_INSECURE_SKIP_VERIFY is set to true, TLS certificate validation will not be performed", "target", target) + transport.TLSClientConfig.InsecureSkipVerify = true + } + if targetSNI != "" && targetSNI != "auto" { + transport.TLSClientConfig.ServerName = targetSNI } rp := httputil.NewSingleHostReverseProxy(targetUri) rp.Transport = transport - if targetHost != "" { + if targetHost != "" || targetSNI == "auto" { originalDirector := rp.Director rp.Director = func(req *http.Request) { originalDirector(req) - req.Host = targetHost + if targetHost != "" { + req.Host = targetHost + } + if targetSNI == "auto" { + transport.TLSClientConfig.ServerName = req.Host + } } } diff --git a/docs/docs/CHANGELOG.md b/docs/docs/CHANGELOG.md index 1ffbb81..2b2fb21 100644 --- a/docs/docs/CHANGELOG.md +++ b/docs/docs/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixes concurrency problems with very old browsers ([#1082](https://github.com/TecharoHQ/anubis/issues/1082)). - Randomly use the Refresh header instead of the meta refresh tag in the metarefresh challenge. - Update OpenRC service to truncate the runtime directory before starting Anubis. +- Add option to set `targetSNI` to special keyword 'auto' to indicate that it should be automatically set to the request Host name ([424](https://github.com/TecharoHQ/anubis/issues/424)). ### Bug Fixes diff --git a/docs/docs/admin/installation.mdx b/docs/docs/admin/installation.mdx index 96f305c..0edd65d 100644 --- a/docs/docs/admin/installation.mdx +++ b/docs/docs/admin/installation.mdx @@ -123,7 +123,7 @@ If you don't know or understand what these settings mean, ignore them. These are | `TARGET_DISABLE_KEEPALIVE` | `false` | If `true`, disables HTTP keep-alive for connections to the target backend. Useful for backends that don't handle keep-alive properly. | | `TARGET_HOST` | unset | If set, overrides the Host header in requests forwarded to `TARGET`. | | `TARGET_INSECURE_SKIP_VERIFY` | `false` | If `true`, skip TLS certificate validation for targets that listen over `https`. If your backend does not listen over `https`, ignore this setting. | -| `TARGET_SNI` | unset | If set, overrides the TLS handshake hostname in requests forwarded to `TARGET`. | +| `TARGET_SNI` | unset | If set, TLS handshake hostname when forwarding requests to the `TARGET`. If set to auto, use Host header. |