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

HVT-4407 implement ip allowlist in terraform provider #625

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d2f39d3
add release notes on the new field
aslamovamir Oct 2, 2023
aac11f2
docs changes
aslamovamir Oct 2, 2023
0ac9b12
add a new field for ip allowlist in data source and resource vault cl…
aslamovamir Oct 2, 2023
0c99438
reuse the validator functions from consul team
aslamovamir Oct 3, 2023
74da53e
change the name of the tests that call the helper validation functions
aslamovamir Oct 3, 2023
86dc34b
set the vault cluster resource data for ip allowlist field
aslamovamir Oct 3, 2023
996aa06
build ip allowlist and set in network config field of cluster in reso…
aslamovamir Oct 3, 2023
5acf65d
include the ip allowlist in resource update operation
aslamovamir Oct 4, 2023
b4db5b3
Merge branch 'main' into aslamovamir-HVT-4407-implement-ip-allowlist-…
aslamovamir Oct 5, 2023
a191463
completed the update operation to add ip allowlist
aslamovamir Oct 5, 2023
9ef5b3b
add tests
aslamovamir Oct 6, 2023
7a64a09
Merge branch 'main' into aslamovamir-HVT-4407-implement-ip-allowlist-…
aslamovamir Oct 6, 2023
8600315
Merge branch 'main' into aslamovamir-HVT-4407-implement-ip-allowlist-…
aslamovamir Oct 6, 2023
696e04c
update the changelog
aslamovamir Oct 6, 2023
be67627
style check
aslamovamir Oct 9, 2023
39c9ced
style check
aslamovamir Oct 9, 2023
907dd1c
fix const test func to properly set ip allowlist field
aslamovamir Oct 10, 2023
b5381d1
Merge branch 'main' into aslamovamir-HVT-4407-implement-ip-allowlist-…
aoripov Oct 12, 2023
29eaebd
bug fix - set ip allowlist for primary cluster as well
aslamovamir Oct 12, 2023
db4d2c3
Merge branch 'aslamovamir-HVT-4407-implement-ip-allowlist-in-terrafor…
aslamovamir Oct 12, 2023
bca48e4
set ip allowlist in a test function and config
aslamovamir Oct 12, 2023
c5fac60
Revert "set ip allowlist in a test function and config"
aslamovamir Oct 13, 2023
9bbe7c2
fix tests
aslamovamir Oct 13, 2023
baa7eb8
Merge branch 'main' into aslamovamir-HVT-4407-implement-ip-allowlist-…
aslamovamir Jan 11, 2024
18f9233
Merge branch 'main' into aslamovamir-HVT-4407-implement-ip-allowlist-…
aslamovamir Jan 17, 2024
7579415
address feedback
aslamovamir Jan 18, 2024
97f77ce
Merge branch 'main' into aslamovamir-HVT-4407-implement-ip-allowlist-…
aslamovamir Jan 18, 2024
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
3 changes: 3 additions & 0 deletions .changelog/625.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
Add `ip_allowlist` field for HCP Vault clusters to create or update allowed IP address ranges (CIDRs) for inbound traffic.
```
10 changes: 10 additions & 0 deletions docs/data-sources/vault_cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ If a project is not configured in the HCP Provider config block, the oldest proj
- `created_at` (String) The time that the Vault cluster was created.
- `hvn_id` (String) The ID of the HVN this HCP Vault cluster is associated to.
- `id` (String) The ID of this resource.
- `ip_allowlist` (List of Object) Allowed IPV4 address ranges (CIDRs) for inbound traffic. Each entry must be a unique CIDR. Maximum 50 CIDRS supported at this time. (see [below for nested schema](#nestedatt--ip_allowlist))
- `major_version_upgrade_config` (List of Object) (see [below for nested schema](#nestedatt--major_version_upgrade_config))
- `metrics_config` (Block List) The metrics configuration for export. (https://developer.hashicorp.com/vault/tutorials/cloud-monitoring/vault-metrics-guide#metrics-streaming-configuration) (see [below for nested schema](#nestedblock--metrics_config))
- `min_vault_version` (String) The minimum Vault version to use when creating the cluster. If not specified, it is defaulted to the version that is currently recommended by HCP.
Expand Down Expand Up @@ -97,6 +98,15 @@ Read-Only:
- `splunk_hecendpoint` (String) Splunk endpoint for streaming audit logs


<a id="nestedatt--ip_allowlist"></a>
### Nested Schema for `ip_allowlist`

Read-Only:

- `address` (String)
- `description` (String)


<a id="nestedatt--major_version_upgrade_config"></a>
### Nested Schema for `major_version_upgrade_config`

Expand Down
13 changes: 13 additions & 0 deletions docs/resources/vault_cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ resource "hcp_vault_cluster" "example" {
### Optional

- `audit_log_config` (Block List, Max: 1) The audit logs configuration for export. (https://developer.hashicorp.com/vault/tutorials/cloud-monitoring/vault-metrics-guide#metrics-streaming-configuration) (see [below for nested schema](#nestedblock--audit_log_config))
- `ip_allowlist` (Block List, Max: 50) Allowed IPV4 address ranges (CIDRs) for inbound traffic. Each entry must be a unique CIDR. Maximum 50 CIDRS supported at this time. (see [below for nested schema](#nestedblock--ip_allowlist))
- `major_version_upgrade_config` (Block List, Max: 1) The Major Version Upgrade configuration. (see [below for nested schema](#nestedblock--major_version_upgrade_config))
- `metrics_config` (Block List, Max: 1) The metrics configuration for export. (https://developer.hashicorp.com/vault/tutorials/cloud-monitoring/vault-metrics-guide#metrics-streaming-configuration) (see [below for nested schema](#nestedblock--metrics_config))
- `min_vault_version` (String) The minimum Vault version to use when creating the cluster. If not specified, it is defaulted to the version that is currently recommended by HCP.
Expand Down Expand Up @@ -117,6 +118,18 @@ Read-Only:
- `elasticsearch_dataset` (String) ElasticSearch dataset for streaming audit logs


<a id="nestedblock--ip_allowlist"></a>
### Nested Schema for `ip_allowlist`

Required:

- `address` (String) IP address range in CIDR notation.

Optional:

- `description` (String) Description to help identify source (maximum 255 chars).


<a id="nestedblock--major_version_upgrade_config"></a>
### Nested Schema for `major_version_upgrade_config`

Expand Down
9 changes: 7 additions & 2 deletions internal/clients/vault_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func UpdateVaultClusterConfig(
ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, clusterID string,
tier *string, publicIpsEnabled *bool, httpProxyOption *vaultmodels.HashicorpCloudVault20201125HTTPProxyOption,
metrics *vaultmodels.HashicorpCloudVault20201125ObservabilityConfig, auditLog *vaultmodels.HashicorpCloudVault20201125ObservabilityConfig,
) (*vaultmodels.HashicorpCloudVault20201125UpdateResponse, error) {
ipAllowlist []*vaultmodels.HashicorpCloudVault20201125CidrRange) (*vaultmodels.HashicorpCloudVault20201125UpdateResponse, error) {

config := &vaultmodels.HashicorpCloudVault20201125InputClusterConfig{}
updateMaskPaths := []string{}
Expand All @@ -181,7 +181,7 @@ func UpdateVaultClusterConfig(
config.Tier = &tier
updateMaskPaths = append(updateMaskPaths, "config.tier")
}
if publicIpsEnabled != nil || httpProxyOption != nil {
if publicIpsEnabled != nil || httpProxyOption != nil || ipAllowlist != nil {
config.NetworkConfig = &vaultmodels.HashicorpCloudVault20201125InputNetworkConfig{}

if publicIpsEnabled != nil {
Expand All @@ -193,6 +193,11 @@ func UpdateVaultClusterConfig(
config.NetworkConfig.HTTPProxyOption = httpProxyOption
updateMaskPaths = append(updateMaskPaths, "config.network_config.http_proxy_option")
}

if ipAllowlist != nil {
config.NetworkConfig.IPAllowlist = ipAllowlist
updateMaskPaths = append(updateMaskPaths, "config.network_config.ip_allowlist")
}
}
if metrics != nil {
config.MetricsConfig = metrics
Expand Down
19 changes: 19 additions & 0 deletions internal/providersdkv2/data_source_vault_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,25 @@ If a project is not configured in the HCP Provider config block, the oldest proj
Type: schema.TypeString,
Computed: true,
},
"ip_allowlist": {
Description: "Allowed IPV4 address ranges (CIDRs) for inbound traffic. Each entry must be a unique CIDR. Maximum 50 CIDRS supported at this time.",
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"address": {
Description: "IP address range in CIDR notation.",
Type: schema.TypeString,
Computed: true,
},
"description": {
Description: "Description to help identify source (maximum 255 chars).",
Type: schema.TypeString,
Computed: true,
},
},
},
},
"min_vault_version": {
Description: "The minimum Vault version to use when creating the cluster. If not specified, it is defaulted to the version that is currently recommended by HCP.",
Type: schema.TypeString,
Expand Down
12 changes: 6 additions & 6 deletions internal/providersdkv2/resource_consul_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,13 +226,13 @@ If a project is not configured in the HCP Provider config block, the oldest proj
Description: "IP address range in CIDR notation.",
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: validateConsulClusterCIDR,
ValidateDiagFunc: validateCIDRRange,
},
"description": {
Description: "Description to help identify source (maximum 255 chars).",
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: validateConsulClusterCIDRDescription,
ValidateDiagFunc: validateCIDRRangeDescription,
},
},
},
Expand Down Expand Up @@ -367,7 +367,7 @@ func resourceConsulClusterCreate(ctx context.Context, d *schema.ResourceData, me

// Convert ip_allowlist to consul model.
cidrs := d.Get("ip_allowlist").([]interface{})
ipAllowlist, err := buildIPAllowlist(cidrs)
ipAllowlist, err := buildIPAllowlistConsulCluster(cidrs)
if err != nil {
return diag.Errorf("Invalid ip_allowlist for Consul cluster (%s): %v", clusterID, err)
}
Expand Down Expand Up @@ -745,7 +745,7 @@ func resourceConsulClusterUpdate(ctx context.Context, d *schema.ResourceData, me

if ipAllowlistChanged {
cidrs := d.Get("ip_allowlist").([]interface{})
ipAllowlist, err := buildIPAllowlist(cidrs)
ipAllowlist, err := buildIPAllowlistConsulCluster(cidrs)
if err != nil {
return diag.Errorf("Invalid ip_allowlist for Consul cluster (%s): %v", clusterID, err)
}
Expand Down Expand Up @@ -868,8 +868,8 @@ func resourceConsulClusterImport(ctx context.Context, d *schema.ResourceData, me
return []*schema.ResourceData{d}, nil
}

// buildIPAllowlist returns a consul model for the IP allowlist.
func buildIPAllowlist(cidrs []interface{}) ([]*consulmodels.HashicorpCloudConsul20210204CidrRange, error) {
// buildIPAllowlistConsulCluster returns a consul model for the IP allowlist.
func buildIPAllowlistConsulCluster(cidrs []interface{}) ([]*consulmodels.HashicorpCloudConsul20210204CidrRange, error) {
ipAllowList := make([]*consulmodels.HashicorpCloudConsul20210204CidrRange, len(cidrs))

for i, cidr := range cidrs {
Expand Down
89 changes: 85 additions & 4 deletions internal/providersdkv2/resource_vault_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,28 @@ If a project is not configured in the HCP Provider config block, the oldest proj
return strings.EqualFold(old, new)
},
},
"ip_allowlist": {
Description: "Allowed IPV4 address ranges (CIDRs) for inbound traffic. Each entry must be a unique CIDR. Maximum 50 CIDRS supported at this time.",
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"address": {
Description: "IP address range in CIDR notation.",
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: validateCIDRRange,
},
"description": {
Description: "Description to help identify source (maximum 255 chars).",
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: validateCIDRRangeDescription,
},
},
},
MaxItems: 50,
},
"min_vault_version": {
Description: "The minimum Vault version to use when creating the cluster. If not specified, it is defaulted to the version that is currently recommended by HCP.",
Type: schema.TypeString,
Expand Down Expand Up @@ -618,6 +640,12 @@ func resourceVaultClusterCreate(ctx context.Context, d *schema.ResourceData, met
httpProxyOption = vaultmodels.HashicorpCloudVault20201125HTTPProxyOption(strings.ToUpper(proxyEndpoint.(string))).Pointer()
}

cidrs := d.Get("ip_allowlist").([]interface{})
ipAllowlist, err := buildIPAllowlistVaultCluster(cidrs)
if err != nil {
return diag.Errorf("Invalid ip_allowlist for Vault cluster (%s): %v", clusterID, err)
}

// If the cluster has a primary_link, make sure the link is valid
diagErr, primaryClusterModel := validatePerformanceReplicationChecksAndReturnPrimaryIfAny(ctx, client, d)
if diagErr != nil {
Expand Down Expand Up @@ -657,6 +685,7 @@ func resourceVaultClusterCreate(ctx context.Context, d *schema.ResourceData, met
NetworkID: hvn.ID,
PublicIpsEnabled: publicEndpoint,
HTTPProxyOption: httpProxyOption,
IPAllowlist: ipAllowlist,
},
},
ID: clusterID,
Expand Down Expand Up @@ -698,6 +727,7 @@ func resourceVaultClusterCreate(ctx context.Context, d *schema.ResourceData, met
NetworkID: hvn.ID,
PublicIpsEnabled: publicEndpoint,
HTTPProxyOption: httpProxyOption,
IPAllowlist: ipAllowlist,
},
},
ID: clusterID,
Expand Down Expand Up @@ -844,7 +874,7 @@ func resourceVaultClusterUpdate(ctx context.Context, d *schema.ResourceData, met
}

// Confirm at least one modifiable field has changed
if !d.HasChanges("tier", "public_endpoint", "proxy_endpoint", "paths_filter", "metrics_config", "audit_log_config", "major_version_upgrade_config") {
if !d.HasChanges("tier", "public_endpoint", "proxy_endpoint", "ip_allowlist", "paths_filter", "metrics_config", "audit_log_config", "major_version_upgrade_config") {
return nil
}

Expand All @@ -854,7 +884,7 @@ func resourceVaultClusterUpdate(ctx context.Context, d *schema.ResourceData, met
return diagErr
}

if d.HasChange("tier") || d.HasChange("public_endpoint") || d.HasChange("proxy_endpoint") || d.HasChange("metrics_config") || d.HasChange("audit_log_config") {
if d.HasChange("tier") || d.HasChange("public_endpoint") || d.HasChange("proxy_endpoint") || d.HasChange("ip_allowlist") || d.HasChange("metrics_config") || d.HasChange("audit_log_config") {
diagErr := updateVaultClusterConfig(ctx, client, d, cluster, clusterID)
if diagErr != nil {
return diagErr
Expand Down Expand Up @@ -962,6 +992,10 @@ func updateVaultClusterConfig(ctx context.Context, client *clients.Client, d *sc
destTier := getClusterTier(d)
publicIpsEnabled := getPublicIpsEnabled(d)
httpProxyOption := getHTTPProxyOption(d)
ipAllowlist, diagErr := getIPAllowlist(d, clusterID)
if diagErr != nil {
return diagErr
}

clusterSharedLoc := &sharedmodels.HashicorpCloudLocationLocation{
OrganizationID: cluster.Location.OrganizationID,
Expand Down Expand Up @@ -993,7 +1027,7 @@ func updateVaultClusterConfig(ctx context.Context, client *clients.Client, d *sc
// Because of (b), if the cluster is a secondary, issue the actual API request to the primary.
isSecondary = true
if d.HasChange("metrics_config") || d.HasChange("audit_log_config") {
updateResp, err := clients.UpdateVaultClusterConfig(ctx, client, clusterSharedLoc, cluster.ID, destTier, publicIpsEnabled, httpProxyOption, metricsConfig, auditConfig)
updateResp, err := clients.UpdateVaultClusterConfig(ctx, client, clusterSharedLoc, cluster.ID, destTier, publicIpsEnabled, httpProxyOption, metricsConfig, auditConfig, ipAllowlist)
if err != nil {
return diag.Errorf("error updating Vault cluster (%s): %v", clusterID, err)
}
Expand All @@ -1019,7 +1053,7 @@ func updateVaultClusterConfig(ctx context.Context, client *clients.Client, d *sc
auditConfig = nil
}
// Invoke update endpoint.
updateResp, err := clients.UpdateVaultClusterConfig(ctx, client, clusterSharedLoc, cluster.ID, destTier, publicIpsEnabled, httpProxyOption, metricsConfig, auditConfig)
updateResp, err := clients.UpdateVaultClusterConfig(ctx, client, clusterSharedLoc, cluster.ID, destTier, publicIpsEnabled, httpProxyOption, metricsConfig, auditConfig, ipAllowlist)
if err != nil {
return diag.Errorf("error updating Vault cluster (%s): %v", clusterID, err)
}
Expand Down Expand Up @@ -1058,6 +1092,19 @@ func getHTTPProxyOption(d *schema.ResourceData) *vaultmodels.HashicorpCloudVault
return nil
}

func getIPAllowlist(d *schema.ResourceData, clusterID string) ([]*vaultmodels.HashicorpCloudVault20201125CidrRange, diag.Diagnostics) {
// If we don't change the ip_allowlist_endpoint, return nil so we don't pass ip_allowlist to the update.
if d.HasChange("ip_allowlist") {
cidrs := d.Get("ip_allowlist").([]interface{})
ipAllowlist, err := buildIPAllowlistVaultCluster(cidrs)
if err != nil {
return nil, diag.Errorf("Invalid ip_allowlist for Vault cluster (%s): %v", clusterID, err)
}
return ipAllowlist, nil
}
return nil, nil
}

// setVaultClusterResourceData sets the KV pairs of the Vault cluster resource schema.
func setVaultClusterResourceData(d *schema.ResourceData, cluster *vaultmodels.HashicorpCloudVault20201125Cluster) error {

Expand Down Expand Up @@ -1149,6 +1196,20 @@ func setVaultClusterResourceData(d *schema.ResourceData, cluster *vaultmodels.Ha
return err
}

if cluster.Config.NetworkConfig != nil {
ipAllowlist := make([]map[string]interface{}, len(cluster.Config.NetworkConfig.IPAllowlist))
for i, cidrRange := range cluster.Config.NetworkConfig.IPAllowlist {
cidr := map[string]interface{}{
"description": cidrRange.Description,
"address": cidrRange.Address,
}
ipAllowlist[i] = cidr
}
if err := d.Set("ip_allowlist", ipAllowlist); err != nil {
return err
}
}

clusterSharedLoc := &sharedmodels.HashicorpCloudLocationLocation{
OrganizationID: cluster.Location.OrganizationID,
ProjectID: cluster.Location.ProjectID,
Expand Down Expand Up @@ -1778,3 +1839,23 @@ func getPathStrings(pathFilter interface{}) []string {
func printPlusScalingWarningMsg() {
log.Printf("[WARN] When scaling Plus-tier Vault clusters, be sure to keep the size of all clusters in a replication group in sync")
}

// buildIPAllowlistVaultCluster returns a vault model for the IP allowlist.
func buildIPAllowlistVaultCluster(cidrs []interface{}) ([]*vaultmodels.HashicorpCloudVault20201125CidrRange, error) {
ipAllowList := make([]*vaultmodels.HashicorpCloudVault20201125CidrRange, len(cidrs))

for i, cidr := range cidrs {
cidrMap := cidr.(map[string]interface{})
address := cidrMap["address"].(string)
description := cidrMap["description"].(string)

cidrRange := &vaultmodels.HashicorpCloudVault20201125CidrRange{
Address: address,
Description: description,
}

ipAllowList[i] = cidrRange
}

return ipAllowList, nil
}
16 changes: 16 additions & 0 deletions internal/providersdkv2/resource_vault_cluster_const_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"testing"
"text/template"

vaultmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-service/stable/2020-11-25/models"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -50,6 +51,7 @@ resource "hcp_vault_cluster" "test" {
major_version_upgrade_config {
upgrade_type = "MANUAL"
}
{{ .IPAllowlist }}
}
`

Expand All @@ -67,6 +69,7 @@ resource "hcp_vault_cluster" "test" {
maintenance_window_day = "WEDNESDAY"
maintenance_window_time = "WINDOW_12AM_4AM"
}
{{ .IPAllowlist }}
}
`

Expand Down Expand Up @@ -102,6 +105,7 @@ resource "hcp_vault_cluster_admin_token" "test" {
Tier string
PublicEndpoint string
ProxyEndpoint string
IPAllowlist string
}{
ClusterID: in.VaultClusterName,
HvnID: in.HvnName,
Expand All @@ -110,7 +114,19 @@ resource "hcp_vault_cluster_admin_token" "test" {
Tier: tier,
PublicEndpoint: in.PublicEndpoint,
ProxyEndpoint: in.ProxyEndpoint,
IPAllowlist: convertIPAllowlistToTFBlocks(in.IPAllowlist),
})
require.NoError(t, err)
return tfResources.String()
}

func convertIPAllowlistToTFBlocks(ipAlowlist []*vaultmodels.HashicorpCloudVault20201125CidrRange) string {
out := ""
for _, entry := range ipAlowlist {
out += fmt.Sprintf(`ip_allowlist {
address = "%s"
description = "%s"
}`, entry.Address, entry.Description)
}
return out
}
Loading