From dcbe864cc63780132b2c86e86260d9e0453aac90 Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Tue, 15 Nov 2022 17:30:41 +0000 Subject: [PATCH] `network_services`: Dual-Token Authentication Support (#6751) Resolves https://github.com/hashicorp/terraform-provider-google/issues/12775 Signed-off-by: Modular Magician --- .changelog/6751.txt | 3 + ...urce_network_services_edge_cache_keyset.go | 149 +++++++- ...rvices_edge_cache_keyset_generated_test.go | 55 +++ ...rce_network_services_edge_cache_service.go | 335 +++++++++++++++++- ...vices_edge_cache_service_generated_test.go | 145 ++++++++ ...k_services_edge_cache_keyset.html.markdown | 100 ++++-- ..._services_edge_cache_service.html.markdown | 210 ++++++++++- 7 files changed, 958 insertions(+), 39 deletions(-) create mode 100644 .changelog/6751.txt diff --git a/.changelog/6751.txt b/.changelog/6751.txt new file mode 100644 index 00000000000..65b86b5375e --- /dev/null +++ b/.changelog/6751.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +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 +``` diff --git a/google/resource_network_services_edge_cache_keyset.go b/google/resource_network_services_edge_cache_keyset.go index e72269882d6..068cd257ba8 100644 --- a/google/resource_network_services_edge_cache_keyset.go +++ b/google/resource_network_services_edge_cache_keyset.go @@ -50,14 +50,27 @@ func resourceNetworkServicesEdgeCacheKeyset() *schema.Resource { 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, and all following characters must be a dash, underscore, letter or digit.`, }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `A human-readable description of the resource.`, + }, + "labels": { + Type: schema.TypeMap, + Optional: true, + Description: `Set of label tags associated with the EdgeCache resource.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, "public_key": { Type: schema.TypeList, - Required: true, + Optional: true, Description: `An ordered list of Ed25519 public keys to use for validating signed requests. -You must specify at least one (1) key, and may have up to three (3) keys. +You must specify 'public_keys' or 'validation_shared_keys' (or both). The keys in 'public_keys' are checked first. +You may specify no more than one Google-managed public key. +If you specify 'public_keys', you must specify at least one (1) key and may specify up to three (3) keys. Ed25519 public keys are not secret, and only allow Google to validate a request was signed by your corresponding private key. -You should ensure that the private key is kept secret, and that only authorized users can add public keys to a keyset.`, +Ensure that the private key is kept secret, and that only authorized users can add public keys to a keyset.`, MinItems: 1, MaxItems: 3, Elem: &schema.Resource{ @@ -69,26 +82,47 @@ You should ensure that the private key is kept secret, and that only authorized 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, and all following characters must be a dash, underscore, letter or digit.`, }, + "managed": { + Type: schema.TypeBool, + Optional: true, + Description: `Set to true to have the CDN automatically manage this public key value.`, + }, "value": { Type: schema.TypeString, - Required: true, + Optional: true, Description: `The base64-encoded value of the Ed25519 public key. The base64 encoding can be padded (44 bytes) or unpadded (43 bytes). Representations or encodings of the public key other than this will be rejected with an error.`, Sensitive: true, }, }, }, + AtLeastOneOf: []string{"public_key", "validation_shared_keys"}, }, - "description": { - Type: schema.TypeString, - Optional: true, - Description: `A human-readable description of the resource.`, - }, - "labels": { - Type: schema.TypeMap, - Optional: true, - Description: `Set of label tags associated with the EdgeCache resource.`, - Elem: &schema.Schema{Type: schema.TypeString}, + "validation_shared_keys": { + Type: schema.TypeList, + Optional: true, + Description: `An ordered list of shared keys to use for validating signed requests. +Shared keys are secret. Ensure that only authorized users can add 'validation_shared_keys' to a keyset. +You can rotate keys by appending (pushing) a new key to the list of 'validation_shared_keys' and removing any superseded keys. +You must specify 'public_keys' or 'validation_shared_keys' (or both). The keys in 'public_keys' are checked first.`, + MinItems: 1, + MaxItems: 3, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "secret_version": { + Type: schema.TypeString, + Required: true, + Description: `The name of the secret version in Secret Manager. + +The resource name of the secret version must be in the format 'projects/*/secrets/*/versions/*' where the '*' values are replaced by the secrets themselves. +The secrets must be at least 16 bytes large. The recommended secret size depends on the signature algorithm you are using. +* If you are using HMAC-SHA1, we suggest 20-byte secrets. +* If you are using HMAC-SHA256, we suggest 32-byte secrets. +See RFC 2104, Section 3 for more details on these recommendations.`, + }, + }, + }, + AtLeastOneOf: []string{"public_key", "validation_shared_keys"}, }, "project": { Type: schema.TypeString, @@ -127,6 +161,12 @@ func resourceNetworkServicesEdgeCacheKeysetCreate(d *schema.ResourceData, meta i } else if v, ok := d.GetOkExists("public_key"); !isEmptyValue(reflect.ValueOf(publicKeysProp)) && (ok || !reflect.DeepEqual(v, publicKeysProp)) { obj["publicKeys"] = publicKeysProp } + validationSharedKeysProp, err := expandNetworkServicesEdgeCacheKeysetValidationSharedKeys(d.Get("validation_shared_keys"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("validation_shared_keys"); !isEmptyValue(reflect.ValueOf(validationSharedKeysProp)) && (ok || !reflect.DeepEqual(v, validationSharedKeysProp)) { + obj["validationSharedKeys"] = validationSharedKeysProp + } url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheKeysets?edgeCacheKeysetId={{name}}") if err != nil { @@ -217,6 +257,9 @@ func resourceNetworkServicesEdgeCacheKeysetRead(d *schema.ResourceData, meta int if err := d.Set("public_key", flattenNetworkServicesEdgeCacheKeysetPublicKey(res["publicKeys"], d, config)); err != nil { return fmt.Errorf("Error reading EdgeCacheKeyset: %s", err) } + if err := d.Set("validation_shared_keys", flattenNetworkServicesEdgeCacheKeysetValidationSharedKeys(res["validationSharedKeys"], d, config)); err != nil { + return fmt.Errorf("Error reading EdgeCacheKeyset: %s", err) + } return nil } @@ -255,6 +298,12 @@ func resourceNetworkServicesEdgeCacheKeysetUpdate(d *schema.ResourceData, meta i } else if v, ok := d.GetOkExists("public_key"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, publicKeysProp)) { obj["publicKeys"] = publicKeysProp } + validationSharedKeysProp, err := expandNetworkServicesEdgeCacheKeysetValidationSharedKeys(d.Get("validation_shared_keys"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("validation_shared_keys"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, validationSharedKeysProp)) { + obj["validationSharedKeys"] = validationSharedKeysProp + } url, err := replaceVars(d, config, "{{NetworkServicesBasePath}}projects/{{project}}/locations/global/edgeCacheKeysets/{{name}}") if err != nil { @@ -275,6 +324,10 @@ func resourceNetworkServicesEdgeCacheKeysetUpdate(d *schema.ResourceData, meta i if d.HasChange("public_key") { updateMask = append(updateMask, "publicKeys") } + + if d.HasChange("validation_shared_keys") { + updateMask = append(updateMask, "validationSharedKeys") + } // updateMask is a URL parameter but not present in the schema, so replaceVars // won't set it url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) @@ -392,8 +445,9 @@ func flattenNetworkServicesEdgeCacheKeysetPublicKey(v interface{}, d *schema.Res continue } transformed = append(transformed, map[string]interface{}{ - "id": flattenNetworkServicesEdgeCacheKeysetPublicKeyId(original["id"], d, config), - "value": flattenNetworkServicesEdgeCacheKeysetPublicKeyValue(original["value"], d, config), + "id": flattenNetworkServicesEdgeCacheKeysetPublicKeyId(original["id"], d, config), + "value": flattenNetworkServicesEdgeCacheKeysetPublicKeyValue(original["value"], d, config), + "managed": flattenNetworkServicesEdgeCacheKeysetPublicKeyManaged(original["managed"], d, config), }) } return transformed @@ -406,6 +460,32 @@ func flattenNetworkServicesEdgeCacheKeysetPublicKeyValue(v interface{}, d *schem return v } +func flattenNetworkServicesEdgeCacheKeysetPublicKeyManaged(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheKeysetValidationSharedKeys(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "secret_version": flattenNetworkServicesEdgeCacheKeysetValidationSharedKeysSecretVersion(original["secretVersion"], d, config), + }) + } + return transformed +} +func flattenNetworkServicesEdgeCacheKeysetValidationSharedKeysSecretVersion(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func expandNetworkServicesEdgeCacheKeysetDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } @@ -445,6 +525,13 @@ func expandNetworkServicesEdgeCacheKeysetPublicKey(v interface{}, d TerraformRes transformed["value"] = transformedValue } + transformedManaged, err := expandNetworkServicesEdgeCacheKeysetPublicKeyManaged(original["managed"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedManaged); val.IsValid() && !isEmptyValue(val) { + transformed["managed"] = transformedManaged + } + req = append(req, transformed) } return req, nil @@ -457,3 +544,33 @@ func expandNetworkServicesEdgeCacheKeysetPublicKeyId(v interface{}, d TerraformR func expandNetworkServicesEdgeCacheKeysetPublicKeyValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } + +func expandNetworkServicesEdgeCacheKeysetPublicKeyManaged(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheKeysetValidationSharedKeys(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedSecretVersion, err := expandNetworkServicesEdgeCacheKeysetValidationSharedKeysSecretVersion(original["secret_version"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSecretVersion); val.IsValid() && !isEmptyValue(val) { + transformed["secretVersion"] = transformedSecretVersion + } + + req = append(req, transformed) + } + return req, nil +} + +func expandNetworkServicesEdgeCacheKeysetValidationSharedKeysSecretVersion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} diff --git a/google/resource_network_services_edge_cache_keyset_generated_test.go b/google/resource_network_services_edge_cache_keyset_generated_test.go index eecf8c0d7f4..79923d735eb 100644 --- a/google/resource_network_services_edge_cache_keyset_generated_test.go +++ b/google/resource_network_services_edge_cache_keyset_generated_test.go @@ -66,6 +66,61 @@ resource "google_network_services_edge_cache_keyset" "default" { `, context) } +func TestAccNetworkServicesEdgeCacheKeyset_networkServicesEdgeCacheKeysetDualTokenExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkServicesEdgeCacheKeysetDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccNetworkServicesEdgeCacheKeyset_networkServicesEdgeCacheKeysetDualTokenExample(context), + }, + { + ResourceName: "google_network_services_edge_cache_keyset.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name"}, + }, + }, + }) +} + +func testAccNetworkServicesEdgeCacheKeyset_networkServicesEdgeCacheKeysetDualTokenExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_secret_manager_secret" "secret-basic" { + secret_id = "tf-test-secret-name%{random_suffix}" + + replication { + automatic = true + } +} + +resource "google_secret_manager_secret_version" "secret-version-basic" { + secret = google_secret_manager_secret.secret-basic.id + + secret_data = "secret-data" +} + +resource "google_network_services_edge_cache_keyset" "default" { + name = "default%{random_suffix}" + description = "The default keyset" + public_key { + id = "my-public-key" + managed = true + } + validation_shared_keys { + secret_version = google_secret_manager_secret_version.secret-version-basic.id + } +} +`, context) +} + func testAccCheckNetworkServicesEdgeCacheKeysetDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { for name, rs := range s.RootModule().Resources { diff --git a/google/resource_network_services_edge_cache_service.go b/google/resource_network_services_edge_cache_service.go index 3a4b6cd4628..f3ce3bbc3c5 100644 --- a/google/resource_network_services_edge_cache_service.go +++ b/google/resource_network_services_edge_cache_service.go @@ -373,6 +373,90 @@ Only one of origin or urlRedirect can be set.`, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "add_signatures": { + Type: schema.TypeList, + Optional: true, + Description: `Enable signature generation or propagation on this route. + +This field may only be specified when signedRequestMode is set to REQUIRE_TOKENS.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "actions": { + Type: schema.TypeList, + Required: true, + Description: `The actions to take to add signatures to responses. Possible values: ["GENERATE_COOKIE", "GENERATE_TOKEN_HLS_COOKIELESS", "PROPAGATE_TOKEN_HLS_COOKIELESS"]`, + MaxItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateEnum([]string{"GENERATE_COOKIE", "GENERATE_TOKEN_HLS_COOKIELESS", "PROPAGATE_TOKEN_HLS_COOKIELESS"}), + }, + }, + "copied_parameters": { + Type: schema.TypeList, + Optional: true, + Description: `The parameters to copy from the verified token to the generated token. + +Only the following parameters may be copied: + + * 'PathGlobs' + * 'paths' + * 'acl' + * 'URLPrefix' + * 'IPRanges' + * 'SessionID' + * 'id' + * 'Data' + * 'data' + * 'payload' + * 'Headers' + +You may specify up to 6 parameters to copy. A given parameter is be copied only if the parameter exists in the verified token. Parameter names are matched exactly as specified. The order of the parameters does not matter. Duplicates are not allowed. + +This field may only be specified when the GENERATE_COOKIE or GENERATE_TOKEN_HLS_COOKIELESS actions are specified.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "keyset": { + Type: schema.TypeString, + Optional: true, + Description: `The keyset to use for signature generation. + +The following are both valid paths to an EdgeCacheKeyset resource: + + * 'projects/project/locations/global/edgeCacheKeysets/yourKeyset' + * 'yourKeyset' + +This must be specified when the GENERATE_COOKIE or GENERATE_TOKEN_HLS_COOKIELESS actions are specified. This field may not be specified otherwise.`, + }, + "token_query_parameter": { + Type: schema.TypeString, + Optional: true, + Description: `The query parameter in which to put the generated token. + +If not specified, defaults to 'edge-cache-token'. + +If specified, 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, and all following characters must be a dash, underscore, letter or digit. + +This field may only be set when the GENERATE_TOKEN_HLS_COOKIELESS or PROPAGATE_TOKEN_HLS_COOKIELESS actions are specified.`, + }, + "token_ttl": { + Type: schema.TypeString, + Optional: true, + Description: `The duration the token is valid starting from the moment the token is first generated. + +Defaults to '86400s' (1 day). + +The TTL must be >= 0 and <= 604,800 seconds (1 week). + +This field may only be specified when the GENERATE_COOKIE or GENERATE_TOKEN_HLS_COOKIELESS actions are specified. + +A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s".`, + }, + }, + }, + }, "cache_key_policy": { Type: schema.TypeList, Optional: true, @@ -555,16 +639,63 @@ Note that when specifying an explicit negativeCachingPolicy, you should take car Optional: true, Description: `The EdgeCacheKeyset containing the set of public keys used to validate signed requests at the edge.`, }, + "signed_request_maximum_expiration_ttl": { + Type: schema.TypeString, + Optional: true, + Description: `Limit how far into the future the expiration time of a signed request may be. + +When set, a signed request is rejected if its expiration time is later than now + signedRequestMaximumExpirationTtl, where now is the time at which the signed request is first handled by the CDN. + +- The TTL must be > 0. +- Fractions of a second are not allowed. + +By default, signedRequestMaximumExpirationTtl is not set and the expiration time of a signed request may be arbitrarily far into future.`, + }, "signed_request_mode": { Type: schema.TypeString, Computed: true, Optional: true, - ValidateFunc: validateEnum([]string{"DISABLED", "REQUIRE_SIGNATURES", ""}), + ValidateFunc: validateEnum([]string{"DISABLED", "REQUIRE_SIGNATURES", "REQUIRE_TOKENS", ""}), Description: `Whether to enforce signed requests. The default value is DISABLED, which means all content is public, and does not authorize access. You must also set a signedRequestKeyset to enable signed requests. -When set to REQUIRE_SIGNATURES, all matching requests will have their signature validated. Requests that were not signed with the corresponding private key, or that are otherwise invalid (expired, do not match the signature, IP address, or header) will be rejected with a HTTP 403 and (if enabled) logged. Possible values: ["DISABLED", "REQUIRE_SIGNATURES"]`, +When set to REQUIRE_SIGNATURES, all matching requests will have their signature validated. Requests that were not signed with the corresponding private key, or that are otherwise invalid (expired, do not match the signature, IP address, or header) will be rejected with a HTTP 403 and (if enabled) logged. Possible values: ["DISABLED", "REQUIRE_SIGNATURES", "REQUIRE_TOKENS"]`, + }, + "signed_token_options": { + Type: schema.TypeList, + Optional: true, + Description: `Additional options for signed tokens. + +signedTokenOptions may only be specified when signedRequestMode is REQUIRE_TOKENS.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allowed_signature_algorithms": { + Type: schema.TypeList, + Optional: true, + Description: `The allowed signature algorithms to use. + +Defaults to using only ED25519. + +You may specify up to 3 signature algorithms to use. Possible values: ["ED25519", "HMAC_SHA_256", "HMAC_SHA1"]`, + MaxItems: 3, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateEnum([]string{"ED25519", "HMAC_SHA_256", "HMAC_SHA1"}), + }, + }, + "token_query_parameter": { + Type: schema.TypeString, + Optional: true, + Description: `The query parameter in which to find the token. + +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, and all following characters must be a dash, underscore, letter or digit. + +Defaults to 'edge-cache-token'.`, + }, + }, + }, }, }, }, @@ -1704,6 +1835,12 @@ func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActio flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedRequestMode(original["signedRequestMode"], d, config) transformed["signed_request_keyset"] = flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedRequestKeyset(original["signedRequestKeyset"], d, config) + transformed["signed_token_options"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedTokenOptions(original["signedTokenOptions"], d, config) + transformed["add_signatures"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignatures(original["addSignatures"], d, config) + transformed["signed_request_maximum_expiration_ttl"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedRequestMaximumExpirationTtl(original["signedRequestMaximumExpirationTtl"], d, config) return []interface{}{transformed} } func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyCacheMode(v interface{}, d *schema.ResourceData, config *Config) interface{} { @@ -1791,6 +1928,74 @@ func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActio return v } +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedTokenOptions(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["token_query_parameter"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedTokenOptionsTokenQueryParameter(original["tokenQueryParameter"], d, config) + transformed["allowed_signature_algorithms"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedTokenOptionsAllowedSignatureAlgorithms(original["allowedSignatureAlgorithms"], d, config) + return []interface{}{transformed} +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedTokenOptionsTokenQueryParameter(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedTokenOptionsAllowedSignatureAlgorithms(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignatures(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["actions"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesActions(original["actions"], d, config) + transformed["keyset"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesKeyset(original["keyset"], d, config) + transformed["token_ttl"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesTokenTtl(original["tokenTtl"], d, config) + transformed["token_query_parameter"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesTokenQueryParameter(original["tokenQueryParameter"], d, config) + transformed["copied_parameters"] = + flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesCopiedParameters(original["copiedParameters"], d, config) + return []interface{}{transformed} +} +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesActions(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesKeyset(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesTokenTtl(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesTokenQueryParameter(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesCopiedParameters(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedRequestMaximumExpirationTtl(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func flattenNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewrite(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { return nil @@ -2671,6 +2876,27 @@ func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteAction transformed["signedRequestKeyset"] = transformedSignedRequestKeyset } + transformedSignedTokenOptions, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedTokenOptions(original["signed_token_options"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSignedTokenOptions); val.IsValid() && !isEmptyValue(val) { + transformed["signedTokenOptions"] = transformedSignedTokenOptions + } + + transformedAddSignatures, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignatures(original["add_signatures"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAddSignatures); val.IsValid() && !isEmptyValue(val) { + transformed["addSignatures"] = transformedAddSignatures + } + + transformedSignedRequestMaximumExpirationTtl, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedRequestMaximumExpirationTtl(original["signed_request_maximum_expiration_ttl"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSignedRequestMaximumExpirationTtl); val.IsValid() && !isEmptyValue(val) { + transformed["signedRequestMaximumExpirationTtl"] = transformedSignedRequestMaximumExpirationTtl + } + return transformed, nil } @@ -2802,6 +3028,111 @@ func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteAction return v, nil } +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedTokenOptions(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedTokenQueryParameter, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedTokenOptionsTokenQueryParameter(original["token_query_parameter"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTokenQueryParameter); val.IsValid() && !isEmptyValue(val) { + transformed["tokenQueryParameter"] = transformedTokenQueryParameter + } + + transformedAllowedSignatureAlgorithms, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedTokenOptionsAllowedSignatureAlgorithms(original["allowed_signature_algorithms"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowedSignatureAlgorithms); val.IsValid() && !isEmptyValue(val) { + transformed["allowedSignatureAlgorithms"] = transformedAllowedSignatureAlgorithms + } + + return transformed, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedTokenOptionsTokenQueryParameter(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedTokenOptionsAllowedSignatureAlgorithms(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignatures(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedActions, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesActions(original["actions"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedActions); val.IsValid() && !isEmptyValue(val) { + transformed["actions"] = transformedActions + } + + transformedKeyset, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesKeyset(original["keyset"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedKeyset); val.IsValid() && !isEmptyValue(val) { + transformed["keyset"] = transformedKeyset + } + + transformedTokenTtl, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesTokenTtl(original["token_ttl"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTokenTtl); val.IsValid() && !isEmptyValue(val) { + transformed["tokenTtl"] = transformedTokenTtl + } + + transformedTokenQueryParameter, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesTokenQueryParameter(original["token_query_parameter"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTokenQueryParameter); val.IsValid() && !isEmptyValue(val) { + transformed["tokenQueryParameter"] = transformedTokenQueryParameter + } + + transformedCopiedParameters, err := expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesCopiedParameters(original["copied_parameters"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCopiedParameters); val.IsValid() && !isEmptyValue(val) { + transformed["copiedParameters"] = transformedCopiedParameters + } + + return transformed, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesActions(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesKeyset(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesTokenTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesTokenQueryParameter(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicyAddSignaturesCopiedParameters(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionCdnPolicySignedRequestMaximumExpirationTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandNetworkServicesEdgeCacheServiceRoutingPathMatcherRouteRuleRouteActionUrlRewrite(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { l := v.([]interface{}) if len(l) == 0 || l[0] == nil { diff --git a/google/resource_network_services_edge_cache_service_generated_test.go b/google/resource_network_services_edge_cache_service_generated_test.go index de2e43840eb..27dc98b19a6 100644 --- a/google/resource_network_services_edge_cache_service_generated_test.go +++ b/google/resource_network_services_edge_cache_service_generated_test.go @@ -317,6 +317,151 @@ resource "google_network_services_edge_cache_service" "instance" { `, context) } +func TestAccNetworkServicesEdgeCacheService_networkServicesEdgeCacheServiceDualTokenExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkServicesEdgeCacheServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccNetworkServicesEdgeCacheService_networkServicesEdgeCacheServiceDualTokenExample(context), + }, + { + ResourceName: "google_network_services_edge_cache_service.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name"}, + }, + }, + }) +} + +func testAccNetworkServicesEdgeCacheService_networkServicesEdgeCacheServiceDualTokenExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_secret_manager_secret" "secret-basic" { + secret_id = "tf-test-secret-name%{random_suffix}" + + replication { + automatic = true + } +} + +resource "google_secret_manager_secret_version" "secret-version-basic" { + secret = google_secret_manager_secret.secret-basic.id + + secret_data = "secret-data" +} + +resource "google_network_services_edge_cache_keyset" "keyset" { + name = "tf-test-keyset-name%{random_suffix}" + description = "The default keyset" + public_key { + id = "my-public-key" + managed = true + } + validation_shared_keys { + secret_version = google_secret_manager_secret_version.secret-version-basic.id + } +} + +resource "google_network_services_edge_cache_origin" "instance" { + name = "tf-test-my-origin%{random_suffix}" + origin_address = "gs://media-edge-default" + description = "The default bucket for media edge test" +} + +resource "google_network_services_edge_cache_service" "instance" { + name = "tf-test-my-service%{random_suffix}" + description = "some description" + routing { + host_rule { + description = "host rule description" + hosts = ["sslcert.tf-test.club"] + path_matcher = "routes" + } + path_matcher { + name = "routes" + route_rule { + description = "a route rule to match against master playlist" + priority = 1 + match_rule { + path_template_match = "/master.m3u8" + } + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + signed_request_mode = "REQUIRE_TOKENS" + signed_request_keyset = google_network_services_edge_cache_keyset.keyset.id + signed_token_options { + token_query_parameter = "edge-cache-token" + } + signed_request_maximum_expiration_ttl = "600s" + add_signatures { + actions = ["GENERATE_COOKIE"] + keyset = google_network_services_edge_cache_keyset.keyset.id + copied_parameters = ["PathGlobs", "SessionID"] + } + } + } + } + route_rule { + description = "a route rule to match against all playlists" + priority = 2 + match_rule { + path_template_match = "/*.m3u8" + } + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + signed_request_mode = "REQUIRE_TOKENS" + signed_request_keyset = google_network_services_edge_cache_keyset.keyset.id + signed_token_options { + token_query_parameter = "hdnts" + allowed_signature_algorithms = ["ED25519", "HMAC_SHA_256", "HMAC_SHA1"] + } + add_signatures { + actions = ["GENERATE_TOKEN_HLS_COOKIELESS"] + keyset = google_network_services_edge_cache_keyset.keyset.id + token_ttl = "1200s" + token_query_parameter = "hdntl" + copied_parameters = ["URLPrefix"] + } + } + } + } + route_rule { + description = "a route rule to match against" + priority = 3 + match_rule { + path_template_match = "/**.m3u8" + } + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + signed_request_mode = "REQUIRE_TOKENS" + signed_request_keyset = google_network_services_edge_cache_keyset.keyset.id + signed_token_options { + token_query_parameter = "hdntl" + } + add_signatures { + actions = ["PROPAGATE_TOKEN_HLS_COOKIELESS"] + token_query_parameter = "hdntl" + } + } + } + } + } + } +} +`, context) +} + func testAccCheckNetworkServicesEdgeCacheServiceDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { for name, rs := range s.RootModule().Resources { diff --git a/website/docs/r/network_services_edge_cache_keyset.html.markdown b/website/docs/r/network_services_edge_cache_keyset.html.markdown index 1e338c3db2e..053ead18ee1 100644 --- a/website/docs/r/network_services_edge_cache_keyset.html.markdown +++ b/website/docs/r/network_services_edge_cache_keyset.html.markdown @@ -50,20 +50,47 @@ resource "google_network_services_edge_cache_keyset" "default" { } } ``` + +## Example Usage - Network Services Edge Cache Keyset Dual Token + + +```hcl +resource "google_secret_manager_secret" "secret-basic" { + secret_id = "secret-name" + + replication { + automatic = true + } +} + +resource "google_secret_manager_secret_version" "secret-version-basic" { + secret = google_secret_manager_secret.secret-basic.id + + secret_data = "secret-data" +} + +resource "google_network_services_edge_cache_keyset" "default" { + name = "default" + description = "The default keyset" + public_key { + id = "my-public-key" + managed = true + } + validation_shared_keys { + secret_version = google_secret_manager_secret_version.secret-version-basic.id + } +} +``` ## Argument Reference The following arguments are supported: -* `public_key` - - (Required) - An ordered list of Ed25519 public keys to use for validating signed requests. - You must specify at least one (1) key, and may have up to three (3) keys. - Ed25519 public keys are not secret, and only allow Google to validate a request was signed by your corresponding private key. - You should ensure that the private key is kept secret, and that only authorized users can add public keys to a keyset. - Structure is [documented below](#nested_public_key). - * `name` - (Required) Name of the resource; provided by the client when the resource is created. @@ -71,6 +98,39 @@ The following arguments are supported: and all following characters must be a dash, underscore, letter or digit. +- - - + + +* `description` - + (Optional) + A human-readable description of the resource. + +* `labels` - + (Optional) + Set of label tags associated with the EdgeCache resource. + +* `public_key` - + (Optional) + An ordered list of Ed25519 public keys to use for validating signed requests. + You must specify `public_keys` or `validation_shared_keys` (or both). The keys in `public_keys` are checked first. + You may specify no more than one Google-managed public key. + If you specify `public_keys`, you must specify at least one (1) key and may specify up to three (3) keys. + Ed25519 public keys are not secret, and only allow Google to validate a request was signed by your corresponding private key. + Ensure that the private key is kept secret, and that only authorized users can add public keys to a keyset. + Structure is [documented below](#nested_public_key). + +* `validation_shared_keys` - + (Optional) + An ordered list of shared keys to use for validating signed requests. + Shared keys are secret. Ensure that only authorized users can add `validation_shared_keys` to a keyset. + You can rotate keys by appending (pushing) a new key to the list of `validation_shared_keys` and removing any superseded keys. + You must specify `public_keys` or `validation_shared_keys` (or both). The keys in `public_keys` are checked first. + Structure is [documented below](#nested_validation_shared_keys). + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. + + The `public_key` block supports: * `id` - @@ -80,25 +140,25 @@ The following arguments are supported: which means the first character must be a letter, and all following characters must be a dash, underscore, letter or digit. * `value` - - (Required) + (Optional) The base64-encoded value of the Ed25519 public key. The base64 encoding can be padded (44 bytes) or unpadded (43 bytes). Representations or encodings of the public key other than this will be rejected with an error. **Note**: This property is sensitive and will not be displayed in the plan. -- - - - - -* `description` - +* `managed` - (Optional) - A human-readable description of the resource. + Set to true to have the CDN automatically manage this public key value. -* `labels` - - (Optional) - Set of label tags associated with the EdgeCache resource. - -* `project` - (Optional) The ID of the project in which the resource belongs. - If it is not provided, the provider project is used. +The `validation_shared_keys` block supports: +* `secret_version` - + (Required) + The name of the secret version in Secret Manager. + The resource name of the secret version must be in the format `projects/*/secrets/*/versions/*` where the `*` values are replaced by the secrets themselves. + The secrets must be at least 16 bytes large. The recommended secret size depends on the signature algorithm you are using. + * If you are using HMAC-SHA1, we suggest 20-byte secrets. + * If you are using HMAC-SHA256, we suggest 32-byte secrets. + See RFC 2104, Section 3 for more details on these recommendations. ## Attributes Reference diff --git a/website/docs/r/network_services_edge_cache_service.html.markdown b/website/docs/r/network_services_edge_cache_service.html.markdown index c7e02115940..cf6750c66ed 100644 --- a/website/docs/r/network_services_edge_cache_service.html.markdown +++ b/website/docs/r/network_services_edge_cache_service.html.markdown @@ -280,6 +280,131 @@ resource "google_network_services_edge_cache_service" "instance" { } } ``` + +## Example Usage - Network Services Edge Cache Service Dual Token + + +```hcl +resource "google_secret_manager_secret" "secret-basic" { + secret_id = "secret-name" + + replication { + automatic = true + } +} + +resource "google_secret_manager_secret_version" "secret-version-basic" { + secret = google_secret_manager_secret.secret-basic.id + + secret_data = "secret-data" +} + +resource "google_network_services_edge_cache_keyset" "keyset" { + name = "keyset-name" + description = "The default keyset" + public_key { + id = "my-public-key" + managed = true + } + validation_shared_keys { + secret_version = google_secret_manager_secret_version.secret-version-basic.id + } +} + +resource "google_network_services_edge_cache_origin" "instance" { + name = "my-origin" + origin_address = "gs://media-edge-default" + description = "The default bucket for media edge test" +} + +resource "google_network_services_edge_cache_service" "instance" { + name = "my-service" + description = "some description" + routing { + host_rule { + description = "host rule description" + hosts = ["sslcert.tf-test.club"] + path_matcher = "routes" + } + path_matcher { + name = "routes" + route_rule { + description = "a route rule to match against master playlist" + priority = 1 + match_rule { + path_template_match = "/master.m3u8" + } + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + signed_request_mode = "REQUIRE_TOKENS" + signed_request_keyset = google_network_services_edge_cache_keyset.keyset.id + signed_token_options { + token_query_parameter = "edge-cache-token" + } + signed_request_maximum_expiration_ttl = "600s" + add_signatures { + actions = ["GENERATE_COOKIE"] + keyset = google_network_services_edge_cache_keyset.keyset.id + copied_parameters = ["PathGlobs", "SessionID"] + } + } + } + } + route_rule { + description = "a route rule to match against all playlists" + priority = 2 + match_rule { + path_template_match = "/*.m3u8" + } + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + signed_request_mode = "REQUIRE_TOKENS" + signed_request_keyset = google_network_services_edge_cache_keyset.keyset.id + signed_token_options { + token_query_parameter = "hdnts" + allowed_signature_algorithms = ["ED25519", "HMAC_SHA_256", "HMAC_SHA1"] + } + add_signatures { + actions = ["GENERATE_TOKEN_HLS_COOKIELESS"] + keyset = google_network_services_edge_cache_keyset.keyset.id + token_ttl = "1200s" + token_query_parameter = "hdntl" + copied_parameters = ["URLPrefix"] + } + } + } + } + route_rule { + description = "a route rule to match against" + priority = 3 + match_rule { + path_template_match = "/**.m3u8" + } + origin = google_network_services_edge_cache_origin.instance.name + route_action { + cdn_policy { + signed_request_mode = "REQUIRE_TOKENS" + signed_request_keyset = google_network_services_edge_cache_keyset.keyset.id + signed_token_options { + token_query_parameter = "hdntl" + } + add_signatures { + actions = ["PROPAGATE_TOKEN_HLS_COOKIELESS"] + token_query_parameter = "hdntl" + } + } + } + } + } + } +} +``` ## Argument Reference @@ -620,12 +745,32 @@ The following arguments are supported: Whether to enforce signed requests. The default value is DISABLED, which means all content is public, and does not authorize access. You must also set a signedRequestKeyset to enable signed requests. When set to REQUIRE_SIGNATURES, all matching requests will have their signature validated. Requests that were not signed with the corresponding private key, or that are otherwise invalid (expired, do not match the signature, IP address, or header) will be rejected with a HTTP 403 and (if enabled) logged. - Possible values are `DISABLED` and `REQUIRE_SIGNATURES`. + Possible values are `DISABLED`, `REQUIRE_SIGNATURES`, and `REQUIRE_TOKENS`. * `signed_request_keyset` - (Optional) The EdgeCacheKeyset containing the set of public keys used to validate signed requests at the edge. +* `signed_token_options` - + (Optional) + Additional options for signed tokens. + signedTokenOptions may only be specified when signedRequestMode is REQUIRE_TOKENS. + Structure is [documented below](#nested_signed_token_options). + +* `add_signatures` - + (Optional) + Enable signature generation or propagation on this route. + This field may only be specified when signedRequestMode is set to REQUIRE_TOKENS. + Structure is [documented below](#nested_add_signatures). + +* `signed_request_maximum_expiration_ttl` - + (Optional) + Limit how far into the future the expiration time of a signed request may be. + When set, a signed request is rejected if its expiration time is later than now + signedRequestMaximumExpirationTtl, where now is the time at which the signed request is first handled by the CDN. + - The TTL must be > 0. + - Fractions of a second are not allowed. + By default, signedRequestMaximumExpirationTtl is not set and the expiration time of a signed request may be arbitrarily far into future. + The `cache_key_policy` block supports: @@ -675,6 +820,69 @@ The following arguments are supported: Note that specifying several cookies, and/or cookies that have a large range of values (e.g., per-user) will dramatically impact the cache hit rate, and may result in a higher eviction rate and reduced performance. You may specify up to three cookie names. +The `signed_token_options` block supports: + +* `token_query_parameter` - + (Optional) + The query parameter in which to find the token. + 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, and all following characters must be a dash, underscore, letter or digit. + Defaults to `edge-cache-token`. + +* `allowed_signature_algorithms` - + (Optional) + The allowed signature algorithms to use. + Defaults to using only ED25519. + You may specify up to 3 signature algorithms to use. + Each value may be one of `ED25519`, `HMAC_SHA_256`, and `HMAC_SHA1`. + +The `add_signatures` block supports: + +* `actions` - + (Required) + The actions to take to add signatures to responses. + Each value may be one of `GENERATE_COOKIE`, `GENERATE_TOKEN_HLS_COOKIELESS`, and `PROPAGATE_TOKEN_HLS_COOKIELESS`. + +* `keyset` - + (Optional) + The keyset to use for signature generation. + The following are both valid paths to an EdgeCacheKeyset resource: + * `projects/project/locations/global/edgeCacheKeysets/yourKeyset` + * `yourKeyset` + This must be specified when the GENERATE_COOKIE or GENERATE_TOKEN_HLS_COOKIELESS actions are specified. This field may not be specified otherwise. + +* `token_ttl` - + (Optional) + The duration the token is valid starting from the moment the token is first generated. + Defaults to `86400s` (1 day). + The TTL must be >= 0 and <= 604,800 seconds (1 week). + This field may only be specified when the GENERATE_COOKIE or GENERATE_TOKEN_HLS_COOKIELESS actions are specified. + A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s". + +* `token_query_parameter` - + (Optional) + The query parameter in which to put the generated token. + If not specified, defaults to `edge-cache-token`. + If specified, 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, and all following characters must be a dash, underscore, letter or digit. + This field may only be set when the GENERATE_TOKEN_HLS_COOKIELESS or PROPAGATE_TOKEN_HLS_COOKIELESS actions are specified. + +* `copied_parameters` - + (Optional) + The parameters to copy from the verified token to the generated token. + Only the following parameters may be copied: + * `PathGlobs` + * `paths` + * `acl` + * `URLPrefix` + * `IPRanges` + * `SessionID` + * `id` + * `Data` + * `data` + * `payload` + * `Headers` + You may specify up to 6 parameters to copy. A given parameter is be copied only if the parameter exists in the verified token. Parameter names are matched exactly as specified. The order of the parameters does not matter. Duplicates are not allowed. + This field may only be specified when the GENERATE_COOKIE or GENERATE_TOKEN_HLS_COOKIELESS actions are specified. + The `url_rewrite` block supports: * `path_prefix_rewrite` -