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