resource_digitalocean_firewall.go: Add DO Cloud Firewalls (#15124)
https://www.digitalocean.com/products/cloud-firewalls/
https://blog.digitalocean.com/cloud-firewalls-secure-droplets-by-default/
4fa9e9d999
air$ go test -v -run TestAccDigitalOceanFirewall
=== RUN TestAccDigitalOceanFirewall_importBasic
=== RUN TestAccDigitalOceanFirewall_importBasic/only_allow_inbound_SSH(TCP/22)
=== RUN TestAccDigitalOceanFirewall_importBasic/only_allow_outbound_SSH(TCP/22)
=== RUN TestAccDigitalOceanFirewall_importBasic/only_allow_inbound_SSH(TCP/22)_and_HTTP(TCP/80)
=== RUN TestAccDigitalOceanFirewall_importBasic/only_allow_outbound_SSH(TCP/22)_and_DNS(UDP/53)
=== RUN TestAccDigitalOceanFirewall_importBasic/allow_inbound_and_outbound_HTTPS(TCP/443),_inbound_SSH(TCP/22),_and_outbound_DNS(UDP/53)
--- PASS: TestAccDigitalOceanFirewall_importBasic (19.14s)
--- PASS: TestAccDigitalOceanFirewall_importBasic/only_allow_inbound_SSH(TCP/22) (3.89s)
--- PASS: TestAccDigitalOceanFirewall_importBasic/only_allow_outbound_SSH(TCP/22) (4.61s)
--- PASS: TestAccDigitalOceanFirewall_importBasic/only_allow_inbound_SSH(TCP/22)_and_HTTP(TCP/80) (3.48s)
--- PASS: TestAccDigitalOceanFirewall_importBasic/only_allow_outbound_SSH(TCP/22)_and_DNS(UDP/53) (4.05s)
--- PASS: TestAccDigitalOceanFirewall_importBasic/allow_inbound_and_outbound_HTTPS(TCP/443),_inbound_SSH(TCP/22),_and_outbound_DNS(UDP/53) (3.11s)
=== RUN TestAccDigitalOceanFirewall_Basic
=== RUN TestAccDigitalOceanFirewall_Basic/only_allow_inbound_SSH(TCP/22)
=== RUN TestAccDigitalOceanFirewall_Basic/only_allow_outbound_SSH(TCP/22)
=== RUN TestAccDigitalOceanFirewall_Basic/only_allow_inbound_SSH(TCP/22)_and_HTTP(TCP/80)
=== RUN TestAccDigitalOceanFirewall_Basic/only_allow_outbound_SSH(TCP/22)_and_DNS(UDP/53)
=== RUN TestAccDigitalOceanFirewall_Basic/allow_inbound_and_outbound_HTTPS(TCP/443),_inbound_SSH(TCP/22),_and_outbound_DNS(UDP/53)
--- PASS: TestAccDigitalOceanFirewall_Basic (18.77s)
--- PASS: TestAccDigitalOceanFirewall_Basic/only_allow_inbound_SSH(TCP/22) (3.56s)
--- PASS: TestAccDigitalOceanFirewall_Basic/only_allow_outbound_SSH(TCP/22) (3.73s)
--- PASS: TestAccDigitalOceanFirewall_Basic/only_allow_inbound_SSH(TCP/22)_and_HTTP(TCP/80) (3.21s)
--- PASS: TestAccDigitalOceanFirewall_Basic/only_allow_outbound_SSH(TCP/22)_and_DNS(UDP/53) (3.84s)
--- PASS: TestAccDigitalOceanFirewall_Basic/allow_inbound_and_outbound_HTTPS(TCP/443),_inbound_SSH(TCP/22),_and_outbound_DNS(UDP/53) (4.43s)
PASS ok github.com/terraform-providers/terraform-provider-digitalocean/digitalocean 37.920s
air$
This commit is contained in:
parent
7a9ee7c2a1
commit
f193630f42
|
@ -0,0 +1,140 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccDigitalOceanFirewall_importBasic(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
firewallName string
|
||||
config string
|
||||
}{
|
||||
{
|
||||
description: "only allow inbound SSH(TCP/22)",
|
||||
firewallName: fmt.Sprintf("foobar-test-terraform-firewall-%s", acctest.RandString(10)),
|
||||
config: `
|
||||
resource "digitalocean_firewall" "foobar" {
|
||||
name = "%s"
|
||||
inbound_rules = [
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "22"
|
||||
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||
},
|
||||
]
|
||||
}`,
|
||||
},
|
||||
{
|
||||
description: "only allow outbound SSH(TCP/22)",
|
||||
firewallName: fmt.Sprintf("foobar-test-terraform-firewall-%s", acctest.RandString(10)),
|
||||
config: `
|
||||
resource "digitalocean_firewall" "foobar" {
|
||||
name = "%s"
|
||||
outbound_rules = [
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "22"
|
||||
destination_addresses = ["0.0.0.0/0", "::/0"]
|
||||
},
|
||||
]
|
||||
}`,
|
||||
},
|
||||
{
|
||||
description: "only allow inbound SSH(TCP/22) and HTTP(TCP/80)",
|
||||
firewallName: fmt.Sprintf("foobar-test-terraform-firewall-%s", acctest.RandString(10)),
|
||||
config: `
|
||||
resource "digitalocean_firewall" "foobar" {
|
||||
name = "%s"
|
||||
inbound_rules = [
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "22"
|
||||
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||
},
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "80"
|
||||
source_addresses = ["1.2.3.0/24", "2002::/16"]
|
||||
},
|
||||
]
|
||||
}`,
|
||||
},
|
||||
{
|
||||
description: "only allow outbound SSH(TCP/22) and DNS(UDP/53)",
|
||||
firewallName: fmt.Sprintf("foobar-test-terraform-firewall-%s", acctest.RandString(10)),
|
||||
config: `
|
||||
resource "digitalocean_firewall" "foobar" {
|
||||
name = "%s"
|
||||
outbound_rules = [
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "22"
|
||||
destination_addresses = ["192.168.1.0/24", "2002:1001::/48"]
|
||||
},
|
||||
{
|
||||
protocol = "udp"
|
||||
port_range = "53"
|
||||
destination_addresses = ["1.2.3.0/24", "2002::/16"]
|
||||
},
|
||||
]
|
||||
}`,
|
||||
},
|
||||
{
|
||||
description: "allow inbound and outbound HTTPS(TCP/443), inbound SSH(TCP/22), and outbound DNS(UDP/53)",
|
||||
firewallName: fmt.Sprintf("foobar-test-terraform-firewall-%s", acctest.RandString(10)),
|
||||
config: `
|
||||
resource "digitalocean_firewall" "foobar" {
|
||||
name = "%s"
|
||||
inbound_rules = [
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "443"
|
||||
source_addresses = ["192.168.1.0/24", "2002:1001:1:2::/64"]
|
||||
},
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "22"
|
||||
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||
},
|
||||
]
|
||||
outbound_rules = [
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "443"
|
||||
destination_addresses = ["192.168.1.0/24", "2002:1001:1:2::/64"]
|
||||
},
|
||||
{
|
||||
protocol = "udp"
|
||||
port_range = "53"
|
||||
destination_addresses = ["0.0.0.0/0", "::/0"]
|
||||
},
|
||||
]
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
config := fmt.Sprintf(tt.config, tt.firewallName)
|
||||
resourceName := fmt.Sprintf("digitalocean_firewall.%s", tt.firewallName)
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDigitalOceanFirewallDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: config,
|
||||
ResourceName: resourceName,
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ func Provider() terraform.ResourceProvider {
|
|||
"digitalocean_certificate": resourceDigitalOceanCertificate(),
|
||||
"digitalocean_domain": resourceDigitalOceanDomain(),
|
||||
"digitalocean_droplet": resourceDigitalOceanDroplet(),
|
||||
"digitalocean_firewall": resourceDigitalOceanFirewall(),
|
||||
"digitalocean_floating_ip": resourceDigitalOceanFloatingIp(),
|
||||
"digitalocean_loadbalancer": resourceDigitalOceanLoadbalancer(),
|
||||
"digitalocean_record": resourceDigitalOceanRecord(),
|
||||
|
|
|
@ -0,0 +1,574 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/digitalocean/godo"
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func resourceDigitalOceanFirewall() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceDigitalOceanFirewallCreate,
|
||||
Read: resourceDigitalOceanFirewallRead,
|
||||
Update: resourceDigitalOceanFirewallUpdate,
|
||||
Delete: resourceDigitalOceanFirewallDelete,
|
||||
Exists: resourceDigitalOceanFirewallExists,
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: schema.ImportStatePassthrough,
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"status": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"created_at": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"pending_changes": {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"droplet_id": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
},
|
||||
"removing": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
},
|
||||
"status": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"droplet_ids": {
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"tags": {
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"inbound_rules": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"protocol": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"port_range": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"source_addresses": {
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
"source_tags": {
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
"source_droplet_ids": {
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeInt},
|
||||
Optional: true,
|
||||
},
|
||||
"source_load_balancer_uids": {
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"outbound_rules": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"protocol": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"port_range": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"destination_addresses": {
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
"destination_tags": {
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
"destination_droplet_ids": {
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeInt},
|
||||
Optional: true,
|
||||
},
|
||||
"destination_load_balancer_uids": {
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDigitalOceanFirewallCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*godo.Client)
|
||||
|
||||
opts, err := firewallRequest(d, client)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error in firewall request: %s", err)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Firewall create configuration: %#v", opts)
|
||||
|
||||
firewall, _, err := client.Firewalls.Create(context.Background(), opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating firewall: %s", err)
|
||||
}
|
||||
|
||||
// Assign the firewall id
|
||||
d.SetId(firewall.ID)
|
||||
|
||||
log.Printf("[INFO] Firewall ID: %s", d.Id())
|
||||
|
||||
return resourceDigitalOceanFirewallRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceDigitalOceanFirewallRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*godo.Client)
|
||||
|
||||
// Retrieve the firewall properties for updating the state
|
||||
firewall, resp, err := client.Firewalls.Get(context.Background(), d.Id())
|
||||
if err != nil {
|
||||
// check if the firewall no longer exists.
|
||||
if resp != nil && resp.StatusCode == 404 {
|
||||
log.Printf("[WARN] DigitalOcean Firewall (%s) not found", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Error retrieving firewall: %s", err)
|
||||
}
|
||||
|
||||
d.Set("status", firewall.Status)
|
||||
d.Set("create_at", firewall.Created)
|
||||
d.Set("pending_changes", firewallPendingChanges(d, firewall))
|
||||
d.Set("name", firewall.Name)
|
||||
d.Set("droplet_ids", firewall.DropletIDs)
|
||||
d.Set("tags", firewall.Tags)
|
||||
d.Set("inbound_rules", matchFirewallInboundRules(d, firewall))
|
||||
d.Set("outbound_rules", matchFirewallOutboundRules(d, firewall))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceDigitalOceanFirewallUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*godo.Client)
|
||||
|
||||
opts, err := firewallRequest(d, client)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error in firewall request: %s", err)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Firewall update configuration: %#v", opts)
|
||||
|
||||
_, _, err = client.Firewalls.Update(context.Background(), d.Id(), opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error updating firewall: %s", err)
|
||||
}
|
||||
|
||||
return resourceDigitalOceanFirewallRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceDigitalOceanFirewallDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*godo.Client)
|
||||
|
||||
log.Printf("[INFO] Deleting firewall: %s", d.Id())
|
||||
|
||||
// Destroy the droplet
|
||||
_, err := client.Firewalls.Delete(context.Background(), d.Id())
|
||||
|
||||
// Handle remotely destroyed droplets
|
||||
if err != nil && strings.Contains(err.Error(), "404 Not Found") {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting firewall: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceDigitalOceanFirewallExists(d *schema.ResourceData, meta interface{}) (bool, error) {
|
||||
client := meta.(*godo.Client)
|
||||
|
||||
log.Printf("[INFO] Exists firewall: %s", d.Id())
|
||||
|
||||
// Retrieve the firewall properties for updating the state
|
||||
_, resp, err := client.Firewalls.Get(context.Background(), d.Id())
|
||||
if err != nil {
|
||||
// check if the firewall no longer exists.
|
||||
if resp != nil && resp.StatusCode == 404 {
|
||||
log.Printf("[WARN] DigitalOcean Firewall (%s) not found", d.Id())
|
||||
d.SetId("")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("Error retrieving firewall: %s", err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func firewallRequest(d *schema.ResourceData, client *godo.Client) (*godo.FirewallRequest, error) {
|
||||
// Build up our firewall request
|
||||
opts := &godo.FirewallRequest{
|
||||
Name: d.Get("name").(string),
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("droplet_ids"); ok {
|
||||
var droplets []int
|
||||
for _, id := range v.([]interface{}) {
|
||||
i, err := strconv.Atoi(id.(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
droplets = append(droplets, i)
|
||||
}
|
||||
opts.DropletIDs = droplets
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("tags"); ok {
|
||||
var tags []string
|
||||
for _, tag := range v.([]interface{}) {
|
||||
tags = append(tags, tag.(string))
|
||||
}
|
||||
opts.Tags = tags
|
||||
}
|
||||
|
||||
// Get inbound_rules
|
||||
opts.InboundRules = firewallInboundRules(d)
|
||||
|
||||
// Get outbound_rules
|
||||
opts.OutboundRules = firewallOutboundRules(d)
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func firewallInboundRules(d *schema.ResourceData) []godo.InboundRule {
|
||||
rules := make([]godo.InboundRule, 0, len(d.Get("inbound_rules").([]interface{})))
|
||||
for i, rawRule := range d.Get("inbound_rules").([]interface{}) {
|
||||
var src godo.Sources
|
||||
|
||||
rule := rawRule.(map[string]interface{})
|
||||
key := fmt.Sprintf("inbound_rules.%d.source_addresses", i)
|
||||
if vv, ok := d.GetOk(key); ok {
|
||||
for _, v := range vv.([]interface{}) {
|
||||
src.Addresses = append(src.Addresses, v.(string))
|
||||
}
|
||||
}
|
||||
key = fmt.Sprintf("inbound_rules.%d.source_tags", i)
|
||||
if vv, ok := d.GetOk(key); ok {
|
||||
for _, v := range vv.([]interface{}) {
|
||||
src.Tags = append(src.Tags, v.(string))
|
||||
}
|
||||
}
|
||||
key = fmt.Sprintf("inbound_rules.%d.source_droplet_ids", i)
|
||||
if vv, ok := d.GetOk(key); ok {
|
||||
for _, v := range vv.([]interface{}) {
|
||||
src.DropletIDs = append(src.DropletIDs, v.(int))
|
||||
}
|
||||
}
|
||||
key = fmt.Sprintf("inbound_rules.%d.source_load_balancer_uids", i)
|
||||
if vv, ok := d.GetOk(key); ok {
|
||||
for _, v := range vv.([]interface{}) {
|
||||
src.LoadBalancerUIDs = append(src.LoadBalancerUIDs, v.(string))
|
||||
}
|
||||
}
|
||||
r := godo.InboundRule{
|
||||
Protocol: rule["protocol"].(string),
|
||||
PortRange: rule["port_range"].(string),
|
||||
Sources: &src,
|
||||
}
|
||||
rules = append(rules, r)
|
||||
}
|
||||
return rules
|
||||
}
|
||||
|
||||
func firewallOutboundRules(d *schema.ResourceData) []godo.OutboundRule {
|
||||
rules := make([]godo.OutboundRule, 0, len(d.Get("outbound_rules").([]interface{})))
|
||||
for i, rawRule := range d.Get("outbound_rules").([]interface{}) {
|
||||
var dest godo.Destinations
|
||||
|
||||
rule := rawRule.(map[string]interface{})
|
||||
key := fmt.Sprintf("outbound_rules.%d.destination_addresses", i)
|
||||
if vv, ok := d.GetOk(key); ok {
|
||||
for _, v := range vv.([]interface{}) {
|
||||
dest.Addresses = append(dest.Addresses, v.(string))
|
||||
}
|
||||
}
|
||||
key = fmt.Sprintf("outbound_rules.%d.destination_tags", i)
|
||||
if vv, ok := d.GetOk(key); ok {
|
||||
for _, v := range vv.([]interface{}) {
|
||||
dest.Tags = append(dest.Tags, v.(string))
|
||||
}
|
||||
}
|
||||
key = fmt.Sprintf("outbound_rules.%d.destination_droplet_ids", i)
|
||||
if vv, ok := d.GetOk(key); ok {
|
||||
for _, v := range vv.([]interface{}) {
|
||||
dest.DropletIDs = append(dest.DropletIDs, v.(int))
|
||||
}
|
||||
}
|
||||
key = fmt.Sprintf("outbound_rules.%d.destination_load_balancer_uids", i)
|
||||
if vv, ok := d.GetOk(key); ok {
|
||||
for _, v := range vv.([]interface{}) {
|
||||
dest.LoadBalancerUIDs = append(dest.LoadBalancerUIDs, v.(string))
|
||||
}
|
||||
}
|
||||
r := godo.OutboundRule{
|
||||
Protocol: rule["protocol"].(string),
|
||||
PortRange: rule["port_range"].(string),
|
||||
Destinations: &dest,
|
||||
}
|
||||
rules = append(rules, r)
|
||||
}
|
||||
return rules
|
||||
}
|
||||
|
||||
func firewallPendingChanges(d *schema.ResourceData, firewall *godo.Firewall) []interface{} {
|
||||
remote := make([]interface{}, 0, len(firewall.PendingChanges))
|
||||
for _, change := range firewall.PendingChanges {
|
||||
rawChange := map[string]interface{}{
|
||||
"droplet_id": change.DropletID,
|
||||
"removing": change.Removing,
|
||||
"status": change.Status,
|
||||
}
|
||||
remote = append(remote, rawChange)
|
||||
}
|
||||
return remote
|
||||
}
|
||||
|
||||
func matchFirewallInboundRules(d *schema.ResourceData, firewall *godo.Firewall) []interface{} {
|
||||
// Prepare the data.
|
||||
local := d.Get("inbound_rules").([]interface{})
|
||||
remote := make([]interface{}, 0, len(firewall.InboundRules))
|
||||
remoteMap := make(map[int]map[string]interface{})
|
||||
for _, rule := range firewall.InboundRules {
|
||||
rawRule := map[string]interface{}{
|
||||
"protocol": rule.Protocol,
|
||||
"port_range": rule.PortRange,
|
||||
"source_droplet_ids": rule.Sources.DropletIDs,
|
||||
"source_tags": rule.Sources.Tags,
|
||||
"source_addresses": rule.Sources.Addresses,
|
||||
"source_load_balancer_uids": rule.Sources.LoadBalancerUIDs,
|
||||
}
|
||||
remote = append(remote, rawRule)
|
||||
hash := hashFirewallRule(rule.Protocol, rule.PortRange)
|
||||
remoteMap[hash] = rawRule
|
||||
}
|
||||
|
||||
// Handle special cases, both using the remote rules.
|
||||
if len(remote) == 0 || len(local) == 0 {
|
||||
return remote
|
||||
}
|
||||
|
||||
// Update the local rules to only contains rules match
|
||||
// to the remote rules.
|
||||
match := make([]interface{}, 0, len(firewall.InboundRules))
|
||||
for _, rawRule := range local {
|
||||
local := rawRule.(map[string]interface{})
|
||||
protocol := local["protocol"].(string)
|
||||
portRange := local["port_range"].(string)
|
||||
hash := hashFirewallRule(protocol, portRange)
|
||||
remote, ok := remoteMap[hash]
|
||||
if !ok {
|
||||
// No entry in the remote, remove it.
|
||||
continue
|
||||
}
|
||||
|
||||
// matches source lists.
|
||||
key := "source_droplet_ids"
|
||||
local[key] = matchFirewallIntLists(key, local, remote)
|
||||
keys := []string{
|
||||
"source_tags",
|
||||
"source_addresses",
|
||||
"source_load_balancer_uids",
|
||||
}
|
||||
for _, key := range keys {
|
||||
local[key] = matchFirewallStringLists(key, local, remote)
|
||||
}
|
||||
|
||||
match = append(match, local)
|
||||
delete(remoteMap, hash)
|
||||
}
|
||||
|
||||
// Append the remaining remote rules.
|
||||
for _, rawRule := range remoteMap {
|
||||
match = append(match, rawRule)
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
func matchFirewallOutboundRules(d *schema.ResourceData, firewall *godo.Firewall) []interface{} {
|
||||
// Prepare the data.
|
||||
local := d.Get("outbound_rules").([]interface{})
|
||||
remote := make([]interface{}, 0, len(firewall.OutboundRules))
|
||||
remoteMap := make(map[int]map[string]interface{})
|
||||
for _, rule := range firewall.OutboundRules {
|
||||
rawRule := map[string]interface{}{
|
||||
"protocol": rule.Protocol,
|
||||
"port_range": rule.PortRange,
|
||||
"destination_droplet_ids": rule.Destinations.DropletIDs,
|
||||
"destination_tags": rule.Destinations.Tags,
|
||||
"destination_addresses": rule.Destinations.Addresses,
|
||||
"destination_load_balancer_uids": rule.Destinations.LoadBalancerUIDs,
|
||||
}
|
||||
remote = append(remote, rawRule)
|
||||
hash := hashFirewallRule(rule.Protocol, rule.PortRange)
|
||||
remoteMap[hash] = rawRule
|
||||
}
|
||||
|
||||
// Handle special cases, both using the remote rules.
|
||||
if len(remote) == 0 || len(local) == 0 {
|
||||
return remote
|
||||
}
|
||||
|
||||
// Update the local rules to only contains rules match
|
||||
// to the remote rules.
|
||||
match := make([]interface{}, 0, len(firewall.OutboundRules))
|
||||
for _, rawRule := range local {
|
||||
local := rawRule.(map[string]interface{})
|
||||
protocol := local["protocol"].(string)
|
||||
portRange := local["port_range"].(string)
|
||||
hash := hashFirewallRule(protocol, portRange)
|
||||
remote, ok := remoteMap[hash]
|
||||
if !ok {
|
||||
// No entry in the remote, remove it.
|
||||
continue
|
||||
}
|
||||
|
||||
// matches destination lists.
|
||||
key := "destination_droplet_ids"
|
||||
local[key] = matchFirewallIntLists(key, local, remote)
|
||||
keys := []string{
|
||||
"destination_tags",
|
||||
"destination_addresses",
|
||||
"destination_load_balancer_uids",
|
||||
}
|
||||
for _, key := range keys {
|
||||
local[key] = matchFirewallStringLists(key, local, remote)
|
||||
}
|
||||
|
||||
match = append(match, local)
|
||||
delete(remoteMap, hash)
|
||||
}
|
||||
|
||||
// Append the remaining remote rules.
|
||||
for _, rawRule := range remoteMap {
|
||||
match = append(match, rawRule)
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
func matchFirewallIntLists(key string, local, remote map[string]interface{}) []interface{} {
|
||||
remoteSize := len(remote[key].([]int))
|
||||
remoteSet := make(map[int]bool)
|
||||
matchedList := make([]interface{}, 0, remoteSize)
|
||||
|
||||
// Create a remote set out of the list for the quick comparison.
|
||||
for _, i := range remote[key].([]int) {
|
||||
remoteSet[i] = true
|
||||
}
|
||||
|
||||
// Add only the item which exists in the remote list.
|
||||
for _, i := range local[key].([]interface{}) {
|
||||
if _, ok := remoteSet[i.(int)]; !ok {
|
||||
continue
|
||||
}
|
||||
matchedList = append(matchedList, i)
|
||||
delete(remoteSet, i.(int))
|
||||
}
|
||||
|
||||
// Append items only exists in the remote list.
|
||||
for i := range remoteSet {
|
||||
matchedList = append(matchedList, i)
|
||||
}
|
||||
|
||||
return matchedList
|
||||
}
|
||||
|
||||
func matchFirewallStringLists(key string, local, remote map[string]interface{}) []interface{} {
|
||||
remoteSize := len(remote[key].([]string))
|
||||
remoteList := make([]interface{}, 0, remoteSize)
|
||||
matchedList := make([]interface{}, 0, remoteSize)
|
||||
|
||||
// Create a remote set out of the list for the quick comparison.
|
||||
for _, s := range remote[key].([]string) {
|
||||
remoteList = append(remoteList, s)
|
||||
}
|
||||
remoteSet := schema.NewSet(schema.HashString, remoteList)
|
||||
|
||||
// Add only the item which exists in the remote list.
|
||||
for _, s := range local[key].([]interface{}) {
|
||||
if !remoteSet.Contains(s.(string)) {
|
||||
continue
|
||||
}
|
||||
matchedList = append(matchedList, s)
|
||||
remoteSet.Remove(s)
|
||||
}
|
||||
|
||||
// Append items only exists in the remote list.
|
||||
for _, s := range remoteSet.List() {
|
||||
matchedList = append(matchedList, s)
|
||||
}
|
||||
|
||||
return matchedList
|
||||
}
|
||||
|
||||
func hashFirewallRule(protocol, portRange string) int {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(fmt.Sprintf("%s-%s", protocol, portRange))
|
||||
return hashcode.String(buf.String())
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/digitalocean/godo"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccDigitalOceanFirewall_Basic(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
firewallName string
|
||||
config string
|
||||
checkers []resource.TestCheckFunc
|
||||
}{
|
||||
{
|
||||
description: "only allow inbound SSH(TCP/22)",
|
||||
firewallName: fmt.Sprintf("foobar-test-terraform-firewall-%s", acctest.RandString(10)),
|
||||
config: `
|
||||
resource "digitalocean_firewall" "foobar" {
|
||||
name = "%s"
|
||||
inbound_rules = [
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "22"
|
||||
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||
},
|
||||
]
|
||||
}`,
|
||||
checkers: []resource.TestCheckFunc{
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.port_range", "22"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.source_addresses.#", "2"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.source_addresses.0", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.source_addresses.1", "::/0"),
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "only allow outbound SSH(TCP/22)",
|
||||
firewallName: fmt.Sprintf("foobar-test-terraform-firewall-%s", acctest.RandString(10)),
|
||||
config: `
|
||||
resource "digitalocean_firewall" "foobar" {
|
||||
name = "%s"
|
||||
outbound_rules = [
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "22"
|
||||
destination_addresses = ["0.0.0.0/0", "::/0"]
|
||||
},
|
||||
]
|
||||
}`,
|
||||
checkers: []resource.TestCheckFunc{
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.#", "1"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.port_range", "22"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.destination_addresses.#", "2"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.destination_addresses.0", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.destination_addresses.1", "::/0"),
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "only allow inbound SSH(TCP/22) and HTTP(TCP/80)",
|
||||
firewallName: fmt.Sprintf("foobar-test-terraform-firewall-%s", acctest.RandString(10)),
|
||||
config: `
|
||||
resource "digitalocean_firewall" "foobar" {
|
||||
name = "%s"
|
||||
inbound_rules = [
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "22"
|
||||
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||
},
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "80"
|
||||
source_addresses = ["1.2.3.0/24", "2002::/16"]
|
||||
},
|
||||
]
|
||||
}`,
|
||||
checkers: []resource.TestCheckFunc{
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.#", "2"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.port_range", "22"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.source_addresses.#", "2"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.source_addresses.0", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.source_addresses.1", "::/0"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.1.port_range", "80"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.1.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.1.source_addresses.#", "2"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.1.source_addresses.0", "1.2.3.0/24"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.1.source_addresses.1", "2002::/16"),
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "only allow outbound SSH(TCP/22) and DNS(UDP/53)",
|
||||
firewallName: fmt.Sprintf("foobar-test-terraform-firewall-%s", acctest.RandString(10)),
|
||||
config: `
|
||||
resource "digitalocean_firewall" "foobar" {
|
||||
name = "%s"
|
||||
outbound_rules = [
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "22"
|
||||
destination_addresses = ["192.168.1.0/24", "2002:1001::/48"]
|
||||
},
|
||||
{
|
||||
protocol = "udp"
|
||||
port_range = "53"
|
||||
destination_addresses = ["1.2.3.0/24", "2002::/16"]
|
||||
},
|
||||
]
|
||||
}`,
|
||||
checkers: []resource.TestCheckFunc{
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.#", "2"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.port_range", "22"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.destination_addresses.#", "2"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.destination_addresses.0", "192.168.1.0/24"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.destination_addresses.1", "2002:1001::/48"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.1.port_range", "53"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.1.protocol", "udp"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.1.destination_addresses.#", "2"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.1.destination_addresses.0", "1.2.3.0/24"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.1.destination_addresses.1", "2002::/16"),
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "allow inbound and outbound HTTPS(TCP/443), inbound SSH(TCP/22), and outbound DNS(UDP/53)",
|
||||
firewallName: fmt.Sprintf("foobar-test-terraform-firewall-%s", acctest.RandString(10)),
|
||||
config: `
|
||||
resource "digitalocean_firewall" "foobar" {
|
||||
name = "%s"
|
||||
inbound_rules = [
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "443"
|
||||
source_addresses = ["192.168.1.0/24", "2002:1001:1:2::/64"]
|
||||
},
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "22"
|
||||
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||
},
|
||||
]
|
||||
outbound_rules = [
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "443"
|
||||
destination_addresses = ["192.168.1.0/24", "2002:1001:1:2::/64"]
|
||||
},
|
||||
{
|
||||
protocol = "udp"
|
||||
port_range = "53"
|
||||
destination_addresses = ["0.0.0.0/0", "::/0"]
|
||||
},
|
||||
]
|
||||
}`,
|
||||
checkers: []resource.TestCheckFunc{
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.#", "2"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.port_range", "443"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.source_addresses.#", "2"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.source_addresses.0", "192.168.1.0/24"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.0.source_addresses.1", "2002:1001:1:2::/64"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.1.port_range", "22"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.1.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.1.source_addresses.#", "2"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.1.source_addresses.0", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "inbound_rules.1.source_addresses.1", "::/0"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.#", "2"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.port_range", "443"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.destination_addresses.#", "2"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.destination_addresses.0", "192.168.1.0/24"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.0.destination_addresses.1", "2002:1001:1:2::/64"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.1.port_range", "53"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.1.protocol", "udp"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.1.destination_addresses.#", "2"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.1.destination_addresses.0", "0.0.0.0/0"),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "outbound_rules.1.destination_addresses.1", "::/0"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var firewall godo.Firewall
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
checkers := []resource.TestCheckFunc{
|
||||
testAccCheckDigitalOceanFirewallExists("digitalocean_firewall.foobar", &firewall),
|
||||
resource.TestCheckResourceAttr("digitalocean_firewall.foobar", "name", tt.firewallName),
|
||||
}
|
||||
checkers = append(checkers, tt.checkers...)
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDigitalOceanFirewallDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: fmt.Sprintf(tt.config, tt.firewallName),
|
||||
Check: resource.ComposeTestCheckFunc(checkers...),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckDigitalOceanFirewallDestroy(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*godo.Client)
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "digitalocean_firewall" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Try to find the firewall
|
||||
_, _, err := client.Firewalls.Get(context.Background(), rs.Primary.ID)
|
||||
|
||||
if err == nil {
|
||||
return fmt.Errorf("Firewall still exists")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckDigitalOceanFirewallExists(n string, firewall *godo.Firewall) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No Record ID is set")
|
||||
}
|
||||
|
||||
client := testAccProvider.Meta().(*godo.Client)
|
||||
|
||||
foundFirewall, _, err := client.Firewalls.Get(context.Background(), rs.Primary.ID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if foundFirewall.ID != rs.Primary.ID {
|
||||
return fmt.Errorf("Record not found")
|
||||
}
|
||||
|
||||
*firewall = *foundFirewall
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -33,6 +33,10 @@
|
|||
<a href="/docs/providers/do/r/droplet.html">digitalocean_droplet</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-do-resource-firewall") %>>
|
||||
<a href="/docs/providers/do/r/firewall.html">digitalocean_firewall</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-do-resource-floating-ip") %>>
|
||||
<a href="/docs/providers/do/r/floating_ip.html">digitalocean_floating_ip</a>
|
||||
</li>
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
---
|
||||
layout: "digitalocean"
|
||||
page_title: "DigitalOcean: digitalocean_firewall"
|
||||
sidebar_current: "docs-do-resource-firewall"
|
||||
description: |-
|
||||
Provides a DigitalOcean Cloud Firewall resource. This can be used to create, modify, and delete Firewalls.
|
||||
---
|
||||
|
||||
# digitalocean\_firewall
|
||||
|
||||
Provides a DigitalOcean Cloud Firewall resource. This can be used to create,
|
||||
modify, and delete Firewalls.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```hcl
|
||||
resource "digitalocean_droplet" "web" {
|
||||
name = "web-1"
|
||||
size = "512mb"
|
||||
image = "centos-7-x64"
|
||||
region = "nyc3"
|
||||
}
|
||||
|
||||
resource "digitalocean_firewall" "web" {
|
||||
name = "only-22-80-and-443"
|
||||
|
||||
droplet_ids = ["${digitalocean_droplet.web.id}"]
|
||||
|
||||
inbound_rules = [
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "22"
|
||||
source_addresses = ["192.168.1.0/24", "2002:1:2::/48"]
|
||||
},
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "80"
|
||||
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||
},
|
||||
{
|
||||
protocol = "tcp"
|
||||
port_range = "443"
|
||||
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||
},
|
||||
]
|
||||
|
||||
outbound_rules = [
|
||||
{
|
||||
protocol = "udp"
|
||||
port_range = "53"
|
||||
destination_addresses = ["0.0.0.0/0", "::/0"]
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `name` - (Required) The Firewall name
|
||||
* `droplet_ids` (Optional) - The list of the IDs of the Droplets assigned
|
||||
to the Firewall.
|
||||
* `tags` (Optional) - The names of the Tags assigned to the Firewall.
|
||||
* `inbound_rules` - (Optional) The inbound access rule block for the Firewall.
|
||||
The `inbound_rules` block is documented below.
|
||||
* `outbound_rules` - (Optional) The outbound access rule block for the Firewall.
|
||||
The `outbound_rules` block is documented below.
|
||||
|
||||
`inbound_rule` supports the following:
|
||||
|
||||
* `protocol` - (Optional) The type of traffic to be allowed.
|
||||
This may be one of "tcp", "udp", or "icmp".
|
||||
* `port_range` - (Optional) The ports on which traffic will be allowed
|
||||
specified as a string containing a single port, a range (e.g. "8000-9000"),
|
||||
or "all" to open all ports for a protocol.
|
||||
* `source_addresses` - (Optional) An array of strings containing the IPv4
|
||||
addresses, IPv6 addresses, IPv4 CIDRs, and/or IPv6 CIDRs from which the
|
||||
inbound traffic will be accepted.
|
||||
* `source_droplet_ids` - (Optional) An array containing the IDs of
|
||||
the Droplets from which the inbound traffic will be accepted.
|
||||
* `source_tags` - (Optional) An array containing the names of Tags
|
||||
corresponding to groups of Droplets from which the inbound traffic
|
||||
will be accepted.
|
||||
* `source_load_balancer_uids` - (Optional) An array containing the IDs
|
||||
of the Load Balancers from which the inbound traffic will be accepted.
|
||||
|
||||
`outbound_rule` supports the following:
|
||||
|
||||
* `protocol` - (Optional) The type of traffic to be allowed.
|
||||
This may be one of "tcp", "udp", or "icmp".
|
||||
* `port_range` - (Optional) The ports on which traffic will be allowed
|
||||
specified as a string containing a single port, a range (e.g. "8000-9000"),
|
||||
or "all" to open all ports for a protocol.
|
||||
* `destination_addresses` - (Optional) An array of strings containing the IPv4
|
||||
addresses, IPv6 addresses, IPv4 CIDRs, and/or IPv6 CIDRs to which the
|
||||
outbound traffic will be allowed.
|
||||
* `destination_droplet_ids` - (Optional) An array containing the IDs of
|
||||
the Droplets to which the outbound traffic will be allowed.
|
||||
* `destination_tags` - (Optional) An array containing the names of Tags
|
||||
corresponding to groups of Droplets to which the outbound traffic will
|
||||
be allowed.
|
||||
traffic.
|
||||
* `destination_load_balancer_uids` - (Optional) An array containing the IDs
|
||||
of the Load Balancers to which the outbound traffic will be allowed.
|
||||
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `id` - A unique ID that can be used to identify and reference a Firewall.
|
||||
* `status` - A status string indicating the current state of the Firewall.
|
||||
This can be "waiting", "succeeded", or "failed".
|
||||
* `created_at` - A time value given in ISO8601 combined date and time format
|
||||
that represents when the Firewall was created.
|
||||
* `pending_changes` - An list of object containing the fields, "droplet_id",
|
||||
"removing", and "status". It is provided to detail exactly which Droplets
|
||||
are having their security policies updated. When empty, all changes
|
||||
have been successfully applied.
|
||||
* `name` - The name of the Firewall.
|
||||
* `droplet_ids` - The list of the IDs of the Droplets assigned to
|
||||
the Firewall.
|
||||
* `tags` - The names of the Tags assigned to the Firewall.
|
||||
* `inbound_rules` - The inbound access rule block for the Firewall.
|
||||
* `outbound_rules` - The outbound access rule block for the Firewall.
|
||||
|
||||
## Import
|
||||
|
||||
Firewalls can be imported using the firewall `id`, e.g.
|
||||
|
||||
```
|
||||
terraform import digitalocean_firewall.myfirewall b8ecd2ab-2267-4a5e-8692-cbf1d32583e3
|
||||
```
|
Loading…
Reference in New Issue