diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5267060..452966c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,21 +2,21 @@ name: Release on: push: tags: - - 'v*.*.*' + - 'v*.*.*' jobs: goreleaser: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@master - - name: Setup Go - uses: actions/setup-go@v1 - with: - go-version: 1.15 - - name: GoReleaser - uses: goreleaser/goreleaser-action@v1 - with: - version: latest - args: release --rm-dist - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Checkout + uses: actions/checkout@master + - name: Setup Go + uses: actions/setup-go@v1 + with: + go-version: 1.21 + - name: GoReleaser + uses: goreleaser/goreleaser-action@v1 + with: + version: latest + args: release --rm-dist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index ead1e6c..8f7e7fd 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,11 @@ the official feature becomes available. - [Features](#features) - * [DNS Service Discovery](#dns-service-discovery) - * [Automatic Service Authentication](#automatic-service-authentication) + - [DNS Service Discovery](#dns-service-discovery) + - [Automatic Service Authentication](#automatic-service-authentication) + - [X-Serverless-Authorization](#x-serverless-authorization) - [Installation](#installation) +- [Usage](#usage) - [Quickstart](#quickstart) - [Architecture](#architecture) - [Troubleshooting](#troubleshooting) @@ -70,6 +72,10 @@ your apps to Cloud Run from other platforms that have name-based DNS resolution ![Cloud Run authentication before & after](assets/img/auth_code.png) +#### X-Serverless-Authorization + +If the `Authorization` header is already populated, this version of `runsd` instead puts the identity token into the `X-Serverless-Authorization` header. For more details, see Point 2 in the [documentation](https://cloud.google.com/run/docs/authenticating/service-to-service#acquire-token) for acquiring and configuring the ID token. + ## Installation > For my tracking purposes, please fill out the form at @@ -117,7 +123,7 @@ Note that: ## Quickstart You can deploy [this](./example) sample application to Cloud Run to try out -querying other **private** Cloud Run services **without tokens** and **without +querying other **private** Cloud Run services **without tokens** and **without full `.run.app` domains** by directly using curl. This sample app [has](./example/Dockerfile) `runsd` as its entrypoint and it @@ -174,7 +180,7 @@ resolved. Patch and more tests are always welcome. ## Limitations and Known Issues -1. All names like `http://NAME` will resolve to a Cloud Run URL even if they +1. All names like `http://NAME` will resolve to a Cloud Run URL even if they don't exist. Therefore, for example, if `http://hello` doesn't exist, it will will still be routed to a URL as if it existed, and it will get HTTP 404. 1. Similar to previous item `http://metadata` will be assumed as a Cloud Run @@ -187,6 +193,6 @@ resolved. Patch and more tests are always welcome. 1. WebSockets, gRPC (incl. streaming) and SSE works. Please file issues if it does not work. ------ +--- This is not an official Google project. diff --git a/go.mod b/go.mod index 61f76fc..5e0352a 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,16 @@ module cloud_run_proxy -go 1.13 +go 1.21 require ( github.com/google/go-cmp v0.4.0 github.com/miekg/dns v1.1.41 - golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e // indirect golang.org/x/net v0.0.0-20210504132125-bbd867fde50d - golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 // indirect - golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 // indirect k8s.io/klog/v2 v2.8.0 ) + +require ( + github.com/go-logr/logr v0.4.0 // indirect + golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 // indirect + golang.org/x/text v0.3.6 // indirect +) diff --git a/go.sum b/go.sum index b3da6f7..9b9b53f 100644 --- a/go.sum +++ b/go.sum @@ -1,50 +1,24 @@ -github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= -github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e h1:8foAy0aoO5GkqCvAEJ4VC4P3zksTg4X4aJCDpZzmgQI= -golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210504132125-bbd867fde50d h1:nTDGCTeAu2LhcsHTRzjyIUbZHCJ4QePArsm27Hka0UM= golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 h1:cdsMqa2nXzqlgs183pHxtvoVwU7CyzaCTAUOg94af4c= golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -k8s.io/klog/v2 v2.0.0-20200127113903-12be8a0d907a h1:Mcu95Qw9AYB0+JYxeTNY0aooqKFu8zdFVDr1Kigx5iI= -k8s.io/klog/v2 v2.0.0-20200127113903-12be8a0d907a/go.mod h1:q4PVo0BneA7GsUJvFqoEvOCVmYJP0c5Y4VxrAYpJrIk= k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts= k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= diff --git a/runsd/dns.go b/runsd/dns.go index 62870d6..25d0053 100644 --- a/runsd/dns.go +++ b/runsd/dns.go @@ -174,14 +174,12 @@ func nxdomain(w dns.ResponseWriter, msg *dns.Msg) { r.Authoritative = true r.Rcode = dns.RcodeNameError w.WriteMsg(r) - return } -// servfail an authoritative SERVFAIL (error) reply +// servfail an authoritative SERVFAIL (error) reply func servfail(w dns.ResponseWriter, msg *dns.Msg) { r := new(dns.Msg) r.SetReply(msg) r.Rcode = dns.RcodeServerFailure w.WriteMsg(r) - return } diff --git a/runsd/proxy.go b/runsd/proxy.go index 234b5bb..74ab102 100644 --- a/runsd/proxy.go +++ b/runsd/proxy.go @@ -18,7 +18,7 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net" "net/http" "net/http/httputil" @@ -44,8 +44,10 @@ func newReverseProxy(projectHash, currentRegion, internalDomain string) *reverse } } +type ctxKeyEarlyResponseType string + const ( - ctxKeyEarlyResponse = `early-response` + ctxKeyEarlyResponse ctxKeyEarlyResponseType = `early-response` ) func (rp *reverseProxy) newReverseProxyHandler(tr http.RoundTripper) http.Handler { @@ -70,7 +72,7 @@ func (rp *reverseProxy) newReverseProxyHandler(tr http.RoundTripper) http.Handle resp := &http.Response{ Request: req, StatusCode: http.StatusInternalServerError, - Body: ioutil.NopCloser(bytes.NewReader([]byte( + Body: io.NopCloser(bytes.NewReader([]byte( fmt.Sprintf("runsd doesn't know how to handle host=%q: %v", req.Host, err)))), } newReq := req.WithContext(context.WithValue(req.Context(), ctxKeyEarlyResponse, resp)) diff --git a/runsd/proxytransport.go b/runsd/proxytransport.go index 4c3fc1a..073eb03 100644 --- a/runsd/proxytransport.go +++ b/runsd/proxytransport.go @@ -16,7 +16,7 @@ package main import ( "fmt" - "io/ioutil" + "io" "net/http" "strings" "time" @@ -45,12 +45,15 @@ func (a authenticatingTransport) RoundTrip(req *http.Request) (*http.Response, e if err != nil { klog.V(1).Infof("WARN: failed to get ID token for host=%s: %v", req.Host, err) r := new(http.Response) - r.Body = ioutil.NopCloser(strings.NewReader(fmt.Sprintf("failed to fetch metadata token: %v", err))) + r.Body = io.NopCloser(strings.NewReader(fmt.Sprintf("failed to fetch metadata token: %v", err))) r.StatusCode = http.StatusInternalServerError return r, nil } if req.Header.Get("authorization") == "" { req.Header.Set("authorization", "Bearer "+idToken) + } else { + // If the request is already using the `Authorization` header, we should use the `X-Serverless-Authorization` header + req.Header.Set("x-serverless-authorization", "Bearer "+idToken) } ua := req.Header.Get("user-agent") req.Header.Set("user-agent", fmt.Sprintf("runsd version=%s", version))