Skip to content

Commit

Permalink
add mtlsTokenSource
Browse files Browse the repository at this point in the history
Signed-off-by: sal rashid <[email protected]>
  • Loading branch information
salrashid123 committed Aug 15, 2024
1 parent 9f404ca commit 0c2e913
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 70 deletions.
10 changes: 7 additions & 3 deletions examples/goapp_mtls/go.mod
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
module main

go 1.19
go 1.22.4

require (
cloud.google.com/go/storage v1.37.0
golang.org/x/oauth2 v0.22.0
golang.org/x/oauth2 v0.22.0 // indirect
google.golang.org/api v0.157.0
mtlstokensource v0.0.0
)

require cloud.google.com/go/compute/metadata v0.5.0

require (
cloud.google.com/go v0.112.0 // indirect
cloud.google.com/go/compute/metadata v0.5.0 // indirect
cloud.google.com/go/iam v1.1.5 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
Expand Down Expand Up @@ -39,3 +41,5 @@ require (
google.golang.org/grpc v1.61.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
)

replace mtlstokensource => ./mtlstokensource
7 changes: 7 additions & 0 deletions examples/goapp_mtls/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY=
github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand All @@ -20,6 +21,7 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
Expand Down Expand Up @@ -52,7 +54,9 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
Expand All @@ -72,6 +76,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs=
Expand All @@ -83,6 +88,7 @@ go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZV
go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg=
go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY=
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0=
go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand Down Expand Up @@ -128,6 +134,7 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
google.golang.org/api v0.157.0 h1:ORAeqmbrrozeyw5NjnMxh7peHO0UzV4wWYSwZeCUb20=
google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
Expand Down
87 changes: 20 additions & 67 deletions examples/goapp_mtls/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"os"
"time"

//"cloud.google.com/go/compute/metadata"
"mtlstokensource"

"cloud.google.com/go/compute/metadata"
"cloud.google.com/go/storage"
"golang.org/x/oauth2"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
)
Expand All @@ -31,39 +31,6 @@ export GCE_METADATA_HOST=localhost:8080
go run main.go
*/

type gceMetadataTransport struct {
rtp http.RoundTripper
tlsConfig *tls.Config
}

func GCEMetadataTLSTransport(tlsconfig *tls.Config) *gceMetadataTransport {
tr := &gceMetadataTransport{
tlsConfig: tlsconfig,
}
myDialer := &net.Dialer{
Timeout: 500 * time.Millisecond,
}
dc := func(ctx context.Context, network, address string) (net.Conn, error) {
overrideAddress := os.Getenv("GCE_METADATA_HOST")
if overrideAddress == "" {
overrideAddress = "metadata.google.internal:443"
}
return myDialer.DialContext(ctx, network, overrideAddress)
}
tr.rtp = &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: dc,
TLSHandshakeTimeout: 400 * time.Millisecond,
TLSClientConfig: tr.tlsConfig,
}
return tr
}

func (tr *gceMetadataTransport) RoundTrip(r *http.Request) (*http.Response, error) {
r.Header.Add("Metadata-Flavor", "Google")
return tr.rtp.RoundTrip(r)
}

const (
bucketName = "core-eso-bucket"
)
Expand Down Expand Up @@ -92,9 +59,10 @@ func main() {
}

client := &http.Client{
Transport: GCEMetadataTLSTransport(tlsconfig),
Transport: mtlstokensource.GCEMetadataTLSTransport(tlsconfig),
}

// get arbitrary metadata using custom http client
projectIDResp, err := client.Get("https://metadata.google.internal/computeMetadata/v1/project/project-id")
if err != nil {
fmt.Printf("error reading projectIDResp %v", err)
Expand All @@ -109,37 +77,30 @@ func main() {
defer projectIDResp.Body.Close()
fmt.Printf("ProjectID: %s\n", string(projectIDBytes))

accessTokenResp, err := client.Get("https://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token")
/// get arbitrary data from metadata server using metadata library
c := metadata.NewClient(client)
instanceID, err := c.InstanceIDWithContext(ctx)
if err != nil {
fmt.Printf("error reading accessTokenResp %v", err)
os.Exit(1)
panic(err)
}
fmt.Printf("InstanceID %s\n", instanceID)

accessTokenBytes, err := io.ReadAll(accessTokenResp.Body)
mts, err := mtlstokensource.MtlsTokenSource(&mtlstokensource.MtlsTokenConfig{
RootCA: *caCertPool,
TLSCertificate: certs,
})
if err != nil {
fmt.Printf("error reading accessTokenBytes %v", err)
fmt.Printf("error reading tokensource %v", err)
os.Exit(1)
}
defer accessTokenResp.Body.Close()

tok := &oauth2.Token{}
err = json.Unmarshal(accessTokenBytes, tok)
tok, err := mts.Token()
if err != nil {
fmt.Println("Error in JSON oauth2Token", err)

fmt.Printf("error reading token %v", err)
os.Exit(1)
}
fmt.Printf("AccessToken: %s\n", tok.AccessToken)

fmt.Printf("AccessToken: %s\n", string(accessTokenBytes))

// note, i'm using a staticTokeSource here.
// a really easy TODO is to write custom token source which'll allow automatic acquisitions refresh of these tokens
// eg https://github.com/salrashid123/gcp_process_credentials_go/blob/main/external.go#L36
// for example, create a
// sts, err := ComputeTokenSourceMTLS(&ComputeTokenSourceMTLSConfig{ CACert: *ca, Cert: *pub, Key: *key })
//
sts := oauth2.StaticTokenSource(tok)

storageClient, err := storage.NewClient(ctx, option.WithTokenSource(sts))
storageClient, err := storage.NewClient(ctx, option.WithTokenSource(mts))
if err != nil {
panic(err)
}
Expand All @@ -160,12 +121,4 @@ func main() {
fmt.Printf("Bucket: %v\n", oattrs.Name)
}

// c := metadata.NewClient(client)
// // get arbitrary metadata values directly; this won't work because the metadata package does not support client certs
// instanceID, err := c.InstanceIDWithContext(ctx)
// if err != nil {
// panic(err)
// }
// fmt.Printf("InstanceID %s\n", instanceID)

}
5 changes: 5 additions & 0 deletions examples/goapp_mtls/mtlstokensource/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module mtlstokensource

go 1.22.4

require golang.org/x/oauth2 v0.22.0
4 changes: 4 additions & 0 deletions examples/goapp_mtls/mtlstokensource/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
115 changes: 115 additions & 0 deletions examples/goapp_mtls/mtlstokensource/token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package mtlstokensource

import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"io"
"net"
"net/http"
"os"
"sync"
"time"

"golang.org/x/oauth2"
)

type MtlsTokenConfig struct {
RootCA x509.CertPool
TLSCertificate tls.Certificate
}

type gceMetadataTransport struct {
rtp http.RoundTripper
tlsConfig *tls.Config
}

func GCEMetadataTLSTransport(tlsconfig *tls.Config) *gceMetadataTransport {
tr := &gceMetadataTransport{
tlsConfig: tlsconfig,
}

myDialer := &net.Dialer{
Timeout: 500 * time.Millisecond,
}
dc := func(ctx context.Context, network, address string) (net.Conn, error) {
overrideAddress := os.Getenv("GCE_METADATA_HOST")
if overrideAddress == "" {
overrideAddress = "metadata.google.internal:443"
}
return myDialer.DialContext(ctx, network, overrideAddress)
}

tr.tlsConfig.ServerName = "metadata.google.internal"
tr.rtp = &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: dc,
TLSHandshakeTimeout: 400 * time.Millisecond,
TLSClientConfig: tr.tlsConfig,
}
return tr
}

func (tr *gceMetadataTransport) RoundTrip(r *http.Request) (*http.Response, error) {
r.URL.Scheme = "https"
r.Header.Add("Metadata-Flavor", "Google")
return tr.rtp.RoundTrip(r)
}

const ()

func MtlsTokenSource(tokenConfig *MtlsTokenConfig) (oauth2.TokenSource, error) {

tlsConfig := &tls.Config{
RootCAs: &tokenConfig.RootCA,
Certificates: []tls.Certificate{tokenConfig.TLSCertificate},
}

return &mtlsTokenSource{
refreshMutex: &sync.Mutex{},
mtlsToken: oauth2.Token{},
tlsConfig: tlsConfig,
}, nil
}

type mtlsTokenSource struct {
refreshMutex *sync.Mutex
mtlsToken oauth2.Token
tlsConfig *tls.Config
}

func (ts *mtlsTokenSource) Token() (*oauth2.Token, error) {

ts.refreshMutex.Lock()
defer ts.refreshMutex.Unlock()

if ts.mtlsToken.Valid() {
return &ts.mtlsToken, nil
}

client := &http.Client{
Transport: GCEMetadataTLSTransport(ts.tlsConfig),
Timeout: time.Duration(100) * time.Millisecond,
}

accessTokenResp, err := client.Get("https://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token")
if err != nil {
return nil, err
}

accessTokenBytes, err := io.ReadAll(accessTokenResp.Body)
if err != nil {
return nil, err
}
defer accessTokenResp.Body.Close()

tok := &oauth2.Token{}
err = json.Unmarshal(accessTokenBytes, tok)
if err != nil {
return nil, err
}

return tok, nil

}

0 comments on commit 0c2e913

Please sign in to comment.