Skip to content
This repository has been archived by the owner on May 21, 2023. It is now read-only.

Support mTLS for connection to AWX #190

Open
wants to merge 46 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
0682247
STORY-27847 - Support MTLS in AWX provider
jmcconnell26 Dec 8, 2022
8d3b8fa
STORY-27847 - Trigger change
jmcconnell26 Dec 9, 2022
837e6a3
Merge pull request #1 from sanity-io/feature/STORY-27847-SupportMTLSI…
jmcconnell26 Dec 9, 2022
55b8ab8
Trigger CI pipeline
jmcconnell26 Dec 9, 2022
2e97079
Update release drafter
jmcconnell26 Dec 9, 2022
7a029cd
STORY-27874 - Match tf provider naming convention
jmcconnell26 Dec 12, 2022
6cb8765
Add further error logging
jmcconnell26 Dec 12, 2022
ba31035
Adding further debug logging
jmcconnell26 Dec 12, 2022
d52bf46
Merge pull request #1 from jmcconnell26/AddFurtherLogging
jmcconnell26 Dec 12, 2022
a03882b
Remove debug logging
jmcconnell26 Dec 12, 2022
9a922ad
Revert release drafter changes
jmcconnell26 Dec 12, 2022
8122bd5
Write mTLS certificates to file, to allow use in other places
jmcconnell26 Dec 19, 2022
e07de12
Move cert writing location
jmcconnell26 Jan 30, 2023
597d26d
Search for credential by name
jmcconnell26 May 5, 2023
e9e4985
Remove unused entry
jmcconnell26 May 5, 2023
9d4db96
Avoid using both required and computed
jmcconnell26 May 5, 2023
7107baf
Explicitly set computed false
jmcconnell26 May 5, 2023
2b95af0
Build object differently
jmcconnell26 May 9, 2023
138fec2
Add execution environment data source
jmcconnell26 May 9, 2023
cf9e934
Provide execution environment id when creating job template
jmcconnell26 May 9, 2023
bc01fbb
Update deps
jmcconnell26 May 9, 2023
df4f37d
Add execution environment to reading + updating job_template
jmcconnell26 May 9, 2023
781dde3
Support importing existing job_templates
jmcconnell26 May 10, 2023
41f3d4a
Add ability to import job_template_credentials
jmcconnell26 May 10, 2023
0ca63e7
Implement read for job template credential
jmcconnell26 May 10, 2023
18ab17a
Fix read of job template credential
jmcconnell26 May 10, 2023
fcf3ec4
Empty commit to trigger build
jmcconnell26 May 10, 2023
a3561b2
Also set job template id
jmcconnell26 May 10, 2023
466641b
Fix resource job template credential id
jmcconnell26 May 11, 2023
a701aee
Use better id
jmcconnell26 May 11, 2023
a4f1857
Support id
jmcconnell26 May 11, 2023
db8a521
Read ID correctly
jmcconnell26 May 11, 2023
c93ca2b
Set credential id to current value on import
jmcconnell26 May 11, 2023
6da6fcf
Don't attempt to dissassociate credential which doesn't exist
jmcconnell26 May 11, 2023
8ebff25
Fix read / import of credential
jmcconnell26 May 11, 2023
9cd7c65
Add check to ensure mTLS certificate hasn't expired
jmcconnell26 May 12, 2023
4f786b2
Check correct value
jmcconnell26 May 12, 2023
a70cc8f
Update provider
jmcconnell26 May 12, 2023
fc1955c
SRE-465 - Add job slicing to provider
jmcconnell26 Oct 18, 2023
a0e7d24
SRE-465 - Update job_slice_count var
jmcconnell26 Oct 18, 2023
f5dbe5f
SRE-465 - Attempt to add custom diff for extra vars
jmcconnell26 Oct 19, 2023
bfe092d
SRE-465 - Update ignoreTrailingWhitespace function
jmcconnell26 Oct 19, 2023
5d2f483
SRE-465 - Invert boolean condition
jmcconnell26 Oct 19, 2023
84af3e1
SRE-465 - Attempt to clear diff for key
jmcconnell26 Oct 19, 2023
8449899
SRE-465 - fmt
jmcconnell26 Oct 19, 2023
55951c6
SRE-465 - Attempt to use DiffSuppressFunc
jmcconnell26 Oct 19, 2023
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
1 change: 0 additions & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,3 @@ release:
draft: false
changelog:
skip: true

74 changes: 45 additions & 29 deletions awx/data_source_credential.go
Original file line number Diff line number Diff line change
@@ -1,65 +1,81 @@
/*
Use this data source to query Credential by ID.

Example Usage
# Example Usage

```hcl
*TBD*
```

*/
package awx

import (
"context"
"strconv"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
awx "github.com/mrcrilly/goawx/client"
"strconv"
)

func dataSourceCredentialByID() *schema.Resource {
func dataSourceCredentialByName() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceCredentialByIDRead,
ReadContext: dataSourceCredentialsRead,
Schema: map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeInt,
Required: true,
Description: "Credential id",
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"username": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "The Username from searched id",
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
Computed: false,
},
"kind": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "The Kind from searched id",
Type: schema.TypeString,
Optional: true,
Computed: true,
},
},
}
}

func dataSourceCredentialByIDRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
func dataSourceCredentialsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
var diags diag.Diagnostics

client := m.(*awx.AWX)
id := d.Get("id").(int)
cred, err := client.CredentialsService.GetCredentialsByID(id, map[string]string{})
params := make(map[string]string)

if name, okName := d.GetOk("name"); okName {
params["name"] = name.(string)
}

if len(params) == 0 {
return buildDiagnosticsMessage(
"Get: Missing Parameters",
"Please use the selector: (name)")
}

credentials, _, err := client.CredentialsService.ListCredentials(map[string]string{})

if err != nil {
diags = append(diags, diag.Diagnostic{
Severity: diag.Error,
Summary: "Unable to fetch credential",
Detail: "The given credential ID is invalid or malformed",
})
return buildDiagnosticsMessage(
"Get: Fail to fetch Credential list",
"Fail to find the Credential list, got: %s",
err)
}

d.Set("username", cred.Inputs["username"])
d.Set("kind", cred.Kind)
d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
for _, credential := range credentials {
if credential.Name == params["name"] {
d.SetId(strconv.Itoa(credential.ID))
d.Set("name", credential.Name)
d.Set("kind", credential.Kind)
return diags
}
}

return diags
return buildDiagnosticsMessage(
"Credential not found",
"Could not find Credential with name: %s",
params["name"])
}
82 changes: 0 additions & 82 deletions awx/data_source_credentials.go

This file was deleted.

66 changes: 66 additions & 0 deletions awx/data_source_execution_environment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package awx

import (
"context"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
awx "github.com/mrcrilly/goawx/client"
"strconv"
)

func dataSourceExecutionEnvironmentByName() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceExecutionEnvironmentsRead,
Schema: map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
Computed: false,
},
},
}
}

func dataSourceExecutionEnvironmentsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
var diags diag.Diagnostics

client := m.(*awx.AWX)
params := make(map[string]string)

if name, okName := d.GetOk("name"); okName {
params["name"] = name.(string)
}

if len(params) == 0 {
return buildDiagnosticsMessage(
"Get: Missing Parameters",
"Please use the selector: (name)")
}

executionEnvironments, _, err := client.ExecutionEnvironmentsService.ListExecutionEnvironments(map[string]string{})

if err != nil {
return buildDiagnosticsMessage(
"Get: Fail to fetch Execution Environment list",
"Fail to find the Execution Environment list, got: %s",
err)
}

for _, executionEnvironment := range executionEnvironments {
if executionEnvironment.Name == params["name"] {
d.SetId(strconv.Itoa(executionEnvironment.ID))
d.Set("name", executionEnvironment.Name)
return diags
}
}

return buildDiagnosticsMessage(
"Execution Environment not found",
"Could not find Execution Environment with name: %s",
params["name"])
}
87 changes: 83 additions & 4 deletions awx/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ package awx
import (
"context"
"crypto/tls"
"net/http"

"crypto/x509"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
awx "github.com/mrcrilly/goawx/client"
"net/http"
"os"
"path/filepath"
)

func Provider() *schema.Provider {
Expand Down Expand Up @@ -35,6 +38,21 @@ func Provider() *schema.Provider {
Sensitive: true,
DefaultFunc: schema.EnvDefaultFunc("AWX_PASSWORD", "password"),
},
"client_cert": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "Client certificate to use for mTLS validation. Must be provided along with client-key and ca-cert for mTLS to be used.",
},
"client_key": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "Client key to use for mTLS validation. Must be provided along with client-cert and ca-cert for mTLS to be used",
},
"ca_cert": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "CA certificate to use for mTLS validation. Must be provided along with client-cert and client-key for mTLS to be used",
},
},
ResourcesMap: map[string]*schema.Resource{
"awx_credential_azure_key_vault": resourceCredentialAzureKeyVault(),
Expand All @@ -58,8 +76,8 @@ func Provider() *schema.Provider {
},
DataSourcesMap: map[string]*schema.Resource{
"awx_credential_azure_key_vault": dataSourceCredentialAzure(),
"awx_credential": dataSourceCredentialByID(),
"awx_credentials": dataSourceCredentials(),
"awx_credential": dataSourceCredentialByName(),
"awx_execution_environment": dataSourceExecutionEnvironmentByName(),
"awx_inventory_group": dataSourceInventoryGroup(),
"awx_inventory": dataSourceInventory(),
"awx_job_template": dataSourceJobTemplate(),
Expand All @@ -76,11 +94,33 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}
username := d.Get("username").(string)
password := d.Get("password").(string)

clientCertPEM, clientCertPEMExists := d.GetOk("client_cert")
clientKeyPEM, clientKeyPEMExists := d.GetOk("client_key")
caCertPEM, caCertPEMExists := d.GetOk("ca_cert")

client := http.DefaultClient
if d.Get("insecure").(bool) {
client.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
} else if clientCertPEMExists && clientKeyPEMExists && caCertPEMExists {
transportTlsConfig, err := generateMtlsConfig(
clientCertPEM.(string),
clientKeyPEM.(string),
caCertPEM.(string))

if err == nil {
client.Transport = &http.Transport{
TLSClientConfig: transportTlsConfig,
}
} else {
var diags diag.Diagnostics
return nil, append(diags, diag.Diagnostic{
Severity: diag.Error,
Summary: "Invalid mTLS config",
Detail: fmt.Sprintf("The provided mTLS config is invalid: %s", err),
})
}
}

// Warning or errors can be collected in a slice type
Expand All @@ -97,3 +137,42 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}

return c, diags
}

// This method also writes the mTLS certificates to disk - to allow them to be used for other calls to
// AWX
func generateMtlsConfig(clientCertPEM string, clientKeyPEM string, caCertPEM string) (*tls.Config, error) {
clientCertPEMBlock := []byte(clientCertPEM)
clientKeyPEMBlock := []byte(clientKeyPEM)
caCertPEMBlock := []byte(caCertPEM)

tempCertificatePath := "/wrk/terraform-provider-awx"
os.MkdirAll(tempCertificatePath, 0700)

writeByteArrayToFile(clientCertPEMBlock, filepath.Join(tempCertificatePath, "tls.crt"))
writeByteArrayToFile(clientKeyPEMBlock, filepath.Join(tempCertificatePath, "tls.key"))
writeByteArrayToFile(caCertPEMBlock, filepath.Join(tempCertificatePath, "ca.crt"))

cert, err := tls.X509KeyPair(clientCertPEMBlock, clientKeyPEMBlock)
if err != nil {
return nil, err
}

caCertPool, _ := x509.SystemCertPool()
if caCertPool == nil {
caCertPool = x509.NewCertPool()
}
caCertPool.AppendCertsFromPEM(caCertPEMBlock)

return &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
}, nil
}

func writeByteArrayToFile(byteArray []byte, filePath string) {
t, err := os.Create(filePath)
if err == nil {
t.Write(byteArray)
t.Close()
}
}
Loading