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 }