Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(tls_mutual_authentication): update activation after mtls creation #829

Merged
merged 13 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 102 additions & 6 deletions docs/resources/tls_mutual_authentication.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,124 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "fastly_tls_mutual_authentication Resource - terraform-provider-fastly"
subcategory: ""
layout: "fastly"
page_title: "Fastly: tls_mutual_authentication"
sidebar_current: "docs-fastly-resource-tls_mutual_authentication"
description: |-
Allows for client-to-server authentication using client-side X.509 authentication.
---

# fastly_tls_mutual_authentication (Resource)
# fastly_tls_mutual_authentication

The Mutual TLS API allows for client-to-server authentication using client-side X.509 authentication.

The main Mutual Authentication object represents the certificate bundle and other configurations which support Mutual TLS for your domains.

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.

## Example Usage

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

```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 = "www.${var.zone}"
}
backend {
address = "httpbin.org"
name = "httpbin"
}
force_destroy = true
}

resource "fastly_tls_subscription" "www" {
domains = [for domain in fastly_service_vcl.example.domain : domain.name if domain.name == "www.${var.zone}"]
certificate_authority = "lets-encrypt"
Integralist marked this conversation as resolved.
Show resolved Hide resolved
}

resource "dnsimple_zone_record" "www_acme_challenge" {
name = "_acme-challenge.www"
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
}

resource "fastly_tls_subscription_validation" "www" {
subscription_id = fastly_tls_subscription.www.id
depends_on = [dnsimple_zone_record.www_acme_challenge]
}

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

resource "dnsimple_zone_record" "www" {
name = "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
}

data "fastly_tls_activation" "www" {
domain = "www.example.com"
depends_on = [dnsimple_zone_record.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
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `activation_id` (String) The ID of your TLS Activation object
- `cert_bundle` (String) One or more certificates. Enter each individual certificate blob on a new line. Must be PEM-formatted.

### Optional

- `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) Comma-separated list of related objects to include (e.g. `tls_activations` will provide you with the TLS domain names that are related to your Mutual TLS authentication).
- `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.

### Read-Only
Expand Down
84 changes: 84 additions & 0 deletions examples/resources/tls_mutual_authentication_basic_usage.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
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 = "www.${var.zone}"
}
backend {
address = "httpbin.org"
name = "httpbin"
}
force_destroy = true
}

resource "fastly_tls_subscription" "www" {
domains = [for domain in fastly_service_vcl.example.domain : domain.name if domain.name == "www.${var.zone}"]
certificate_authority = "lets-encrypt"
Integralist marked this conversation as resolved.
Show resolved Hide resolved
}

resource "dnsimple_zone_record" "www_acme_challenge" {
name = "_acme-challenge.www"
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
}

resource "fastly_tls_subscription_validation" "www" {
subscription_id = fastly_tls_subscription.www.id
depends_on = [dnsimple_zone_record.www_acme_challenge]
}

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

resource "dnsimple_zone_record" "www" {
name = "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
}

data "fastly_tls_activation" "www" {
domain = "www.example.com"
depends_on = [dnsimple_zone_record.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
}
57 changes: 50 additions & 7 deletions fastly/resource_fastly_tls_mutual_authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ 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",
Required: true,
},
"cert_bundle": {
Type: schema.TypeString,
Description: "One or more certificates. Enter each individual certificate blob on a new line. Must be PEM-formatted.",
Expand All @@ -40,7 +45,7 @@ func resourceFastlyTLSMutualAuthentication() *schema.Resource {
},
"include": {
Type: schema.TypeString,
Description: "Comma-separated list of related objects to include (e.g. `tls_activations` will provide you with the TLS domain names that are related to your Mutual TLS authentication).",
Description: "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`).",
Optional: true,
},
"name": {
Expand All @@ -66,28 +71,45 @@ func resourceFastlyTLSMutualAuthentication() *schema.Resource {
}
}

// There are two steps to setting up mTLS:
//
// 1. POST /tls/mutual_authentications
// 2. PATCH /tls/activations/tls_activation_id
//
// The fastly_tls_activation data source can be used to acquire the Activation
// ID.
func resourceFastlyTLSMutualAuthenticationCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
conn := meta.(*APIClient).conn

input := &gofastly.CreateTLSMutualAuthenticationInput{
inputCreate := &gofastly.CreateTLSMutualAuthenticationInput{
CertBundle: d.Get("cert_bundle").(string),
}

if v, ok := d.GetOk("enforced"); ok {
input.Enforced = v.(bool)
inputCreate.Enforced = v.(bool)
}
if v, ok := d.GetOk("name"); ok {
input.Name = v.(string)
inputCreate.Name = v.(string)
}

log.Printf("[DEBUG] CREATE: TLS Mutual Authentication input: %#v", input)
log.Printf("[DEBUG] CREATE: TLS Mutual Authentication input: %#v", inputCreate)

output, err := conn.CreateTLSMutualAuthentication(input)
mTLS, err := conn.CreateTLSMutualAuthentication(inputCreate)
if err != nil {
return diag.FromErr(err)
}

d.SetId(output.ID)
d.SetId(mTLS.ID)

inputUpdate := &gofastly.UpdateTLSActivationInput{
ID: d.Get("activation_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)
}

return resourceFastlyTLSMutualAuthenticationRead(ctx, d, meta)
}
Expand Down Expand Up @@ -148,6 +170,14 @@ func resourceFastlyTLSMutualAuthenticationRead(_ context.Context, d *schema.Reso
return nil
}

// There are two steps to setting up mTLS:
//
// 1. POST /tls/mutual_authentications
// 2. PATCH /tls/activations/tls_activation_id
//
// Once mTLS is set up and the Activation object updated with the mTLS object
// ID, then for the resource's UPDATE operation we need to allow the user to
// change the Activation ID.
func resourceFastlyTLSMutualAuthenticationUpdate(_ context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
conn := meta.(*APIClient).conn

Expand All @@ -169,6 +199,19 @@ func resourceFastlyTLSMutualAuthenticationUpdate(_ context.Context, d *schema.Re
if err != nil {
return diag.FromErr(err)
}

if d.HasChange("activation_id") {
inputUpdate := &gofastly.UpdateTLSActivationInput{
ID: d.Get("activation_id").(string),
MutualAuthentication: &gofastly.TLSMutualAuthentication{ID: d.Id()},
}
log.Printf("[DEBUG] UPDATE: TLS Activation input: %#v", inputUpdate)
_, err = conn.UpdateTLSActivation(inputUpdate)
if err != nil {
return diag.FromErr(err)
}
}

return nil
}

Expand Down
Loading
Loading