github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/azure/networking.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package azure 5 6 import ( 7 "fmt" 8 "net" 9 "strconv" 10 11 "github.com/Azure/azure-sdk-for-go/arm/network" 12 "github.com/Azure/go-autorest/autorest/to" 13 "github.com/juju/errors" 14 15 "github.com/juju/juju/provider/azure/internal/armtemplates" 16 "github.com/juju/juju/provider/azure/internal/iputils" 17 ) 18 19 const ( 20 // internalNetworkName is the name of the virtual network that all 21 // Juju machines within a resource group are connected to. 22 // 23 // Each resource group is given its own network, subnet and network 24 // security group to manage. Each resource group will have its own 25 // private 192.168.0.0/16 network. 26 internalNetworkName = "juju-internal-network" 27 28 // internalSecurityGroupName is the name of the network security 29 // group that each machine's primary (internal network) NIC is 30 // attached to. 31 internalSecurityGroupName = "juju-internal-nsg" 32 33 // internalSubnetName is the name of the subnet that each 34 // non-controller machine's primary NIC is attached to. 35 internalSubnetName = "juju-internal-subnet" 36 37 // internalSubnetPrefix is the address prefix for the subnet that 38 // each non-controller machine's primary NIC is attached to. 39 internalSubnetPrefix = "192.168.0.0/20" 40 41 // controllerSubnetName is the name of the subnet that each controller 42 // machine's primary NIC is attached to. 43 controllerSubnetName = "juju-controller-subnet" 44 45 // controllerSubnetPrefix is the address prefix for the subnet that 46 // each controller machine's primary NIC is attached to. 47 controllerSubnetPrefix = "192.168.16.0/20" 48 ) 49 50 const ( 51 // securityRuleInternalMin is the beginning of the range of 52 // internal security group rules defined by Juju. 53 securityRuleInternalMin = 100 54 55 // securityRuleInternalMax is the end of the range of internal 56 // security group rules defined by Juju. 57 securityRuleInternalMax = 199 58 59 // securityRuleMax is the maximum allowable security rule 60 // priority. 61 securityRuleMax = 4096 62 ) 63 64 const ( 65 // securityRuleInternalSSHInbound is the priority of the 66 // security rule that allows inbound SSH access to all 67 // machines. 68 securityRuleInternalSSHInbound = securityRuleInternalMin + iota 69 70 // securityRuleInternalAPIInbound is the priority of the 71 // security rule that allows inbound Juju API access to 72 // controller machines 73 securityRuleInternalAPIInbound 74 ) 75 76 var ( 77 sshSecurityRule = network.SecurityRule{ 78 Name: to.StringPtr("SSHInbound"), 79 Properties: &network.SecurityRulePropertiesFormat{ 80 Description: to.StringPtr("Allow SSH access to all machines"), 81 Protocol: network.TCP, 82 SourceAddressPrefix: to.StringPtr("*"), 83 SourcePortRange: to.StringPtr("*"), 84 DestinationAddressPrefix: to.StringPtr("*"), 85 DestinationPortRange: to.StringPtr("22"), 86 Access: network.Allow, 87 Priority: to.Int32Ptr(securityRuleInternalSSHInbound), 88 Direction: network.Inbound, 89 }, 90 } 91 92 apiSecurityRule = network.SecurityRule{ 93 Name: to.StringPtr("JujuAPIInbound"), 94 Properties: &network.SecurityRulePropertiesFormat{ 95 Description: to.StringPtr("Allow API connections to controller machines"), 96 Protocol: network.TCP, 97 SourceAddressPrefix: to.StringPtr("*"), 98 SourcePortRange: to.StringPtr("*"), 99 DestinationAddressPrefix: to.StringPtr(controllerSubnetPrefix), 100 // DestinationPortRange is set by createInternalNetworkSecurityGroup. 101 Access: network.Allow, 102 Priority: to.Int32Ptr(securityRuleInternalAPIInbound), 103 Direction: network.Inbound, 104 }, 105 } 106 ) 107 108 // networkTemplateResources returns resource definitions for creating network 109 // resources shared by all machines in a model. 110 func networkTemplateResources( 111 location string, 112 envTags map[string]string, 113 apiPort int, 114 ) []armtemplates.Resource { 115 // Create a network security group for the environment. There is only 116 // one NSG per environment (there's a limit of 100 per subscription), 117 // in which we manage rules for each exposed machine. 118 apiSecurityRule := apiSecurityRule 119 properties := *apiSecurityRule.Properties 120 properties.DestinationPortRange = to.StringPtr(fmt.Sprint(apiPort)) 121 apiSecurityRule.Properties = &properties 122 securityRules := []network.SecurityRule{sshSecurityRule, apiSecurityRule} 123 124 // NOTE(axw) we create the API rule for all models to avoid having to 125 // make queries when creating resources, making deployment faster and 126 // more robust. The controller subnet is never used in non-controller 127 // models, so there are no security implications. 128 nsgId := fmt.Sprintf( 129 `[resourceId('Microsoft.Network/networkSecurityGroups', '%s')]`, 130 internalSecurityGroupName, 131 ) 132 subnets := []network.Subnet{{ 133 Name: to.StringPtr(internalSubnetName), 134 Properties: &network.SubnetPropertiesFormat{ 135 AddressPrefix: to.StringPtr(internalSubnetPrefix), 136 NetworkSecurityGroup: &network.SecurityGroup{ 137 ID: to.StringPtr(nsgId), 138 }, 139 }, 140 }, { 141 Name: to.StringPtr(controllerSubnetName), 142 Properties: &network.SubnetPropertiesFormat{ 143 AddressPrefix: to.StringPtr(controllerSubnetPrefix), 144 NetworkSecurityGroup: &network.SecurityGroup{ 145 ID: to.StringPtr(nsgId), 146 }, 147 }, 148 }} 149 150 addressPrefixes := []string{internalSubnetPrefix, controllerSubnetPrefix} 151 resources := []armtemplates.Resource{{ 152 APIVersion: network.APIVersion, 153 Type: "Microsoft.Network/networkSecurityGroups", 154 Name: internalSecurityGroupName, 155 Location: location, 156 Tags: envTags, 157 Properties: &network.SecurityGroupPropertiesFormat{ 158 SecurityRules: &securityRules, 159 }, 160 }, { 161 APIVersion: network.APIVersion, 162 Type: "Microsoft.Network/virtualNetworks", 163 Name: internalNetworkName, 164 Location: location, 165 Tags: envTags, 166 Properties: &network.VirtualNetworkPropertiesFormat{ 167 AddressSpace: &network.AddressSpace{&addressPrefixes}, 168 Subnets: &subnets, 169 }, 170 DependsOn: []string{nsgId}, 171 }} 172 return resources 173 } 174 175 // nextSecurityRulePriority returns the next available priority in the given 176 // security group within a specified range. 177 func nextSecurityRulePriority(group network.SecurityGroup, min, max int32) (int32, error) { 178 if group.Properties.SecurityRules == nil { 179 return min, nil 180 } 181 for p := min; p <= max; p++ { 182 var found bool 183 for _, rule := range *group.Properties.SecurityRules { 184 if to.Int32(rule.Properties.Priority) == p { 185 found = true 186 break 187 } 188 } 189 if !found { 190 return p, nil 191 } 192 } 193 return -1, errors.Errorf( 194 "no priorities available in the range [%d, %d]", min, max, 195 ) 196 } 197 198 // machineSubnetIP returns the private IP address to use for the given 199 // subnet prefix. 200 func machineSubnetIP(subnetPrefix, machineId string) (net.IP, error) { 201 _, ipnet, err := net.ParseCIDR(subnetPrefix) 202 if err != nil { 203 return nil, errors.Annotate(err, "parsing subnet prefix") 204 } 205 n, err := strconv.Atoi(machineId) 206 if err != nil { 207 return nil, errors.Annotate(err, "parsing machine ID") 208 } 209 ip := iputils.NthSubnetIP(ipnet, n) 210 if ip == nil { 211 // TODO(axw) getting nil means we've cycled through roughly 212 // 2^12 machines. To work around this limitation, we must 213 // maintain an in-memory set of in-use IP addresses for each 214 // subnet. 215 return nil, errors.Errorf( 216 "no available IP addresses in %s", subnetPrefix, 217 ) 218 } 219 return ip, nil 220 }