Skip to content

Commit 8c91a3b

Browse files
authored
Merge pull request #1578 from marensofier/add_access_token
Support `GOOGLE_OAUTH_ACCESS_TOKEN` for Google Cloud Platform
2 parents 2e22c04 + ecf3194 commit 8c91a3b

File tree

3 files changed

+81
-11
lines changed

3 files changed

+81
-11
lines changed

README.rst

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,12 @@ It is also possible to use ``updatekeys``, when adding or removing age recipient
266266
267267
Encrypting using GCP KMS
268268
~~~~~~~~~~~~~~~~~~~~~~~~
269-
GCP KMS uses `Application Default Credentials
270-
<https://developers.google.com/identity/protocols/application-default-credentials>`_.
269+
GCP KMS has support for authorization with the use of `Application Default Credentials
270+
<https://developers.google.com/identity/protocols/application-default-credentials>`_ and using an OAuth 2.0 token.
271+
Application default credentials precedes the use of access token.
272+
273+
Using Application Default Credentials you can authorize by doing this:
274+
271275
If you already logged in using
272276
273277
.. code:: sh
@@ -280,6 +284,18 @@ you can enable application default credentials using the sdk:
280284
281285
$ gcloud auth application-default login
282286
287+
Using OAauth tokens you can authorize by doing this:
288+
289+
.. code:: sh
290+
291+
$ export GOOGLE_OAUTH_ACCESS_TOKEN=<your access token>
292+
293+
Or if you are logged in you can authorize by generating an access token:
294+
295+
.. code:: sh
296+
297+
$ export GOOGLE_OAUTH_ACCESS_TOKEN="$(gcloud auth print-access-token)"
298+
283299
Encrypting/decrypting with GCP KMS requires a KMS ResourceID. You can use the
284300
cloud console the get the ResourceID or you can create one using the gcloud
285301
sdk:

gcpkms/keysource.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ const (
2424
// a path to a credentials file, or directly as the variable's value in JSON
2525
// format.
2626
SopsGoogleCredentialsEnv = "GOOGLE_CREDENTIALS"
27+
// SopsGoogleCredentialsOAuthTokenEnv is the environment variable used for the
28+
// GCP OAuth 2.0 Token.
29+
SopsGoogleCredentialsOAuthTokenEnv = "GOOGLE_OAUTH_ACCESS_TOKEN"
2730
// KeyTypeIdentifier is the string used to identify a GCP KMS MasterKey.
2831
KeyTypeIdentifier = "gcp_kms"
2932
)
@@ -245,12 +248,19 @@ func (key *MasterKey) newKMSClient() (*kms.KeyManagementClient, error) {
245248
default:
246249
credentials, err := getGoogleCredentials()
247250
if err != nil {
248-
return nil, err
251+
return nil, fmt.Errorf("credentials: failed to obtain credentials from %q: %w", SopsGoogleCredentialsEnv, err)
249252
}
250253
if credentials != nil {
251254
opts = append(opts, option.WithCredentialsJSON(credentials))
255+
break
256+
}
257+
258+
if atCredentials := getGoogleOAuthTokenFromEnv(); atCredentials != nil {
259+
opts = append(opts, option.WithTokenSource(atCredentials))
260+
break
252261
}
253262
}
263+
254264
if key.grpcConn != nil {
255265
opts = append(opts, option.WithGRPCConn(key.grpcConn))
256266
}
@@ -266,8 +276,8 @@ func (key *MasterKey) newKMSClient() (*kms.KeyManagementClient, error) {
266276

267277
// getGoogleCredentials returns the SopsGoogleCredentialsEnv variable, as
268278
// either the file contents of the path of a credentials file, or as value in
269-
// JSON format. It returns an error if the file cannot be read, and may return
270-
// a nil byte slice if no value is set.
279+
// JSON format.
280+
// It returns an error and a nil byte slice if the file cannot be read.
271281
func getGoogleCredentials() ([]byte, error) {
272282
if defaultCredentials, ok := os.LookupEnv(SopsGoogleCredentialsEnv); ok && len(defaultCredentials) > 0 {
273283
if _, err := os.Stat(defaultCredentials); err == nil {
@@ -277,3 +287,16 @@ func getGoogleCredentials() ([]byte, error) {
277287
}
278288
return nil, nil
279289
}
290+
291+
// getGoogleOAuthTokenFromEnv returns the SopsGoogleCredentialsOauthTokenEnv variable,
292+
// as the OAauth 2.0 token.
293+
// It returns an error and a nil byte slice if the envrionment variable is not set.
294+
func getGoogleOAuthTokenFromEnv() oauth2.TokenSource {
295+
if token, ok := os.LookupEnv(SopsGoogleCredentialsOAuthTokenEnv); ok && len(token) > 0 {
296+
tokenSource := oauth2.StaticTokenSource(
297+
&oauth2.Token{AccessToken: token},
298+
)
299+
return tokenSource
300+
}
301+
return nil
302+
}

gcpkms/keysource_test.go

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ func TestMasterKey_Encrypt(t *testing.T) {
6161
})
6262

6363
key := MasterKey{
64-
grpcConn: newGRPCServer("0"),
65-
ResourceID: testResourceID,
64+
grpcConn: newGRPCServer("0"),
65+
ResourceID: testResourceID,
66+
credentialJSON: []byte("arbitrary credentials"),
6667
}
6768
err := key.Encrypt([]byte("encrypt"))
6869
assert.NoError(t, err)
@@ -88,9 +89,10 @@ func TestMasterKey_Decrypt(t *testing.T) {
8889
Plaintext: []byte(decryptedData),
8990
})
9091
key := MasterKey{
91-
grpcConn: newGRPCServer("0"),
92-
ResourceID: testResourceID,
93-
EncryptedKey: "encryptedKey",
92+
grpcConn: newGRPCServer("0"),
93+
ResourceID: testResourceID,
94+
EncryptedKey: "encryptedKey",
95+
credentialJSON: []byte("arbitrary credentials"),
9496
}
9597
data, err := key.Decrypt()
9698
assert.NoError(t, err)
@@ -124,7 +126,7 @@ func TestMasterKey_ToMap(t *testing.T) {
124126
}, key.ToMap())
125127
}
126128

127-
func TestMasterKey_createCloudKMSService(t *testing.T) {
129+
func TestMasterKey_createCloudKMSService_withCredentialsFile(t *testing.T) {
128130
tests := []struct {
129131
key MasterKey
130132
errString string
@@ -144,6 +146,12 @@ func TestMasterKey_createCloudKMSService(t *testing.T) {
144146
"type": "authorized_user"}`),
145147
},
146148
},
149+
{
150+
key: MasterKey{
151+
ResourceID: testResourceID,
152+
},
153+
errString: `credentials: failed to obtain credentials from "SOPS_GOOGLE_CREDENTIALS"`,
154+
},
147155
}
148156

149157
for _, tt := range tests {
@@ -157,6 +165,29 @@ func TestMasterKey_createCloudKMSService(t *testing.T) {
157165
}
158166
}
159167

168+
func TestMasterKey_createCloudKMSService_withOauthToken(t *testing.T) {
169+
t.Setenv(SopsGoogleCredentialsOAuthTokenEnv, "token")
170+
171+
masterKey := MasterKey{
172+
ResourceID: testResourceID,
173+
}
174+
175+
_, err := masterKey.newKMSClient()
176+
177+
assert.NoError(t, err)
178+
}
179+
180+
func TestMasterKey_createCloudKMSService_withoutCredentials(t *testing.T) {
181+
masterKey := MasterKey{
182+
ResourceID: testResourceID,
183+
}
184+
185+
_, err := masterKey.newKMSClient()
186+
187+
assert.Error(t, err)
188+
assert.ErrorContains(t, err, "credentials: could not find default credentials")
189+
}
190+
160191
func newGRPCServer(port string) *grpc.ClientConn {
161192
serv := grpc.NewServer()
162193
kmspb.RegisterKeyManagementServiceServer(serv, &mockKeyManagement)

0 commit comments

Comments
 (0)