From 73c371f78403b9e11259d7241caba2ca4654911b Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 17 Sep 2025 12:49:00 -0700 Subject: [PATCH] cmd/derper: permit port 80 in ACE targets Updates tailscale/corp#32168 Updates tailscale/corp#32226 Change-Id: Iddc017b060c76e6eab8f6d0c989a775bcaae3518 Signed-off-by: Brad Fitzpatrick --- cmd/derper/ace.go | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/cmd/derper/ace.go b/cmd/derper/ace.go index 301b029cc..a11539a6e 100644 --- a/cmd/derper/ace.go +++ b/cmd/derper/ace.go @@ -35,8 +35,35 @@ func serveConnect(s *derp.Server, w http.ResponseWriter, r *http.Request) { if err != nil { return err } - if port != "443" { - return fmt.Errorf("only port 443 is allowed") + if port != "443" && port != "80" { + // There are only two types of CONNECT requests the client makes + // via ACE: requests for /key (port 443) and requests to upgrade + // to the bidirectional ts2021 Noise protocol. + // + // The ts2021 layer can bootstrap over port 80 (http) or port + // 443 (https). + // + // Without ACE, we prefer port 80 to avoid unnecessary double + // encryption. But enough places require TLS+port 443 that we do + // support that double encryption path as a fallback. + // + // But ACE adds its own TLS layer (ACE is always CONNECT over + // https). If we don't permit port 80 here as a target, we'd + // have three layers of encryption (TLS + TLS + Noise) which is + // even more silly than two. + // + // So we permit port 80 such that we can only have two layers of + // encryption, varying by the request type: + // + // 1. TLS from client to ACE proxy (CONNECT) + // 2a. TLS from ACE proxy to https://controlplane.tailscale.com/key (port 443) + // 2b. ts2021 Noise from ACE proxy to http://controlplane.tailscale.com/ts2021 (port 80) + // + // But nothing's stopping the client from doing its ts2021 + // upgrade over https anyway and having three layers of + // encryption. But we can at least permit the client to do a + // "CONNECT controlplane.tailscale.com:80 HTTP/1.1" if it wants. + return fmt.Errorf("only ports 443 and 80 are allowed") } // TODO(bradfitz): make policy configurable from flags and/or come // from local tailscaled nodeAttrs