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

Add firewall support for Loadbalancers #911

Merged
merged 6 commits into from
Dec 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 25 additions & 1 deletion digitalocean/datasource_digitalocean_loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,32 @@ func dataSourceDigitalOceanLoadbalancer() *schema.Resource {
Computed: true,
Description: " Specifies the idle timeout for HTTPS connections on the load balancer.",
},

"project_id": {
Type: schema.TypeString,
Computed: true,
Description: "The ID of the project that the load balancer is associated with.",
},
"firewall": {
Type: schema.TypeList,
Computed: true,
Description: "the firewall rules for allowing/denying traffic to the load balancer",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"allow": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
Description: "the rules for ALLOWING traffic to the LB (strings in the form: 'ip:1.2.3.4' or 'cidr:1.2.0.0/16')",
},
"deny": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
Description: "the rules for DENYING traffic to the LB (strings in the form: 'ip:1.2.3.4' or 'cidr:1.2.0.0/16')",
},
},
},
},
},
}
}
Expand Down Expand Up @@ -324,6 +344,10 @@ func dataSourceDigitalOceanLoadbalancerRead(ctx context.Context, d *schema.Resou
return diag.Errorf("[DEBUG] Error setting Load Balancer forwarding_rule - error: %#v", err)
}

if err := d.Set("firewall", flattenLBFirewall(foundLoadbalancer.Firewall)); err != nil {
return diag.Errorf("[DEBUG] Error setting Load Balancer firewall - error: %#v", err)
}

return nil
}

Expand Down
38 changes: 38 additions & 0 deletions digitalocean/loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,30 @@ func expandStickySessions(config []interface{}) *godo.StickySessions {
return stickySession
}

func expandLBFirewall(config []interface{}) *godo.LBFirewall {
firewallConfig := config[0].(map[string]interface{})

firewall := &godo.LBFirewall{}

if v, ok := firewallConfig["allow"]; ok {
allows := make([]string, 0, len(v.([]interface{})))
for _, val := range v.([]interface{}) {
allows = append(allows, val.(string))
}
firewall.Allow = allows
}

if v, ok := firewallConfig["deny"]; ok {
denies := make([]string, 0, len(v.([]interface{})))
for _, val := range v.([]interface{}) {
denies = append(denies, val.(string))
}
firewall.Deny = denies
}

return firewall
}

func expandHealthCheck(config []interface{}) *godo.HealthCheck {
healthcheckConfig := config[0].(map[string]interface{})

Expand Down Expand Up @@ -187,6 +211,20 @@ func flattenStickySessions(session *godo.StickySessions) []map[string]interface{
return result
}

func flattenLBFirewall(firewall *godo.LBFirewall) []map[string]interface{} {
result := make([]map[string]interface{}, 0, 1)

if firewall != nil {
r := make(map[string]interface{})
r["allow"] = (*firewall).Allow
r["deny"] = (*firewall).Deny

result = append(result, r)
}

return result
}

func flattenForwardingRules(client *godo.Client, rules []godo.ForwardingRule) ([]map[string]interface{}, error) {
result := make([]map[string]interface{}, 0, 1)

Expand Down
31 changes: 31 additions & 0 deletions digitalocean/resource_digitalocean_loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,29 @@ func resourceDigitalOceanLoadBalancerV0() *schema.Resource {
Optional: true,
Computed: true,
},

"firewall": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"allow": {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
Description: "the rules for ALLOWING traffic to the LB (strings in the form: 'ip:1.2.3.4' or 'cidr:1.2.0.0/16')",
},
"deny": {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
Description: "the rules for DENYING traffic to the LB (strings in the form: 'ip:1.2.3.4' or 'cidr:1.2.0.0/16')",
},
},
},
},
},
}
}
Expand Down Expand Up @@ -459,6 +482,10 @@ func buildLoadBalancerRequest(client *godo.Client, d *schema.ResourceData) (*god
opts.StickySessions = expandStickySessions(v.([]interface{}))
}

if v, ok := d.GetOk("firewall"); ok {
opts.Firewall = expandLBFirewall(v.(*schema.Set).List())
}

if v, ok := d.GetOk("vpc_uuid"); ok {
opts.VPCUUID = v.(string)
}
Expand Down Expand Up @@ -556,6 +583,10 @@ func resourceDigitalOceanLoadbalancerRead(ctx context.Context, d *schema.Resourc
return diag.Errorf("[DEBUG] Error setting Load Balancer forwarding_rule - error: %#v", err)
}

if err := d.Set("firewall", flattenLBFirewall(loadbalancer.Firewall)); err != nil {
return diag.Errorf("[DEBUG] Error setting Load Balancer firewall - error: %#v", err)
}

return nil

}
Expand Down
62 changes: 62 additions & 0 deletions digitalocean/resource_digitalocean_loadbalancer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,37 @@ func TestAccDigitalOceanLoadbalancer_WithVPC(t *testing.T) {
})
}

func TestAccDigitalOceanLoadbalancer_Firewall(t *testing.T) {
var loadbalancer godo.LoadBalancer
lbName := randomTestName()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: testAccProviderFactories,
CheckDestroy: testAccCheckDigitalOceanLoadbalancerDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckDigitalOceanLoadbalancerConfig_Firewall(lbName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckDigitalOceanLoadbalancerExists("digitalocean_loadbalancer.foobar", &loadbalancer),
resource.TestCheckResourceAttr(
"digitalocean_loadbalancer.foobar", "name", lbName),
resource.TestCheckResourceAttr(
"digitalocean_loadbalancer.foobar", "firewall.#", "1"),
resource.TestCheckResourceAttr(
"digitalocean_loadbalancer.foobar", "firewall.0.deny.0", "cidr:1.2.0.0/16"),
resource.TestCheckResourceAttr(
"digitalocean_loadbalancer.foobar", "firewall.0.deny.1", "ip:2.3.4.5"),
resource.TestCheckResourceAttr(
"digitalocean_loadbalancer.foobar", "firewall.0.allow.0", "ip:1.2.3.4"),
resource.TestCheckResourceAttr(
"digitalocean_loadbalancer.foobar", "firewall.0.allow.1", "cidr:2.3.4.0/24"),
),
},
},
})
}

func testAccCheckDigitalOceanLoadbalancerDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*CombinedConfig).godoClient()

Expand Down Expand Up @@ -1122,3 +1153,34 @@ resource "digitalocean_loadbalancer" "foobar" {
droplet_ids = [digitalocean_droplet.foobar.id]
}`, randomTestName(), randomTestName(), name)
}

func testAccCheckDigitalOceanLoadbalancerConfig_Firewall(name string) string {
return fmt.Sprintf(`
resource "digitalocean_droplet" "foobar" {
name = "%s"
size = "s-1vcpu-1gb"
image = "ubuntu-22-04-x64"
region = "nyc3"
}

resource "digitalocean_loadbalancer" "foobar" {
name = "%s"
region = "nyc3"
size = "lb-small"

forwarding_rule {
entry_port = 80
entry_protocol = "http"

target_port = 80
target_protocol = "http"
}

firewall {
deny = ["cidr:1.2.0.0/16", "ip:2.3.4.5"]
allow = ["ip:1.2.3.4", "cidr:2.3.4.0/24"]
}

droplet_ids = [digitalocean_droplet.foobar.id]
}`, randomTestName(), name)
}
6 changes: 4 additions & 2 deletions digitalocean/resource_digitalocean_monitor_alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ func resourceDigitalOceanMonitorAlert() *schema.Resource {
godo.LoadBalancerConnectionUtilizationPercent,
godo.LoadBalancerDropletHealth,
godo.LoadBalancerTLSUtilizationPercent,
godo.LoadBalancerIncreaseInHTTPErrorRatePercentage,
godo.LoadBalancerIncreaseInHTTPErrorRateCount,
godo.LoadBalancerIncreaseInHTTPErrorRatePercentage4xx,
jrolheiser marked this conversation as resolved.
Show resolved Hide resolved
godo.LoadBalancerIncreaseInHTTPErrorRatePercentage5xx,
godo.LoadBalancerIncreaseInHTTPErrorRateCount4xx,
godo.LoadBalancerIncreaseInHTTPErrorRateCount5xx,
godo.LoadBalancerHighHttpResponseTime,
godo.LoadBalancerHighHttpResponseTime50P,
godo.LoadBalancerHighHttpResponseTime95P,
Expand Down
9 changes: 8 additions & 1 deletion docs/resources/loadbalancer.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ the backend service. Default value is `false`.
* `vpc_uuid` - (Optional) The ID of the VPC where the load balancer will be located.
* `droplet_ids` (Optional) - A list of the IDs of each droplet to be attached to the Load Balancer.
* `droplet_tag` (Optional) - The name of a Droplet tag corresponding to Droplets to be assigned to the Load Balancer.
* `firewall` (Optional) - A block containing rules for allowing/denying traffic to the Load Balancer. The `firewall` block is documented below. Only 1 firewall is allowed.

`forwarding_rule` supports the following:

Expand All @@ -137,11 +138,17 @@ the backend service. Default value is `false`.
* `protocol` - (Required) The protocol used for health checks sent to the backend Droplets. The possible values are `http`, `https` or `tcp`.
* `port` - (Optional) An integer representing the port on the backend Droplets on which the health check will attempt a connection.
* `path` - (Optional) The path on the backend Droplets to which the Load Balancer instance will send a request.
* `check_interval_seconds` - (Optional) The number of seconds between between two consecutive health checks. If not specified, the default value is `10`.
* `check_interval_seconds` - (Optional) The number of seconds between two consecutive health checks. If not specified, the default value is `10`.
* `response_timeout_seconds` - (Optional) The number of seconds the Load Balancer instance will wait for a response until marking a health check as failed. If not specified, the default value is `5`.
* `unhealthy_threshold` - (Optional) The number of times a health check must fail for a backend Droplet to be marked "unhealthy" and be removed from the pool. If not specified, the default value is `3`.
* `healthy_threshold` - (Optional) The number of times a health check must pass for a backend Droplet to be marked "healthy" and be re-added to the pool. If not specified, the default value is `5`.

`firewall` supports the following:

* `deny` - (Optional) A list of strings describing deny rules. Must be colon delimited strings of the form `{type}:{source}`
* `allow` - (Optional) A list of strings describing allow rules. Must be colon delimited strings of the form `{type}:{source}`
* Ex. `deny = ["cidr:1.2.0.0/16", "ip:2.3.4.5"]` or `allow = ["ip:1.2.3.4", "cidr:2.3.4.0/24"]`


## Attributes Reference

Expand Down
3 changes: 2 additions & 1 deletion docs/resources/monitor_alert.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ The following arguments are supported:
`v1/insights/droplet/private_outbound_bandwidth`, `v1/insights/droplet/private_inbound_bandwidth`,
`v1/insights/lbaas/avg_cpu_utilization_percent`, `v1/insights/lbaas/connection_utilization_percent`,
`v1/insights/lbaas/droplet_health`, `v1/insights/lbaas/tls_connections_per_second_utilization_percent`,
`v1/insights/lbaas/increase_in_http_error_rate_percentage`, `v1/insights/lbaas/increase_in_http_error_rate_count`,
`v1/insights/lbaas/increase_in_http_error_rate_percentage_5xx`, `v1/insights/lbaas/increase_in_http_error_rate_percentage_4xx`,
`v1/insights/lbaas/increase_in_http_error_rate_count_5xx`, `v1/insights/lbaas/increase_in_http_error_rate_count_4xx`,
`v1/insights/lbaas/high_http_request_response_time`, `v1/insights/lbaas/high_http_request_response_time_50p`,
`v1/insights/lbaas/high_http_request_response_time_95p`, `v1/insights/lbaas/high_http_request_response_time_99p`,
`v1/dbaas/alerts/load_15_alerts`, `v1/dbaas/alerts/cpu_alerts`, `v1/dbaas/alerts/memory_utilization_alerts`, or
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module github.com/digitalocean/terraform-provider-digitalocean

require (
github.com/aws/aws-sdk-go v1.42.18
github.com/digitalocean/godo v1.91.1
github.com/digitalocean/godo v1.92.0
github.com/hashicorp/awspolicyequivalence v1.5.0
github.com/hashicorp/go-uuid v1.0.2
github.com/hashicorp/go-version v1.3.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/digitalocean/godo v1.91.1 h1:1o30VOCu1aC6488qBd0SkQiBeAZ35RSTvLwCA1pQMhc=
github.com/digitalocean/godo v1.91.1/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA=
github.com/digitalocean/godo v1.92.0 h1:eK9DgdLcjozZbywjh/9k2NF++ttbcidASOoCGRu48yQ=
github.com/digitalocean/godo v1.92.0/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
Expand Down
8 changes: 8 additions & 0 deletions vendor/github.com/digitalocean/godo/CHANGELOG.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.