diff --git a/get.go b/get.go index 42c28ca..2c31549 100644 --- a/get.go +++ b/get.go @@ -8,14 +8,20 @@ import ( "io" "io/ioutil" "net/http" + "net/url" + "strconv" "strings" "time" "github.com/google/go-attestation/attest" "github.com/gorilla/websocket" + "github.com/hashicorp/mdns" "github.com/pkg/errors" ) +const MDNSTimeout = 15 * time.Second +const MDNSServiceName = "_kcrypt._tcp" + // GetAuthToken generates an authentication token from the host TPM. // It will return the token as a string and the generated AK that should // be saved by the caller for later Authentication. @@ -104,6 +110,14 @@ func writeRead(conn *websocket.Conn, input []byte) ([]byte, error) { // Get retrieves a message from a remote ws server after // a successfully process of the TPM challenge func Get(url string, opts ...Option) ([]byte, error) { + fmt.Printf("url = %+v\n", url) + // TODO: + // - Parse the URL and find the domain + // - if the domain is `.local` then try to find the IP address that matches + // using mdns (check that this is a valid assumption according to standards) + // - If an mdns response is returned, construct a request that targets the + // IP address we received and has the appropriate "Host" header set to the + // original domain (is this how websockets also work?) conn, err := Connection(url, opts...) if err != nil { return nil, err @@ -121,6 +135,7 @@ func Get(url string, opts ...Option) ([]byte, error) { // Connection returns a connection to the endpoint which suathenticated already. // The server side needs to call AuthRequest on the http request in order to authenticate and refuse connections func Connection(url string, opts ...Option) (*websocket.Conn, error) { + fmt.Println("Creating a connection") c := newConfig() c.apply(opts...) @@ -129,6 +144,12 @@ func Connection(url string, opts ...Option) (*websocket.Conn, error) { header = http.Header{} } + var err error + url, err = checkMDNSDomain(url, &header) + if err != nil { + return nil, err + } + dialer := websocket.DefaultDialer if len(c.cacerts) > 0 { pool := x509.NewCertPool() @@ -237,3 +258,66 @@ func getChallengeResponse(c *config, ec *attest.EncryptedCredential, aikBytes [] Secret: secret, }, nil } + +// TODO: +// - Check if domain ends in ".local" +// - If yes, try mdns +// - If no reply, leave everything as is (let normal DNS take over) +// - If we get a reply, add the original domain in the "Host" header and return +// the IP address as the new url. +func checkMDNSDomain(originalURL string, headers *http.Header) (string, error) { + fmt.Println("checking mdns domain") + parsedURL, err := url.Parse(originalURL) + if err != nil { + return originalURL, fmt.Errorf("parsing the mdns url: %w", err) + } + + host := parsedURL.Host + if !strings.HasSuffix(host, ".local") { + return originalURL, nil + } + + mdnsIP, mdnsPort := discoverMDNS(host) + if mdnsIP == "" { // no reply + return originalURL, nil + } + + headers.Add("Host", parsedURL.Host) + newURL := strings.ReplaceAll(originalURL, host, mdnsIP) + // Remove any port in the original url + if port := parsedURL.Port(); port != "" { + newURL = strings.ReplaceAll(newURL, port, "") + } + + // Add any possible port from the mdns response + if mdnsPort != "" { + newURL = strings.ReplaceAll(newURL, mdnsIP, fmt.Sprintf("%s:%s", mdnsIP, mdnsPort)) + } + + return newURL, nil +} + +func discoverMDNS(host string) (string, string) { + // Make a channel for results and start listening + entriesCh := make(chan *mdns.ServiceEntry, 4) + defer close(entriesCh) + + // Start the lookup. + // The channel is buffered so it doesn't block. + // The Lookup here has its own timeout until it receives a response. + // We use a select with a timeout to read because we don't know if we didn't + // get the response yet or if there will be no response at all. + mdns.Lookup(MDNSServiceName, entriesCh) + + select { + case entry := <-entriesCh: + // TODO: For now we don't care what the actual "host" is set to. Any response + // will do. Maybe in the future we can verify with that entry.Host matches host, + // or something like that but it's not a security measure. Anyone could bring up + // a server that advertises to be "_kcrypt._tcp" type of service as long as they + // can connect to the same network. + return entry.AddrV4.String(), strconv.Itoa(entry.Port) // TODO: v6? + case <-time.After(MDNSTimeout): + return "", "" + } +} diff --git a/go.mod b/go.mod index 11be81b..e939ecb 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/google/go-tpm v0.3.3 github.com/google/go-tpm-tools v0.3.7 github.com/gorilla/websocket v1.5.0 + github.com/hashicorp/mdns v1.0.5 github.com/onsi/ginkgo/v2 v2.1.3 github.com/onsi/gomega v1.17.0 github.com/pkg/errors v0.9.1 @@ -16,6 +17,7 @@ require ( require ( github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad // indirect + github.com/miekg/dns v1.1.41 // indirect golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect diff --git a/go.sum b/go.sum index d5de815..f7ab0c4 100644 --- a/go.sum +++ b/go.sum @@ -231,6 +231,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.5 h1:1M5hW1cunYeoXOqHwEb/GBDDHAFo0Yqb/uz/beC6LbE= +github.com/hashicorp/mdns v1.0.5/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -279,6 +281,8 @@ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -501,6 +505,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -519,6 +524,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -564,7 +571,9 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316092937-0b90fd5c4c48/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=