github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/joyent/environ_firewall.go (about)

     1  // Copyright 2013 Joyent Inc.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package joyent
     5  
     6  import (
     7  	"fmt"
     8  	"regexp"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/joyent/gosdc/cloudapi"
    13  
    14  	"github.com/juju/juju/environs/config"
    15  	"github.com/juju/juju/network"
    16  )
    17  
    18  const (
    19  	firewallRuleAll = "FROM tag %s TO tag juju ALLOW %s %s"
    20  )
    21  
    22  var (
    23  	firewallSinglePortRule = regexp.MustCompile("FROM tag [a-z0-9 \\-]+ TO (?:tag|vm) [a-z0-9 \\-]+ ALLOW (?P<protocol>[a-z]+) PORT (?P<port>[0-9]+)")
    24  	firewallMultiPortRule  = regexp.MustCompile("FROM tag [a-z0-9 \\-]+ TO (?:tag|vm) [a-z0-9 \\-]+ ALLOW (?P<protocol>[a-z]+) \\(\\s*(?P<ports>PORT [0-9]+(?: AND PORT [0-9]+)*)\\s*\\)")
    25  )
    26  
    27  // Helper method to create a firewall rule string for the given port
    28  func createFirewallRuleAll(envName string, portRange network.PortRange) string {
    29  	ports := []string{}
    30  	for p := portRange.FromPort; p <= portRange.ToPort; p++ {
    31  		ports = append(ports, fmt.Sprintf("PORT %d", p))
    32  	}
    33  	var portList string
    34  	if len(ports) > 1 {
    35  		portList = fmt.Sprintf("( %s )", strings.Join(ports, " AND "))
    36  	} else if len(ports) == 1 {
    37  		portList = ports[0]
    38  	}
    39  	return fmt.Sprintf(firewallRuleAll, envName, strings.ToLower(portRange.Protocol), portList)
    40  }
    41  
    42  // Helper method to check if a firewall rule string already exist
    43  func ruleExists(rules []cloudapi.FirewallRule, rule string) (bool, string) {
    44  	for _, r := range rules {
    45  		if strings.EqualFold(r.Rule, rule) {
    46  			return true, r.Id
    47  		}
    48  	}
    49  
    50  	return false, ""
    51  }
    52  
    53  // Helper method to get port from the given firewall rules
    54  func getPorts(envName string, rules []cloudapi.FirewallRule) []network.PortRange {
    55  	portRanges := []network.PortRange{}
    56  	for _, r := range rules {
    57  		rule := r.Rule
    58  		if r.Enabled && strings.HasPrefix(rule, "FROM tag "+envName) && strings.Contains(rule, "PORT") {
    59  			if firewallSinglePortRule.MatchString(rule) {
    60  				parts := firewallSinglePortRule.FindStringSubmatch(rule)
    61  				if len(parts) != 3 {
    62  					continue
    63  				}
    64  				protocol := parts[1]
    65  				n, _ := strconv.Atoi(parts[2])
    66  				portRanges = append(portRanges, network.PortRange{Protocol: protocol, FromPort: n, ToPort: n})
    67  			} else if firewallMultiPortRule.MatchString(rule) {
    68  				parts := firewallMultiPortRule.FindStringSubmatch(rule)
    69  				if len(parts) != 3 {
    70  					continue
    71  				}
    72  				protocol := parts[1]
    73  				ports := []network.Port{}
    74  				portStrings := strings.Split(parts[2], " AND ")
    75  				for _, portString := range portStrings {
    76  					portString = portString[strings.LastIndex(portString, "PORT")+5:]
    77  					port, _ := strconv.Atoi(portString)
    78  					ports = append(ports, network.Port{protocol, port})
    79  				}
    80  				portRanges = append(portRanges, network.CollapsePorts(ports)...)
    81  			}
    82  		}
    83  	}
    84  
    85  	network.SortPortRanges(portRanges)
    86  	return portRanges
    87  }
    88  
    89  func (env *joyentEnviron) OpenPorts(ports []network.PortRange) error {
    90  	if env.Config().FirewallMode() != config.FwGlobal {
    91  		return fmt.Errorf("invalid firewall mode %q for opening ports on model", env.Config().FirewallMode())
    92  	}
    93  
    94  	fwRules, err := env.compute.cloudapi.ListFirewallRules()
    95  	if err != nil {
    96  		return fmt.Errorf("cannot get firewall rules: %v", err)
    97  	}
    98  
    99  	for _, p := range ports {
   100  		rule := createFirewallRuleAll(env.Config().Name(), p)
   101  		if e, id := ruleExists(fwRules, rule); e {
   102  			_, err := env.compute.cloudapi.EnableFirewallRule(id)
   103  			if err != nil {
   104  				return fmt.Errorf("couldn't enable rule %s: %v", rule, err)
   105  			}
   106  		} else {
   107  			_, err := env.compute.cloudapi.CreateFirewallRule(cloudapi.CreateFwRuleOpts{
   108  				Enabled: true,
   109  				Rule:    rule,
   110  			})
   111  			if err != nil {
   112  				return fmt.Errorf("couldn't create rule %s: %v", rule, err)
   113  			}
   114  		}
   115  	}
   116  
   117  	logger.Infof("ports %v opened in model", ports)
   118  
   119  	return nil
   120  }
   121  
   122  func (env *joyentEnviron) ClosePorts(ports []network.PortRange) error {
   123  	if env.Config().FirewallMode() != config.FwGlobal {
   124  		return fmt.Errorf("invalid firewall mode %q for closing ports on model", env.Config().FirewallMode())
   125  	}
   126  
   127  	fwRules, err := env.compute.cloudapi.ListFirewallRules()
   128  	if err != nil {
   129  		return fmt.Errorf("cannot get firewall rules: %v", err)
   130  	}
   131  
   132  	for _, p := range ports {
   133  		rule := createFirewallRuleAll(env.Config().Name(), p)
   134  		if e, id := ruleExists(fwRules, rule); e {
   135  			_, err := env.compute.cloudapi.DisableFirewallRule(id)
   136  			if err != nil {
   137  				return fmt.Errorf("couldn't disable rule %s: %v", rule, err)
   138  			}
   139  		} else {
   140  			_, err := env.compute.cloudapi.CreateFirewallRule(cloudapi.CreateFwRuleOpts{
   141  				Enabled: false,
   142  				Rule:    rule,
   143  			})
   144  			if err != nil {
   145  				return fmt.Errorf("couldn't create rule %s: %v", rule, err)
   146  			}
   147  		}
   148  	}
   149  
   150  	logger.Infof("ports %v closed in model", ports)
   151  
   152  	return nil
   153  }
   154  
   155  func (env *joyentEnviron) Ports() ([]network.PortRange, error) {
   156  	if env.Config().FirewallMode() != config.FwGlobal {
   157  		return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from model", env.Config().FirewallMode())
   158  	}
   159  
   160  	fwRules, err := env.compute.cloudapi.ListFirewallRules()
   161  	if err != nil {
   162  		return nil, fmt.Errorf("cannot get firewall rules: %v", err)
   163  	}
   164  
   165  	return getPorts(env.Config().Name(), fwRules), nil
   166  }