From b5de49917c5f632e49c838222e0a12ceb79c609d Mon Sep 17 00:00:00 2001 From: Roberto Santalla Date: Mon, 21 Dec 2020 11:55:16 +0100 Subject: [PATCH] cmd/relaypoolsrv: Allow validation of relay join requests by certificate (fixes #7196) (#7217) --- cmd/strelaypoolsrv/main.go | 21 ++++++++++++++++++++- cmd/strelaysrv/main.go | 2 +- cmd/strelaysrv/pool.go | 22 ++++++++++++++++++++-- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/cmd/strelaypoolsrv/main.go b/cmd/strelaypoolsrv/main.go index 17b668e68..1e9b57b06 100644 --- a/cmd/strelaypoolsrv/main.go +++ b/cmd/strelaypoolsrv/main.go @@ -6,9 +6,11 @@ import ( "compress/gzip" "context" "crypto/tls" + "crypto/x509" "encoding/json" "flag" "fmt" + "github.com/syncthing/syncthing/lib/protocol" "io" "io/ioutil" "log" @@ -208,6 +210,7 @@ func main() { tlsCfg := &tls.Config{ Certificates: []tls.Certificate{cert}, MinVersion: tls.VersionTLS10, // No SSLv3 + ClientAuth: tls.RequestClientCert, CipherSuites: []uint16{ // No RC4 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, @@ -338,6 +341,12 @@ func handleGetRequest(rw http.ResponseWriter, r *http.Request) { } func handlePostRequest(w http.ResponseWriter, r *http.Request) { + var relayCert *x509.Certificate + if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 { + relayCert = r.TLS.PeerCertificates[0] + log.Printf("Got TLS cert from relay server") + } + var newRelay relay err := json.NewDecoder(r.Body).Decode(&newRelay) r.Body.Close() @@ -359,6 +368,16 @@ func handlePostRequest(w http.ResponseWriter, r *http.Request) { return } + if relayCert != nil { + advertisedId := uri.Query().Get("id") + idFromCert := protocol.NewDeviceID(relayCert.Raw).String() + if advertisedId != idFromCert { + log.Println("Warning: Relay server requested to join with an ID different from the join request, rejecting") + http.Error(w, "mismatched advertised id and join request cert", http.StatusBadRequest) + return + } + } + host, port, err := net.SplitHostPort(uri.Host) if err != nil { if debug { @@ -379,7 +398,7 @@ func handlePostRequest(w http.ResponseWriter, r *http.Request) { if ip == nil || ip.IsUnspecified() { uri.Host = net.JoinHostPort(rhost, port) newRelay.URL = uri.String() - } else if host != rhost { + } else if host != rhost && relayCert == nil { if debug { log.Println("IP address advertised does not match client IP address", r.RemoteAddr, uri) } diff --git a/cmd/strelaysrv/main.go b/cmd/strelaysrv/main.go index 1c0f86d42..b568d0817 100644 --- a/cmd/strelaysrv/main.go +++ b/cmd/strelaysrv/main.go @@ -247,7 +247,7 @@ func main() { for _, pool := range pools { pool = strings.TrimSpace(pool) if len(pool) > 0 { - go poolHandler(pool, uri, mapping) + go poolHandler(pool, uri, mapping, cert) } } diff --git a/cmd/strelaysrv/pool.go b/cmd/strelaysrv/pool.go index fd8ee83a5..9ddde16c5 100644 --- a/cmd/strelaysrv/pool.go +++ b/cmd/strelaysrv/pool.go @@ -4,6 +4,7 @@ package main import ( "bytes" + "crypto/tls" "encoding/json" "io/ioutil" "log" @@ -16,7 +17,7 @@ const ( httpStatusEnhanceYourCalm = 429 ) -func poolHandler(pool string, uri *url.URL, mapping mapping) { +func poolHandler(pool string, uri *url.URL, mapping mapping, ownCert tls.Certificate) { if debug { log.Println("Joining", pool) } @@ -31,7 +32,24 @@ func poolHandler(pool string, uri *url.URL, mapping mapping) { uriCopy.String(), }) - resp, err := httpClient.Post(pool, "application/json", &b) + poolUrl, err := url.Parse(pool) + if err != nil { + log.Printf("Could not parse pool url '%s': %v", pool, err) + } + + client := http.DefaultClient + if poolUrl.Scheme == "https" { + // Sent our certificate in join request + client = &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + Certificates: []tls.Certificate{ownCert}, + }, + }, + } + } + + resp, err := client.Post(pool, "application/json", &b) if err != nil { log.Printf("Error joining pool %v: HTTP request: %v", pool, err) time.Sleep(time.Minute)