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  }