From 0f4d81e0917c51257d4d746c607f806913376356 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 | 29 +++++- builder/azure/arm/config_test.go | 102 +++++++++++++++++++++ website/source/docs/builders/azure.html.md | 5 + 3 files changed, 131 insertions(+), 5 deletions(-) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 5e9b8905c..aafd1bca4 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -10,6 +10,7 @@ import ( "fmt" "io/ioutil" "math/big" + "net" "regexp" "strings" "time" @@ -129,11 +130,12 @@ type Config struct { TempResourceGroupName string `mapstructure:"temp_resource_group_name"` BuildResourceGroupName string `mapstructure:"build_resource_group_name"` storageAccountBlobEndpoint string - PrivateVirtualNetworkWithPublicIp bool `mapstructure:"private_virtual_network_with_public_ip"` - VirtualNetworkName string `mapstructure:"virtual_network_name"` - VirtualNetworkSubnetName string `mapstructure:"virtual_network_subnet_name"` - VirtualNetworkResourceGroupName string `mapstructure:"virtual_network_resource_group_name"` - CustomDataFile string `mapstructure:"custom_data_file"` + PrivateVirtualNetworkWithPublicIp bool `mapstructure:"private_virtual_network_with_public_ip"` + VirtualNetworkName string `mapstructure:"virtual_network_name"` + VirtualNetworkSubnetName string `mapstructure:"virtual_network_subnet_name"` + VirtualNetworkResourceGroupName string `mapstructure:"virtual_network_resource_group_name"` + AllowedInboundIpAddresses []string `mapstructure:"allowed_inbound_ip_addresses"` + CustomDataFile string `mapstructure:"custom_data_file"` customData string PlanInfo PlanInformation `mapstructure:"plan_info"` @@ -673,6 +675,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 != "" { @@ -744,6 +752,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 ef5c9fb93..9175c3d26 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()) diff --git a/website/source/docs/builders/azure.html.md b/website/source/docs/builders/azure.html.md index aa741d675..37c62eda1 100644 --- a/website/source/docs/builders/azure.html.md +++ b/website/source/docs/builders/azure.html.md @@ -336,6 +336,11 @@ Providing `temp_resource_group_name` or `location` in combination with containing the virtual network. If the resource group cannot be found, or it cannot be disambiguated, this value should be set. +- `allowed_inbound_ip_addresses` (array of strings) 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. + - `virtual_network_subnet_name` (string) If virtual\_network\_name is set, this value **may** also be set. If virtual\_network\_name is set, and this value is not set the builder attempts to determine the subnet to use with