From ae22aa4dc55d2125939e5cc280505047c7fe5304 Mon Sep 17 00:00:00 2001 From: sal rashid Date: Thu, 5 Sep 2024 09:22:58 -0400 Subject: [PATCH] migrate to using jwtaccesstoken with scope only Signed-off-by: sal rashid --- README.md | 9 ++- example/tpm/no_policy/main.go | 13 ++-- example/tpm/password_policy/main.go | 5 +- example/tpm/pcr_policy/main.go | 5 +- tpm/tpm.go | 108 +++++----------------------- 5 files changed, 29 insertions(+), 111 deletions(-) diff --git a/README.md b/README.md index b88b1d524..663bda23a 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,9 @@ Implementations of various [TokenSource](https://godoc.org/golang.org/x/oauth2#T for a simple end-to-end, see [Trusted Platform Module (TPM) based GCP Service Account Key](https://gist.github.com/salrashid123/865ea715881cb7c020da987b08c3881a) -There are two types of tokens this TokenSource fulfills: +The types of tokens this TokenSource fulfills: -- `JWTAccessToken` -- `Oauth2 access_tokens`. +- [Self-signed JWT with Scopes](https://google.aip.dev/auth/4111) ### Usage @@ -210,7 +209,8 @@ note, there are also several ways to securely transfer public/private keys betwe The TPM based `TokenSource` can now be used to access a GCP resource using either a plain HTTPClient or _native_ GCP library (`google-cloud-pubsub`)!! ```bash - go run main.go --projectId=core-eso \ +cd example/tpm/ + go run no_policy/main.go --projectId=core-eso \ --persistentHandle=0x81010002 \ --serviceAccountEmail="tpm-sa@core-eso.iam.gserviceaccount.com" \ --bucketName=core-eso-bucket --keyId=71b831d149e4667809644840cda2e7e0080035d5 @@ -237,7 +237,6 @@ eg Name: pub.Name, }, Email: *serviceAccountEmail, - UseOauthToken: true, }) // use it with a gcp api client diff --git a/example/tpm/no_policy/main.go b/example/tpm/no_policy/main.go index 7e65a334e..424a7b4a5 100644 --- a/example/tpm/no_policy/main.go +++ b/example/tpm/no_policy/main.go @@ -164,11 +164,10 @@ func main() { // Name: pub.Name, // }, // Email: *serviceAccountEmail, - // UseOauthToken: true, // }) - // log.Printf("======= oauth2 end using persistent handle ========") - // + log.Printf("======= oauth2 end using persistent handle ========") + pub, err := tpm2.ReadPublic{ ObjectHandle: tpm2.TPMHandle(*persistentHandle), //persistent handle }.Execute(rwr) @@ -182,8 +181,7 @@ func main() { Handle: tpm2.TPMHandle(*persistentHandle), // persistent handle Name: pub.Name, }, - Email: *serviceAccountEmail, - UseOauthToken: true, + Email: *serviceAccountEmail, }) if err != nil { log.Fatal(err) @@ -199,21 +197,20 @@ func main() { ctx := context.Background() - // GCS does not support JWTAccessTokens, the following will only work if UseOauthToken is set to True storageClient, err := storage.NewClient(ctx, option.WithTokenSource(ts)) if err != nil { log.Fatal(err) } sit := storageClient.Buckets(ctx, *projectId) for { - _, err := sit.Next() + battrs, err := sit.Next() if err == iterator.Done { break } if err != nil { log.Fatal(err) } - //log.Printf(battrs.Name) + log.Printf(battrs.Name) } i = i + 1 log.Printf("%d\n", i) diff --git a/example/tpm/password_policy/main.go b/example/tpm/password_policy/main.go index 9baf98e3b..2528d7fd5 100644 --- a/example/tpm/password_policy/main.go +++ b/example/tpm/password_policy/main.go @@ -123,9 +123,8 @@ func main() { Handle: tpm2.TPMHandle(*persistentHandle), // persistent handle Name: pub.Name, }, - AuthSession: se, - Email: *serviceAccountEmail, - UseOauthToken: true, + AuthSession: se, + Email: *serviceAccountEmail, }) if err != nil { log.Fatal(err) diff --git a/example/tpm/pcr_policy/main.go b/example/tpm/pcr_policy/main.go index 581bb3a1e..a4226b50d 100644 --- a/example/tpm/pcr_policy/main.go +++ b/example/tpm/pcr_policy/main.go @@ -128,9 +128,8 @@ func main() { Handle: tpm2.TPMHandle(*persistentHandle), // persistent handle Name: pub.Name, }, - AuthSession: p, - Email: *serviceAccountEmail, - UseOauthToken: true, + AuthSession: p, + Email: *serviceAccountEmail, }) if err != nil { log.Fatal(err) diff --git a/tpm/tpm.go b/tpm/tpm.go index c66dc3792..6047ba5ee 100644 --- a/tpm/tpm.go +++ b/tpm/tpm.go @@ -5,13 +5,9 @@ package google import ( - "bytes" "context" - "encoding/json" "fmt" "io" - "net/http" - "net/url" "strings" "sync" "time" @@ -29,12 +25,11 @@ const ( // TpmTokenConfig parameters to start Credential based off of TPM RSA Private Key. type TpmTokenConfig struct { TPMDevice io.ReadWriteCloser - Email, Audience string + Email string NamedHandle tpm2.NamedHandle // load a key from handle AuthSession tpmjwt.Session KeyId string Scopes []string - UseOauthToken bool EncryptionHandle tpm2.TPMHandle // (optional) handle to use for transit encryption EncryptionPub *tpm2.TPMTPublic // (optional) public key to use for transit encryption @@ -43,13 +38,12 @@ type TpmTokenConfig struct { type tpmTokenSource struct { refreshMutex *sync.Mutex oauth2.TokenSource - email, audience string + email string tpmdevice io.ReadWriteCloser namedHandle tpm2.NamedHandle authSession tpmjwt.Session keyId string scopes []string - useOauthToken bool myToken *oauth2.Token encryptionHandle tpm2.TPMHandle // (optional) handle to use for transit encryption encryptionPub *tpm2.TPMTPublic // (optional) public key to use for transit encryption @@ -88,9 +82,6 @@ type ClaimWithSubject struct { // This field is optional but recomended if UseOauthTOken is false // Find the keyId associated with the service account by running: // `gcloud iam service-accounts keys list --iam-account=`` -// UseOauthToken (bool): Use oauth2 access_token (true) or JWTAccessToken (false) -// see: https://developers.google.com/identity/protocols/oauth2/service-account#jwt-auth -// eg: audience="https://pubsub.googleapis.com/google.pubsub.v1.Publisher" func TpmTokenSource(tokenConfig *TpmTokenConfig) (oauth2.TokenSource, error) { if &tokenConfig.NamedHandle == nil || tokenConfig.TPMDevice == nil { @@ -101,10 +92,6 @@ func TpmTokenSource(tokenConfig *TpmTokenConfig) (oauth2.TokenSource, error) { return nil, fmt.Errorf("salrashid123/x/oauth2/google: TPMTokenConfig.Email and cannot be nil") } - if tokenConfig.Audience == "" && tokenConfig.UseOauthToken == false { - return nil, fmt.Errorf("salrashid123/x/oauth2/google: Audience must be specified if UseOauthToken is false") - } - if len(tokenConfig.Scopes) == 0 { tokenConfig.Scopes = []string{GCP_CLOUD_PLATFORM_SCOPE} } @@ -112,12 +99,10 @@ func TpmTokenSource(tokenConfig *TpmTokenConfig) (oauth2.TokenSource, error) { return &tpmTokenSource{ refreshMutex: &sync.Mutex{}, email: tokenConfig.Email, - audience: tokenConfig.Audience, tpmdevice: tokenConfig.TPMDevice, authSession: tokenConfig.AuthSession, keyId: tokenConfig.KeyId, scopes: tokenConfig.Scopes, - useOauthToken: tokenConfig.UseOauthToken, namedHandle: tokenConfig.NamedHandle, encryptionHandle: tokenConfig.EncryptionHandle, encryptionPub: tokenConfig.EncryptionPub, @@ -154,89 +139,28 @@ func (ts *tpmTokenSource) Token() (*oauth2.Token, error) { exp := iat.Add(time.Hour) msg := "" - if ts.useOauthToken { - - claims := &ClaimWithSubject{ - Scope: strings.Join(ts.scopes, " "), - RegisteredClaims: jwt.RegisteredClaims{ - IssuedAt: jwt.NewNumericDate(iat), - ExpiresAt: jwt.NewNumericDate(exp), - Issuer: ts.email, - Audience: []string{"https://oauth2.googleapis.com/token"}, - }, - } - - token := jwt.NewWithClaims(tpmjwt.SigningMethodTPMRS256, claims) - - if ts.keyId != "" { - token.Header["kid"] = ts.keyId - } - - tokenString, err := token.SignedString(keyctx) - if err != nil { - return nil, fmt.Errorf("salrashid123/x/oauth2/google: unable to POST token request, %v", err) - } - - client := &http.Client{} - - data := url.Values{} - data.Set("grant_type", "assertion") - data.Add("assertion_type", "http://oauth.net/grant_type/jwt/1.0/bearer") - data.Add("assertion", tokenString) - - hreq, err := http.NewRequest(http.MethodPost, "https://accounts.google.com/o/oauth2/token", bytes.NewBufferString(data.Encode())) - if err != nil { - return nil, fmt.Errorf("salrashid123/x/oauth2/google: Unable to generate token Request, %v", err) - } - hreq.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") - resp, err := client.Do(hreq) - if err != nil { - return nil, fmt.Errorf("salrashid123/x/oauth2/google: unable to POST token request, %v", err) - } - - if resp.StatusCode != http.StatusOK { - f, err := io.ReadAll(resp.Body) - defer resp.Body.Close() - if err != nil { - return nil, fmt.Errorf("salrashid123/x/oauth2/google: unable to POST token request %v", err) - } - return nil, fmt.Errorf("salrashid123/x/oauth2/google: Token Request error:, %s", string(f)) - } - - f, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("salrashid123/x/oauth2/google: unable to parse tokenresponse, %v", err) - } - resp.Body.Close() - var m rtokenJSON - err = json.Unmarshal(f, &m) - if err != nil { - return nil, fmt.Errorf("salrashid123/x/oauth2/google: Unable to unmarshal response, %v", err) - } - msg = m.AccessToken - - } else { - - claims := &jwt.RegisteredClaims{ + claims := &ClaimWithSubject{ + Scope: strings.Join(ts.scopes, " "), + RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(iat), ExpiresAt: jwt.NewNumericDate(exp), Issuer: ts.email, Subject: ts.email, - Audience: []string{ts.audience}, - } + }, + } - token := jwt.NewWithClaims(tpmjwt.SigningMethodTPMRS256, claims) + token := jwt.NewWithClaims(tpmjwt.SigningMethodTPMRS256, claims) - if ts.keyId != "" { - token.Header["kid"] = ts.keyId - } + if ts.keyId != "" { + token.Header["kid"] = ts.keyId + } - tokenString, err := token.SignedString(keyctx) - if err != nil { - return nil, fmt.Errorf("salrashid123/x/oauth2/google: unable to POST token request, %v", err) - } - msg = tokenString + tokenString, err := token.SignedString(keyctx) + if err != nil { + return nil, fmt.Errorf("salrashid123/x/oauth2/google: unable to POST token request, %v", err) } + msg = tokenString + ts.myToken = &oauth2.Token{AccessToken: msg, TokenType: "Bearer", Expiry: exp} return ts.myToken, nil }