Skip to content

Commit 1136b94

Browse files
network_services: Dual-Token Authentication Support (#6751) (#13041)
Resolves #12775 Signed-off-by: Modular Magician <[email protected]> Signed-off-by: Modular Magician <[email protected]>
1 parent 7b64d79 commit 1136b94

7 files changed

+958
-39
lines changed

.changelog/6751.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
network_services: added `signed_token_options` and `add_signatures` field to `google_network_services_edge_cache_service` and `validation_shared_keys` to `google_network_services_edge_cache_keyset` to support dual-token authentication
3+
```

google/resource_network_services_edge_cache_keyset.go

+133-16
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,27 @@ func resourceNetworkServicesEdgeCacheKeyset() *schema.Resource {
5050
The name must be 1-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* which means the first character must be a letter,
5151
and all following characters must be a dash, underscore, letter or digit.`,
5252
},
53+
"description": {
54+
Type: schema.TypeString,
55+
Optional: true,
56+
Description: `A human-readable description of the resource.`,
57+
},
58+
"labels": {
59+
Type: schema.TypeMap,
60+
Optional: true,
61+
Description: `Set of label tags associated with the EdgeCache resource.`,
62+
Elem: &schema.Schema{Type: schema.TypeString},
63+
},
5364
"public_key": {
5465
Type: schema.TypeList,
55-
Required: true,
66+
Optional: true,
5667
Description: `An ordered list of Ed25519 public keys to use for validating signed requests.
57-
You must specify at least one (1) key, and may have up to three (3) keys.
68+
You must specify 'public_keys' or 'validation_shared_keys' (or both). The keys in 'public_keys' are checked first.
69+
You may specify no more than one Google-managed public key.
70+
If you specify 'public_keys', you must specify at least one (1) key and may specify up to three (3) keys.
5871
5972
Ed25519 public keys are not secret, and only allow Google to validate a request was signed by your corresponding private key.
60-
You should ensure that the private key is kept secret, and that only authorized users can add public keys to a keyset.`,
73+
Ensure that the private key is kept secret, and that only authorized users can add public keys to a keyset.`,
6174
MinItems: 1,
6275
MaxItems: 3,
6376
Elem: &schema.Resource{
@@ -69,26 +82,47 @@ You should ensure that the private key is kept secret, and that only authorized
6982
The name must be 1-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]*
7083
which means the first character must be a letter, and all following characters must be a dash, underscore, letter or digit.`,
7184
},
85+
"managed": {
86+
Type: schema.TypeBool,
87+
Optional: true,
88+
Description: `Set to true to have the CDN automatically manage this public key value.`,
89+
},
7290
"value": {
7391
Type: schema.TypeString,
74-
Required: true,
92+
Optional: true,
7593
Description: `The base64-encoded value of the Ed25519 public key. The base64 encoding can be padded (44 bytes) or unpadded (43 bytes).
7694
Representations or encodings of the public key other than this will be rejected with an error.`,
7795
Sensitive: true,
7896
},
7997
},
8098
},
99+
AtLeastOneOf: []string{"public_key", "validation_shared_keys"},
81100
},
82-
"description": {
83-
Type: schema.TypeString,
84-
Optional: true,
85-
Description: `A human-readable description of the resource.`,
86-
},
87-
"labels": {
88-
Type: schema.TypeMap,
89-
Optional: true,
90-
Description: `Set of label tags associated with the EdgeCache resource.`,
91-
Elem: &schema.Schema{Type: schema.TypeString},
101+
"validation_shared_keys": {
102+
Type: schema.TypeList,
103+
Optional: true,
104+
Description: `An ordered list of shared keys to use for validating signed requests.
105+
Shared keys are secret. Ensure that only authorized users can add 'validation_shared_keys' to a keyset.
106+
You can rotate keys by appending (pushing) a new key to the list of 'validation_shared_keys' and removing any superseded keys.
107+
You must specify 'public_keys' or 'validation_shared_keys' (or both). The keys in 'public_keys' are checked first.`,
108+
MinItems: 1,
109+
MaxItems: 3,
110+
Elem: &schema.Resource{
111+
Schema: map[string]*schema.Schema{
112+
"secret_version": {
113+
Type: schema.TypeString,
114+
Required: true,
115+
Description: `The name of the secret version in Secret Manager.
116+
117+
The resource name of the secret version must be in the format 'projects/*/secrets/*/versions/*' where the '*' values are replaced by the secrets themselves.
118+
The secrets must be at least 16 bytes large. The recommended secret size depends on the signature algorithm you are using.
119+
* If you are using HMAC-SHA1, we suggest 20-byte secrets.
120+
* If you are using HMAC-SHA256, we suggest 32-byte secrets.
121+
See RFC 2104, Section 3 for more details on these recommendations.`,
122+
},
123+
},
124+
},
125+
AtLeastOneOf: []string{"public_key", "validation_shared_keys"},
92126
},
93127
"project": {
94128
Type: schema.TypeString,
@@ -127,6 +161,12 @@ func resourceNetworkServicesEdgeCacheKeysetCreate(d *schema.ResourceData, meta i
127161
} else if v, ok := d.GetOkExists("public_key"); !isEmptyValue(reflect.ValueOf(publicKeysProp)) && (ok || !reflect.DeepEqual(v, publicKeysProp)) {
128162
obj["publicKeys"] = publicKeysProp
129163
}
164+
validationSharedKeysProp, err := expandNetworkServicesEdgeCacheKeysetValidationSharedKeys(d.Get("validation_shared_keys"), d, config)
165+
if err != nil {
166+
return err
167+
} else if v, ok := d.GetOkExists("validation_shared_keys"); !isEmptyValue(reflect.ValueOf(validationSharedKeysProp)) && (ok || !reflect.DeepEqual(v, validationSharedKeysProp)) {
168+
obj["validationSharedKeys"] = validationSharedKeysProp
169+
}
130170

131171
url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheKeysets?edgeCacheKeysetId={{name}}")
132172
if err != nil {
@@ -217,6 +257,9 @@ func resourceNetworkServicesEdgeCacheKeysetRead(d *schema.ResourceData, meta int
217257
if err := d.Set("public_key", flattenNetworkServicesEdgeCacheKeysetPublicKey(res["publicKeys"], d, config)); err != nil {
218258
return fmt.Errorf("Error reading EdgeCacheKeyset: %s", err)
219259
}
260+
if err := d.Set("validation_shared_keys", flattenNetworkServicesEdgeCacheKeysetValidationSharedKeys(res["validationSharedKeys"], d, config)); err != nil {
261+
return fmt.Errorf("Error reading EdgeCacheKeyset: %s", err)
262+
}
220263

221264
return nil
222265
}
@@ -255,6 +298,12 @@ func resourceNetworkServicesEdgeCacheKeysetUpdate(d *schema.ResourceData, meta i
255298
} else if v, ok := d.GetOkExists("public_key"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, publicKeysProp)) {
256299
obj["publicKeys"] = publicKeysProp
257300
}
301+
validationSharedKeysProp, err := expandNetworkServicesEdgeCacheKeysetValidationSharedKeys(d.Get("validation_shared_keys"), d, config)
302+
if err != nil {
303+
return err
304+
} else if v, ok := d.GetOkExists("validation_shared_keys"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, validationSharedKeysProp)) {
305+
obj["validationSharedKeys"] = validationSharedKeysProp
306+
}
258307

259308
url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheKeysets/{{name}}")
260309
if err != nil {
@@ -275,6 +324,10 @@ func resourceNetworkServicesEdgeCacheKeysetUpdate(d *schema.ResourceData, meta i
275324
if d.HasChange("public_key") {
276325
updateMask = append(updateMask, "publicKeys")
277326
}
327+
328+
if d.HasChange("validation_shared_keys") {
329+
updateMask = append(updateMask, "validationSharedKeys")
330+
}
278331
// updateMask is a URL parameter but not present in the schema, so replaceVars
279332
// won't set it
280333
url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
@@ -392,8 +445,9 @@ func flattenNetworkServicesEdgeCacheKeysetPublicKey(v interface{}, d *schema.Res
392445
continue
393446
}
394447
transformed = append(transformed, map[string]interface{}{
395-
"id": flattenNetworkServicesEdgeCacheKeysetPublicKeyId(original["id"], d, config),
396-
"value": flattenNetworkServicesEdgeCacheKeysetPublicKeyValue(original["value"], d, config),
448+
"id": flattenNetworkServicesEdgeCacheKeysetPublicKeyId(original["id"], d, config),
449+
"value": flattenNetworkServicesEdgeCacheKeysetPublicKeyValue(original["value"], d, config),
450+
"managed": flattenNetworkServicesEdgeCacheKeysetPublicKeyManaged(original["managed"], d, config),
397451
})
398452
}
399453
return transformed
@@ -406,6 +460,32 @@ func flattenNetworkServicesEdgeCacheKeysetPublicKeyValue(v interface{}, d *schem
406460
return v
407461
}
408462

463+
func flattenNetworkServicesEdgeCacheKeysetPublicKeyManaged(v interface{}, d *schema.ResourceData, config *Config) interface{} {
464+
return v
465+
}
466+
467+
func flattenNetworkServicesEdgeCacheKeysetValidationSharedKeys(v interface{}, d *schema.ResourceData, config *Config) interface{} {
468+
if v == nil {
469+
return v
470+
}
471+
l := v.([]interface{})
472+
transformed := make([]interface{}, 0, len(l))
473+
for _, raw := range l {
474+
original := raw.(map[string]interface{})
475+
if len(original) < 1 {
476+
// Do not include empty json objects coming back from the api
477+
continue
478+
}
479+
transformed = append(transformed, map[string]interface{}{
480+
"secret_version": flattenNetworkServicesEdgeCacheKeysetValidationSharedKeysSecretVersion(original["secretVersion"], d, config),
481+
})
482+
}
483+
return transformed
484+
}
485+
func flattenNetworkServicesEdgeCacheKeysetValidationSharedKeysSecretVersion(v interface{}, d *schema.ResourceData, config *Config) interface{} {
486+
return v
487+
}
488+
409489
func expandNetworkServicesEdgeCacheKeysetDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
410490
return v, nil
411491
}
@@ -445,6 +525,13 @@ func expandNetworkServicesEdgeCacheKeysetPublicKey(v interface{}, d TerraformRes
445525
transformed["value"] = transformedValue
446526
}
447527

528+
transformedManaged, err := expandNetworkServicesEdgeCacheKeysetPublicKeyManaged(original["managed"], d, config)
529+
if err != nil {
530+
return nil, err
531+
} else if val := reflect.ValueOf(transformedManaged); val.IsValid() && !isEmptyValue(val) {
532+
transformed["managed"] = transformedManaged
533+
}
534+
448535
req = append(req, transformed)
449536
}
450537
return req, nil
@@ -457,3 +544,33 @@ func expandNetworkServicesEdgeCacheKeysetPublicKeyId(v interface{}, d TerraformR
457544
func expandNetworkServicesEdgeCacheKeysetPublicKeyValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
458545
return v, nil
459546
}
547+
548+
func expandNetworkServicesEdgeCacheKeysetPublicKeyManaged(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
549+
return v, nil
550+
}
551+
552+
func expandNetworkServicesEdgeCacheKeysetValidationSharedKeys(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
553+
l := v.([]interface{})
554+
req := make([]interface{}, 0, len(l))
555+
for _, raw := range l {
556+
if raw == nil {
557+
continue
558+
}
559+
original := raw.(map[string]interface{})
560+
transformed := make(map[string]interface{})
561+
562+
transformedSecretVersion, err := expandNetworkServicesEdgeCacheKeysetValidationSharedKeysSecretVersion(original["secret_version"], d, config)
563+
if err != nil {
564+
return nil, err
565+
} else if val := reflect.ValueOf(transformedSecretVersion); val.IsValid() && !isEmptyValue(val) {
566+
transformed["secretVersion"] = transformedSecretVersion
567+
}
568+
569+
req = append(req, transformed)
570+
}
571+
return req, nil
572+
}
573+
574+
func expandNetworkServicesEdgeCacheKeysetValidationSharedKeysSecretVersion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
575+
return v, nil
576+
}

google/resource_network_services_edge_cache_keyset_generated_test.go

+55
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,61 @@ resource "google_network_services_edge_cache_keyset" "default" {
6666
`, context)
6767
}
6868

69+
func TestAccNetworkServicesEdgeCacheKeyset_networkServicesEdgeCacheKeysetDualTokenExample(t *testing.T) {
70+
t.Parallel()
71+
72+
context := map[string]interface{}{
73+
"random_suffix": randString(t, 10),
74+
}
75+
76+
vcrTest(t, resource.TestCase{
77+
PreCheck: func() { testAccPreCheck(t) },
78+
Providers: testAccProviders,
79+
CheckDestroy: testAccCheckNetworkServicesEdgeCacheKeysetDestroyProducer(t),
80+
Steps: []resource.TestStep{
81+
{
82+
Config: testAccNetworkServicesEdgeCacheKeyset_networkServicesEdgeCacheKeysetDualTokenExample(context),
83+
},
84+
{
85+
ResourceName: "google_network_services_edge_cache_keyset.default",
86+
ImportState: true,
87+
ImportStateVerify: true,
88+
ImportStateVerifyIgnore: []string{"name"},
89+
},
90+
},
91+
})
92+
}
93+
94+
func testAccNetworkServicesEdgeCacheKeyset_networkServicesEdgeCacheKeysetDualTokenExample(context map[string]interface{}) string {
95+
return Nprintf(`
96+
resource "google_secret_manager_secret" "secret-basic" {
97+
secret_id = "tf-test-secret-name%{random_suffix}"
98+
99+
replication {
100+
automatic = true
101+
}
102+
}
103+
104+
resource "google_secret_manager_secret_version" "secret-version-basic" {
105+
secret = google_secret_manager_secret.secret-basic.id
106+
107+
secret_data = "secret-data"
108+
}
109+
110+
resource "google_network_services_edge_cache_keyset" "default" {
111+
name = "default%{random_suffix}"
112+
description = "The default keyset"
113+
public_key {
114+
id = "my-public-key"
115+
managed = true
116+
}
117+
validation_shared_keys {
118+
secret_version = google_secret_manager_secret_version.secret-version-basic.id
119+
}
120+
}
121+
`, context)
122+
}
123+
69124
func testAccCheckNetworkServicesEdgeCacheKeysetDestroyProducer(t *testing.T) func(s *terraform.State) error {
70125
return func(s *terraform.State) error {
71126
for name, rs := range s.RootModule().Resources {

0 commit comments

Comments
 (0)