From 132779c343676568778c56de6b4438664e55ab16 Mon Sep 17 00:00:00 2001 From: Sumit Kalra <44450797+sumit-kalra@users.noreply.github.com> Date: Tue, 24 Sep 2019 09:34:19 -0700 Subject: [PATCH] Adding config to specify allowed inbound IP addresses and CIDR blocks (#1) * Adding config to specify allowed inbound IP addresses * Also allowing CIDR blocks in addition to IP addresses --- builder/azure/arm/config.go | 23 +++++++ builder/azure/arm/config_test.go | 102 +++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 7243bd2a8..6744287cc 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -12,6 +12,7 @@ import ( "fmt" "io/ioutil" "math/big" + "net" "regexp" "strings" "time" @@ -342,6 +343,11 @@ type Config struct { // are None, ReadOnly, and ReadWrite. The default value is ReadWrite. DiskCachingType string `mapstructure:"disk_caching_type" required:"false"` diskCachingType compute.CachingTypes + // Specify the list of IP addresses and CIDR blocks that should be + // allowed access to the VM. If provided, an Azure Network Security + // Group will be created with corresponding rules and be bound to + // the NIC attached to the VM. + AllowedInboundIpAddresses []string `mapstructure:"allowed_inbound_ip_addresses"` // Runtime Values UserName string @@ -872,6 +878,12 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) { errs = packer.MultiErrorAppend(errs, fmt.Errorf("If virtual_network_subnet_name is specified, so must virtual_network_name")) } + if c.AllowedInboundIpAddresses != nil && len(c.AllowedInboundIpAddresses) >= 1 { + if ok, err := assertAllowedInboundIpAddresses(c.AllowedInboundIpAddresses, "allowed_inbound_ip_addresses"); !ok { + errs = packer.MultiErrorAppend(errs, err) + } + } + ///////////////////////////////////////////// // Plan Info if c.PlanInfo.PlanName != "" || c.PlanInfo.PlanProduct != "" || c.PlanInfo.PlanPublisher != "" || c.PlanInfo.PlanPromotionCode != "" { @@ -943,6 +955,17 @@ func assertManagedImageDataDiskSnapshotName(name, setting string) (bool, error) return true, nil } +func assertAllowedInboundIpAddresses(ipAddresses []string, setting string) (bool, error) { + for _, ipAddress := range ipAddresses { + if net.ParseIP(ipAddress) == nil { + if _, _, err := net.ParseCIDR(ipAddress); err != nil { + return false, fmt.Errorf("The setting %s must only contain valid IP addresses or CIDR blocks", setting) + } + } + } + return true, nil +} + func assertResourceGroupName(rgn, setting string) (bool, error) { if !isValidAzureName(reResourceGroupName, rgn) { return false, fmt.Errorf("The setting %s must match the regular expression %q, and not end with a '-' or '.'.", setting, validResourceGroupNameRe) diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index 9ed25fffa..ce446b246 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -270,6 +270,108 @@ func TestConfigVirtualNetworkSubnetNameMustBeSetWithVirtualNetworkName(t *testin } } +func TestConfigAllowedInboundIpAddressesIsOptional(t *testing.T) { + config := map[string]string{ + "capture_name_prefix": "ignore", + "capture_container_name": "ignore", + "location": "ignore", + "image_url": "ignore", + "storage_account": "ignore", + "resource_group_name": "ignore", + "subscription_id": "ignore", + "os_type": constants.Target_Linux, + "communicator": "none", + "virtual_network_name": "MyVirtualNetwork", + } + + c, _, err := newConfig(config, getPackerConfiguration()) + if err != nil { + t.Fatal(err) + } + if c.AllowedInboundIpAddresses != nil { + t.Errorf("Expected Config to set allowed_inbound_ip_addresses to nil, but got %v", c.AllowedInboundIpAddresses) + } +} + +func TestConfigShouldAcceptCorrectInboundIpAddresses(t *testing.T) { + ipValue0 := "127.0.0.1" + ipValue1 := "127.0.0.2" + cidrValue2 := "192.168.100.0/24" + cidrValue3 := "10.10.1.16/32" + config := map[string]interface{}{ + "capture_name_prefix": "ignore", + "capture_container_name": "ignore", + "location": "ignore", + "image_url": "ignore", + "storage_account": "ignore", + "resource_group_name": "ignore", + "subscription_id": "ignore", + "os_type": constants.Target_Linux, + "communicator": "none", + "virtual_network_name": "MyVirtualNetwork", + } + + config["allowed_inbound_ip_addresses"] = ipValue0 + c, _, err := newConfig(config, getPackerConfiguration()) + if err != nil { + t.Fatal(err) + } + if c.AllowedInboundIpAddresses == nil || len(c.AllowedInboundIpAddresses) != 1 || + c.AllowedInboundIpAddresses[0] != ipValue0 { + t.Errorf("Expected 'allowed_inbound_ip_addresses' to have one element (%s), but got '%v'.", ipValue0, c.AllowedInboundIpAddresses) + } + + config["allowed_inbound_ip_addresses"] = cidrValue2 + c, _, err = newConfig(config, getPackerConfiguration()) + if err != nil { + t.Fatal(err) + } + if c.AllowedInboundIpAddresses == nil || len(c.AllowedInboundIpAddresses) != 1 || + c.AllowedInboundIpAddresses[0] != cidrValue2 { + t.Errorf("Expected 'allowed_inbound_ip_addresses' to have one element (%s), but got '%v'.", cidrValue2, c.AllowedInboundIpAddresses) + } + + config["allowed_inbound_ip_addresses"] = []string{ipValue0, cidrValue2, ipValue1, cidrValue3} + c, _, err = newConfig(config, getPackerConfiguration()) + if err != nil { + t.Fatal(err) + } + if c.AllowedInboundIpAddresses == nil || len(c.AllowedInboundIpAddresses) != 4 || + c.AllowedInboundIpAddresses[0] != ipValue0 || c.AllowedInboundIpAddresses[1] != cidrValue2 || + c.AllowedInboundIpAddresses[2] != ipValue1 || c.AllowedInboundIpAddresses[3] != cidrValue3 { + t.Errorf("Expected 'allowed_inbound_ip_addresses' to have four elements (%s %s %s %s), but got '%v'.", ipValue0, cidrValue2, ipValue1, + cidrValue3, c.AllowedInboundIpAddresses) + } +} + +func TestConfigShouldRejectIncorrectInboundIpAddresses(t *testing.T) { + config := map[string]interface{}{ + "capture_name_prefix": "ignore", + "capture_container_name": "ignore", + "location": "ignore", + "image_url": "ignore", + "storage_account": "ignore", + "resource_group_name": "ignore", + "subscription_id": "ignore", + "os_type": constants.Target_Linux, + "communicator": "none", + "virtual_network_name": "MyVirtualNetwork", + } + + config["allowed_inbound_ip_addresses"] = []string{"127.0.0.1", "127.0.0.two"} + c, _, err := newConfig(config, getPackerConfiguration()) + if err == nil { + t.Errorf("Expected configuration creation to fail, but it succeeded with the malformed allowed_inbound_ip_addresses set to %v", c.AllowedInboundIpAddresses) + } + + config["allowed_inbound_ip_addresses"] = []string{"192.168.100.1000/24", "10.10.1.16/32"} + c, _, err = newConfig(config, getPackerConfiguration()) + if err == nil { + // 192.168.100.1000/24 is invalid + t.Errorf("Expected configuration creation to fail, but it succeeded with the malformed allowed_inbound_ip_addresses set to %v", c.AllowedInboundIpAddresses) + } +} + func TestConfigShouldDefaultToPublicCloud(t *testing.T) { c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())