github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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 9 "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" 10 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork" 11 "github.com/juju/errors" 12 13 "github.com/juju/juju/provider/azure/internal/armtemplates" 14 ) 15 16 const ( 17 // internalNetworkName is the name of the virtual network that all 18 // Juju machines within a resource group are connected to. 19 // 20 // Each resource group is given its own network, subnet and network 21 // security group to manage. Each resource group will have its own 22 // private 192.168.0.0/16 network. 23 internalNetworkName = "juju-internal-network" 24 25 // internalSecurityGroupName is the name of the network security 26 // group that each machine's primary (internal network) NIC is 27 // attached to. 28 internalSecurityGroupName = "juju-internal-nsg" 29 30 // internalSubnetName is the name of the subnet that each 31 // non-controller machine's primary NIC is attached to. 32 internalSubnetName = "juju-internal-subnet" 33 34 // internalSubnetPrefix is the address prefix for the subnet that 35 // each non-controller machine's primary NIC is attached to. 36 internalSubnetPrefix = "192.168.0.0/20" 37 38 // controllerSubnetName is the name of the subnet that each controller 39 // machine's primary NIC is attached to. 40 controllerSubnetName = "juju-controller-subnet" 41 42 // controllerSubnetPrefix is the address prefix for the subnet that 43 // each controller machine's primary NIC is attached to. 44 controllerSubnetPrefix = "192.168.16.0/20" 45 ) 46 47 const ( 48 // securityRuleInternalMin is the beginning of the range of 49 // internal security group rules defined by Juju. 50 securityRuleInternalMin = 100 51 52 // securityRuleInternalMax is the end of the range of internal 53 // security group rules defined by Juju. 54 securityRuleInternalMax = 199 55 56 // securityRuleMax is the maximum allowable security rule 57 // priority. 58 securityRuleMax = 4096 59 ) 60 61 const ( 62 // securityRuleInternalSSHInbound is the priority of the 63 // security rule that allows inbound SSH access to all 64 // machines. 65 securityRuleInternalSSHInbound = securityRuleInternalMin + iota 66 67 // securityRuleInternalAPIInbound is the priority of the 68 // security rule that allows inbound Juju API access to 69 // controller machines 70 securityRuleInternalAPIInbound 71 ) 72 73 const ( 74 apiSecurityRulePrefix = "JujuAPIInbound" 75 sshSecurityRuleName = "SSHInbound" 76 ) 77 78 // newSecurityRule returns a security rule with the given parameters. 79 func newSecurityRule(p newSecurityRuleParams) *armnetwork.SecurityRule { 80 return &armnetwork.SecurityRule{ 81 Name: to.Ptr(p.name), 82 Properties: &armnetwork.SecurityRulePropertiesFormat{ 83 Description: to.Ptr(p.description), 84 Protocol: to.Ptr(armnetwork.SecurityRuleProtocolTCP), 85 SourceAddressPrefix: to.Ptr("*"), 86 SourcePortRange: to.Ptr("*"), 87 DestinationAddressPrefix: to.Ptr(p.destPrefix), 88 DestinationPortRange: to.Ptr(fmt.Sprint(p.port)), 89 Access: to.Ptr(armnetwork.SecurityRuleAccessAllow), 90 Priority: to.Ptr(int32(p.priority)), 91 Direction: to.Ptr(armnetwork.SecurityRuleDirectionInbound), 92 }, 93 } 94 } 95 96 // newSecurityRuleParams holds parameters for calling newSecurityRule, like the 97 // rule name, description, the destination address prefix, port and priority. 98 type newSecurityRuleParams struct { 99 name string 100 description string 101 destPrefix string 102 port int 103 priority int 104 } 105 106 // networkTemplateResources returns resource definitions for creating network 107 // resources shared by all machines in a model. 108 // 109 // If apiPort is -1, then there should be no controller subnet created, and 110 // no network security rule allowing Juju API traffic. 111 func networkTemplateResources( 112 location string, 113 envTags map[string]string, 114 apiPorts []int, 115 extraRules []*armnetwork.SecurityRule, 116 ) ([]armtemplates.Resource, []string) { 117 securityRules := networkSecurityRules(apiPorts, extraRules) 118 nsgID := fmt.Sprintf( 119 `[resourceId('Microsoft.Network/networkSecurityGroups', '%s')]`, 120 internalSecurityGroupName, 121 ) 122 resources := []armtemplates.Resource{{ 123 APIVersion: networkAPIVersion, 124 Type: "Microsoft.Network/networkSecurityGroups", 125 Name: internalSecurityGroupName, 126 Location: location, 127 Tags: envTags, 128 Properties: &armnetwork.SecurityGroupPropertiesFormat{ 129 SecurityRules: securityRules, 130 }, 131 }} 132 subnets := []*armnetwork.Subnet{{ 133 Name: to.Ptr(internalSubnetName), 134 Properties: &armnetwork.SubnetPropertiesFormat{ 135 AddressPrefix: to.Ptr(internalSubnetPrefix), 136 NetworkSecurityGroup: &armnetwork.SecurityGroup{ 137 ID: to.Ptr(nsgID), 138 }, 139 }, 140 }} 141 addressPrefixes := []*string{to.Ptr(internalSubnetPrefix)} 142 if len(apiPorts) > 0 { 143 addressPrefixes = append(addressPrefixes, to.Ptr(controllerSubnetPrefix)) 144 subnets = append(subnets, &armnetwork.Subnet{ 145 Name: to.Ptr(controllerSubnetName), 146 Properties: &armnetwork.SubnetPropertiesFormat{ 147 AddressPrefix: to.Ptr(controllerSubnetPrefix), 148 NetworkSecurityGroup: &armnetwork.SecurityGroup{ 149 ID: to.Ptr(nsgID), 150 }, 151 }, 152 }) 153 } 154 resources = append(resources, armtemplates.Resource{ 155 APIVersion: networkAPIVersion, 156 Type: "Microsoft.Network/virtualNetworks", 157 Name: internalNetworkName, 158 Location: location, 159 Tags: envTags, 160 Properties: &armnetwork.VirtualNetworkPropertiesFormat{ 161 AddressSpace: &armnetwork.AddressSpace{addressPrefixes}, 162 Subnets: subnets, 163 }, 164 DependsOn: []string{nsgID}, 165 }) 166 return resources, []string{nsgID} 167 } 168 169 // networkSecurityRules creates network security rules for the environment. 170 func networkSecurityRules( 171 apiPorts []int, 172 extraRules []*armnetwork.SecurityRule, 173 ) []*armnetwork.SecurityRule { 174 securityRules := []*armnetwork.SecurityRule{newSecurityRule(newSecurityRuleParams{ 175 name: sshSecurityRuleName, 176 description: "Allow SSH access to all machines", 177 destPrefix: "*", 178 port: 22, 179 priority: securityRuleInternalSSHInbound, 180 })} 181 for i, apiPort := range apiPorts { 182 securityRules = append(securityRules, newSecurityRule(newSecurityRuleParams{ 183 // Two rules cannot have the same name. 184 name: fmt.Sprintf("%s%d", apiSecurityRulePrefix, apiPort), 185 description: "Allow API connections to controller machines", 186 destPrefix: controllerSubnetPrefix, 187 port: apiPort, 188 // Two rules cannot have the same priority and direction. 189 priority: securityRuleInternalAPIInbound + i, 190 })) 191 } 192 securityRules = append(securityRules, extraRules...) 193 return securityRules 194 } 195 196 // nextSecurityRulePriority returns the next available priority in the given 197 // security group within a specified range. 198 func nextSecurityRulePriority(group *armnetwork.SecurityGroup, min, max int32) (int32, error) { 199 if group.Properties == nil { 200 return min, nil 201 } 202 for p := min; p <= max; p++ { 203 var found bool 204 for _, rule := range group.Properties.SecurityRules { 205 if rule.Properties == nil { 206 continue 207 } 208 if toValue(rule.Properties.Priority) == p { 209 found = true 210 break 211 } 212 } 213 if !found { 214 return p, nil 215 } 216 } 217 return -1, errors.Errorf( 218 "no priorities available in the range [%d, %d]", min, max, 219 ) 220 }