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  }