Compare commits

...

1 Commits

Author SHA1 Message Date
Brad Fitzpatrick
3928ea206e control/controlhttp: send expected control public key in upgrade request
So we can do key rotation later and have small windows of overlapping
valid server keys.

Updates #3488

Change-Id: Ib5c7f2006a797a069e3f55d37f5d41f533e82f71
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-08 15:00:47 -08:00
2 changed files with 17 additions and 0 deletions

View File

@@ -50,6 +50,10 @@ const (
// payload, to save an RTT.
handshakeHeaderName = "X-Tailscale-Handshake"
// serverPubHeaderName is the HTTP request header that
// says the expected public key of the control plane.
serverPubHeaderName = "X-Tailscale-Control-Public"
// serverUpgradePath is where the server-side HTTP handler to
// to do the protocol switch is located.
serverUpgradePath = "/ts2021"
@@ -194,6 +198,7 @@ func (a *dialParams) tryURL(u *url.URL, init []byte) (net.Conn, error) {
"Upgrade": []string{upgradeHeaderValue},
"Connection": []string{"upgrade"},
handshakeHeaderName: []string{base64.StdEncoding.EncodeToString(init)},
serverPubHeaderName: []string{a.controlKey.String()},
},
}
req = req.WithContext(ctx)

View File

@@ -44,6 +44,18 @@ func AcceptHTTP(ctx context.Context, w http.ResponseWriter, r *http.Request, pri
return nil, fmt.Errorf("decoding base64 handshake header: %v", err)
}
if wantPub := r.Header.Get(serverPubHeaderName); wantPub != "" {
// If the client declared the public key they expect to speak to,
// check it.
// TODO: replace the 'private' parameter with a func/interface
// that looks up the private key as a function of the public key
// to see if we have a currently in-rotation key that's valid.
if private.Public().String() != wantPub {
http.Error(w, "requested server key unavailable", http.StatusServiceUnavailable)
return nil, errors.New("client requested unavailble server key")
}
}
hijacker, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "make request over HTTP/1", http.StatusBadRequest)