Skip to content

Commit

Permalink
fix(tls_mutual_authentication): support multiple TLS Activations
Browse files Browse the repository at this point in the history
  • Loading branch information
Integralist committed Apr 10, 2024
1 parent c2b92f5 commit a2706cc
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 39 deletions.
122 changes: 114 additions & 8 deletions docs/resources/tls_mutual_authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ The main Mutual Authentication object represents the certificate bundle and othe

Mutual TLS can be added to existing TLS activations to allow for client-to-server authentication. In order to use mutual TLS, you must already have active server-side TLS using either custom certificates or an enabled Fastly-managed subscription.

The example below demonstrates how to use Mutual Authentication along with a TLS Subscription. Refer to the `fastly_tls_subscription` resource documentation for a deeper explanation of that code.
The examples below demonstrate how to use Mutual Authentication along with a TLS Subscription. Refer to the `fastly_tls_subscription` resource documentation for a deeper explanation of that code.

## Example Usage
## Example: Single Activation

The following example sets up a TLS Subscription for `www.example.com` and then adds Mutual Authentication.

Expand Down Expand Up @@ -74,7 +74,7 @@ resource "dnsimple_zone_record" "www_acme_challenge" {
ttl = "60"
type = "CNAME"
value = one([for obj in fastly_tls_subscription.www.managed_dns_challenges : obj.record_value if obj.record_name == "_acme-challenge.www.${var.zone}"])
zone_name = local.zone
zone_name = var.zone
}
resource "fastly_tls_subscription_validation" "www" {
Expand All @@ -92,7 +92,7 @@ resource "dnsimple_zone_record" "www" {
ttl = "60"
type = "CNAME"
value = one([for record in data.fastly_tls_configuration.default.dns_records : record.record_value if record.record_type == "CNAME"])
zone_name = local.zone
zone_name = var.zone
}
data "fastly_tls_activation" "www" {
Expand All @@ -101,9 +101,115 @@ data "fastly_tls_activation" "www" {
}
resource "fastly_tls_mutual_authentication" "www" {
activation_id = data.fastly_tls_activation.www.id
cert_bundle = "-----BEGIN CERTIFICATE-----\n<REDACTED>\n-----END CERTIFICATE-----"
enforced = true
activation_ids = [data.fastly_tls_activation.www.id]
cert_bundle = "-----BEGIN CERTIFICATE-----\n<REDACTED>\n-----END CERTIFICATE-----"
enforced = true
}
```

## Example: Multiple Activations

The following example sets up a TLS Subscription for `foo.example.com` and `bar.example.com` and then adds Mutual Authentication to each TLS Activation.

```terraform
terraform {
required_providers {
dnsimple = {
source = "dnsimple/dnsimple"
version = "1.5.0"
}
fastly = {
source = "fastly/fastly"
version = "5.7.2"
}
}
}
variable "dnsimple_token" {
type = string
}
variable "dnsimple_account" {
type = string
}
provider "dnsimple" {
account = var.dnsimple_account
token = var.dnsimple_token
}
variable "zone" {
type = string
default = "example.com"
}
resource "fastly_service_vcl" "example" {
name = "example"
domain {
name = "foo.${var.zone}"
}
domain {
name = "bar.${var.zone}"
}
backend {
address = "httpbin.org"
name = "httpbin"
}
force_destroy = true
}
resource "fastly_tls_subscription" "example" {
domains = [for domain in fastly_service_vcl.example.domain : domain.name]
certificate_authority = "lets-encrypt"
}
resource "dnsimple_zone_record" "example_acme_challenge" {
for_each = {
for domain in fastly_tls_subscription.example.domains : domain => one([
for obj in fastly_tls_subscription.example.managed_dns_challenges : obj if obj.record_name == "_acme-challenge.${domain}"
])
}
name = each.value.record_name
ttl = "60"
type = each.value.record_type
value = each.value.record_value
zone_name = var.zone
}
resource "fastly_tls_subscription_validation" "example" {
subscription_id = fastly_tls_subscription.example.id
depends_on = [dnsimple_zone_record.example_acme_challenge]
}
data "fastly_tls_configuration" "default" {
default = true
depends_on = [fastly_tls_subscription_validation.example]
}
resource "dnsimple_zone_record" "foo" {
name = "foo"
ttl = "60"
type = "CNAME"
value = one([for record in data.fastly_tls_configuration.default.dns_records : record.record_value if record.record_type == "CNAME"])
zone_name = var.zone
}
resource "dnsimple_zone_record" "bar" {
name = "bar"
ttl = "60"
type = "CNAME"
value = one([for record in data.fastly_tls_configuration.default.dns_records : record.record_value if record.record_type == "CNAME"])
zone_name = var.zone
}
data "fastly_tls_activation_ids" "example" {
certificate_id = fastly_tls_subscription.example.certificate_id
}
resource "fastly_tls_mutual_authentication" "fastly_dev" {
activation_ids = data.fastly_tls_activation_ids.example.ids
cert_bundle = "-----BEGIN CERTIFICATE-----\n<REDACTED>\n-----END CERTIFICATE-----"
enforced = true
}
```

Expand All @@ -116,7 +222,7 @@ resource "fastly_tls_mutual_authentication" "www" {

### Optional

- `activation_id` (String) The ID of your TLS Activation object
- `activation_ids` (Set of String) List of TLS Activation IDs
- `enforced` (Boolean) Determines whether Mutual TLS will fail closed (enforced) or fail open. A true value will require a successful Mutual TLS handshake for the connection to continue and will fail closed if unsuccessful. A false value will fail open and allow the connection to proceed (if this attribute is not set we default to `false`).
- `include` (String) A comma-separated list used by the Terraform provider during a state refresh to return more data related to your mutual authentication from the Fastly API (permitted values: `tls_activations`).
- `name` (String) A custom name for your mutual authentication. If name is not supplied we will auto-generate one.
Expand Down
10 changes: 5 additions & 5 deletions examples/resources/tls_mutual_authentication_basic_usage.tf
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ resource "dnsimple_zone_record" "www_acme_challenge" {
ttl = "60"
type = "CNAME"
value = one([for obj in fastly_tls_subscription.www.managed_dns_challenges : obj.record_value if obj.record_name == "_acme-challenge.www.${var.zone}"])
zone_name = local.zone
zone_name = var.zone
}

resource "fastly_tls_subscription_validation" "www" {
Expand All @@ -69,7 +69,7 @@ resource "dnsimple_zone_record" "www" {
ttl = "60"
type = "CNAME"
value = one([for record in data.fastly_tls_configuration.default.dns_records : record.record_value if record.record_type == "CNAME"])
zone_name = local.zone
zone_name = var.zone
}

data "fastly_tls_activation" "www" {
Expand All @@ -78,7 +78,7 @@ data "fastly_tls_activation" "www" {
}

resource "fastly_tls_mutual_authentication" "www" {
activation_id = data.fastly_tls_activation.www.id
cert_bundle = "-----BEGIN CERTIFICATE-----\n<REDACTED>\n-----END CERTIFICATE-----"
enforced = true
activation_ids = [data.fastly_tls_activation.www.id]
cert_bundle = "-----BEGIN CERTIFICATE-----\n<REDACTED>\n-----END CERTIFICATE-----"
enforced = true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
terraform {
required_providers {
dnsimple = {
source = "dnsimple/dnsimple"
version = "1.5.0"
}
fastly = {
source = "fastly/fastly"
version = "5.7.2"
}
}
}

variable "dnsimple_token" {
type = string
}

variable "dnsimple_account" {
type = string
}

provider "dnsimple" {
account = var.dnsimple_account
token = var.dnsimple_token
}

variable "zone" {
type = string
default = "example.com"
}

resource "fastly_service_vcl" "example" {
name = "example"
domain {
name = "foo.${var.zone}"
}
domain {
name = "bar.${var.zone}"
}
backend {
address = "httpbin.org"
name = "httpbin"
}
force_destroy = true
}

resource "fastly_tls_subscription" "example" {
domains = [for domain in fastly_service_vcl.example.domain : domain.name]
certificate_authority = "lets-encrypt"
}

resource "dnsimple_zone_record" "example_acme_challenge" {
for_each = {
for domain in fastly_tls_subscription.example.domains : domain => one([
for obj in fastly_tls_subscription.example.managed_dns_challenges : obj if obj.record_name == "_acme-challenge.${domain}"
])
}
name = each.value.record_name
ttl = "60"
type = each.value.record_type
value = each.value.record_value
zone_name = var.zone
}

resource "fastly_tls_subscription_validation" "example" {
subscription_id = fastly_tls_subscription.example.id
depends_on = [dnsimple_zone_record.example_acme_challenge]
}

data "fastly_tls_configuration" "default" {
default = true
depends_on = [fastly_tls_subscription_validation.example]
}

resource "dnsimple_zone_record" "foo" {
name = "foo"
ttl = "60"
type = "CNAME"
value = one([for record in data.fastly_tls_configuration.default.dns_records : record.record_value if record.record_type == "CNAME"])
zone_name = var.zone
}

resource "dnsimple_zone_record" "bar" {
name = "bar"
ttl = "60"
type = "CNAME"
value = one([for record in data.fastly_tls_configuration.default.dns_records : record.record_value if record.record_type == "CNAME"])
zone_name = var.zone
}

data "fastly_tls_activation_ids" "example" {
certificate_id = fastly_tls_subscription.example.certificate_id
}

resource "fastly_tls_mutual_authentication" "example" {
activation_ids = data.fastly_tls_activation_ids.example.ids
cert_bundle = "-----BEGIN CERTIFICATE-----\n<REDACTED>\n-----END CERTIFICATE-----"
enforced = true
}
74 changes: 51 additions & 23 deletions fastly/resource_fastly_tls_mutual_authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ func resourceFastlyTLSMutualAuthentication() *schema.Resource {
StateContext: schema.ImportStatePassthroughContext,
},
Schema: map[string]*schema.Schema{
"activation_id": {
Type: schema.TypeString,
Description: "The ID of your TLS Activation object",
"activation_ids": {
Type: schema.TypeSet,
Description: "List of TLS Activation IDs",
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"cert_bundle": {
Type: schema.TypeString,
Expand Down Expand Up @@ -101,15 +104,19 @@ func resourceFastlyTLSMutualAuthenticationCreate(ctx context.Context, d *schema.

d.SetId(mTLS.ID)

if v, ok := d.GetOk("activation_id"); ok {
inputUpdate := &gofastly.UpdateTLSActivationInput{
ID: v.(string),
MutualAuthentication: &gofastly.TLSMutualAuthentication{ID: mTLS.ID},
}
log.Printf("[DEBUG] CREATE: Update TLS Activation input: %#v", inputUpdate)
_, err = conn.UpdateTLSActivation(inputUpdate)
if err != nil {
return diag.FromErr(err)
if v, ok := d.GetOk("activation_ids"); ok {
activationIDs := v.(*schema.Set).List()
for _, id := range activationIDs {
inputUpdate := &gofastly.UpdateTLSActivationInput{
ID: id.(string),
MutualAuthentication: &gofastly.TLSMutualAuthentication{ID: mTLS.ID},
}
log.Printf("[DEBUG] CREATE: Update TLS Activation input: %#v", inputUpdate)
_, err = conn.UpdateTLSActivation(inputUpdate)
if err != nil {
return diag.FromErr(err)
}

}
}

Expand Down Expand Up @@ -202,15 +209,35 @@ func resourceFastlyTLSMutualAuthenticationUpdate(_ context.Context, d *schema.Re
return diag.FromErr(err)
}

if d.HasChange("activation_id") {
inputUpdate := &gofastly.UpdateTLSActivationInput{
ID: d.Get("activation_id").(string),
MutualAuthentication: &gofastly.TLSMutualAuthentication{ID: d.Id()},
if v, ok := d.GetOk("activation_ids"); ok {
activationIDs := v.(*schema.Set).List()
for _, id := range activationIDs {
inputUpdate := &gofastly.UpdateTLSActivationInput{
ID: id.(string),
MutualAuthentication: &gofastly.TLSMutualAuthentication{ID: d.Id()},
}
log.Printf("[DEBUG] CREATE: Update TLS Activation input: %#v", inputUpdate)
_, err = conn.UpdateTLSActivation(inputUpdate)
if err != nil {
return diag.FromErr(err)
}

}
log.Printf("[DEBUG] UPDATE: TLS Activation input: %#v", inputUpdate)
_, err = conn.UpdateTLSActivation(inputUpdate)
if err != nil {
return diag.FromErr(err)
}

if d.HasChange("activation_ids") {
activationIDs := d.Get("activation_ids").(*schema.Set).List()
for _, id := range activationIDs {
inputUpdate := &gofastly.UpdateTLSActivationInput{
ID: id.(string),
MutualAuthentication: &gofastly.TLSMutualAuthentication{ID: d.Id()},
}
log.Printf("[DEBUG] UPDATE: Update TLS Activation input: %#v", inputUpdate)
_, err = conn.UpdateTLSActivation(inputUpdate)
if err != nil {
return diag.FromErr(err)
}

}
}

Expand All @@ -222,10 +249,11 @@ func resourceFastlyTLSMutualAuthenticationDelete(_ context.Context, d *schema.Re

// IMPORTANT: You can't delete mTLS with active domains.
// You must first disable the active domains.
// To do that, you can set "" for the mTLS ID.
if d.Get("activation_id").(string) != "" {
// To do that, you can set "" for the mTLS ID on each TLS Activation.
activationIDs := d.Get("activation_ids").(*schema.Set).List()
for _, id := range activationIDs {
input := &gofastly.UpdateTLSActivationInput{
ID: d.Get("activation_id").(string),
ID: id.(string),
MutualAuthentication: &gofastly.TLSMutualAuthentication{ID: ""},
}
log.Printf("[DEBUG] DELETE: TLS Activation input: %#v", input)
Expand Down
Loading

0 comments on commit a2706cc

Please sign in to comment.