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 }