github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/nat/service_pfctl.go (about) 1 /* 2 * Copyright (C) 2018 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package nat 19 20 import ( 21 "fmt" 22 "net" 23 "strings" 24 "sync" 25 26 "github.com/pkg/errors" 27 "github.com/rs/zerolog/log" 28 29 "github.com/mysteriumnetwork/node/config" 30 "github.com/mysteriumnetwork/node/utils/cmdutil" 31 ) 32 33 type servicePFCtl struct { 34 mu sync.Mutex 35 rules []string 36 ipForward serviceIPForward 37 } 38 39 // Setup sets NAT/Firewall rules for the given NATOptions. 40 func (service *servicePFCtl) Setup(opts Options) (appliedRules []interface{}, err error) { 41 log.Info().Msg("Setting up NAT/Firewall rules") 42 service.mu.Lock() 43 defer service.mu.Unlock() 44 45 rules, err := makePfctlRules(opts) 46 if err != nil { 47 return nil, err 48 } 49 50 if err := service.pfctlExec(rules); err != nil { 51 return nil, err 52 } 53 service.rules = append(service.rules, rules...) 54 55 return untypedPfctlRules(rules), nil 56 } 57 58 func (service *servicePFCtl) Del(rules []interface{}) error { 59 log.Info().Msg("Deleting NAT/Firewall rules") 60 service.mu.Lock() 61 defer service.mu.Unlock() 62 63 rulesToDel := typedPfctlRules(rules) 64 var newRules []string 65 for _, rule := range service.rules { 66 shouldDelete := false 67 for _, ruleToDel := range rulesToDel { 68 if rule == ruleToDel { 69 shouldDelete = true 70 break 71 } 72 } 73 74 if !shouldDelete { 75 newRules = append(newRules, rule) 76 } 77 } 78 79 if err := service.pfctlExec(newRules); err != nil { 80 return err 81 } 82 83 service.rules = newRules 84 log.Info().Msg("Deleting NAT/Firewall rules... done") 85 return nil 86 } 87 88 func makePfctlRules(opts Options) (rules []string, err error) { 89 externalIface, err := ifaceByAddress(opts.ProviderExtIP) 90 if err != nil { 91 return nil, err 92 } 93 94 // DNS port redirect rule 95 tunnelIface, err := ifaceByAddress(opts.DNSIP) 96 if err != nil { 97 return nil, err 98 } 99 rule := fmt.Sprintf("rdr pass on %s inet proto { udp, tcp } from any to %s port 53 -> %s port %d", 100 tunnelIface, 101 opts.VPNNetwork.String(), 102 opts.DNSIP, 103 config.GetInt(config.FlagDNSListenPort), 104 ) 105 rules = append(rules, rule) 106 107 // Protect private networks rule 108 networks := protectedNetworks() 109 if len(networks) > 0 { 110 var targets []string 111 for _, network := range networks { 112 targets = append(targets, network.String()) 113 } 114 rule := fmt.Sprintf("no nat on %s inet from %s to { %s }", 115 externalIface, 116 opts.VPNNetwork.String(), 117 strings.Join(targets, ", "), 118 ) 119 rules = append(rules, rule) 120 } 121 122 // NAT forwarding rule 123 rule = fmt.Sprintf("nat on %s inet from %s to any -> %s", 124 externalIface, 125 opts.VPNNetwork.String(), 126 opts.ProviderExtIP, 127 ) 128 rules = append(rules, rule) 129 130 return rules, nil 131 } 132 133 // Enable enables NAT service. 134 func (service *servicePFCtl) Enable() error { 135 err := service.ipForward.Enable() 136 if err != nil { 137 log.Warn().Err(err).Msg("Failed to enable IP forwarding") 138 } 139 return err 140 } 141 142 // Disable disables NAT service and deletes all rules. 143 func (service *servicePFCtl) Disable() error { 144 service.mu.Lock() 145 defer service.mu.Unlock() 146 147 service.ipForward.Disable() 148 service.rules = nil 149 service.disableRules() 150 return nil 151 } 152 153 func ifaceByAddress(ip net.IP) (string, error) { 154 ifaces, err := net.Interfaces() 155 if err != nil { 156 return "", err 157 } 158 159 for _, ifi := range ifaces { 160 addresses, err := ifi.Addrs() 161 if err != nil { 162 return "", err 163 } 164 for _, address := range addresses { 165 if address.(*net.IPNet).Contains(ip) { 166 return ifi.Name, nil 167 } 168 } 169 } 170 return "", errors.New("not able to determine outbound ethernet interface for IP: " + ip.String()) 171 } 172 173 func (service *servicePFCtl) pfctlExec(rules []string) error { 174 natRule := strings.Join(rules, "\n") 175 arguments := fmt.Sprintf(`echo "%v" | /sbin/pfctl -vEf -`, natRule) 176 177 if output, err := cmdutil.ExecOutput("sh", "-c", arguments); err != nil { 178 if !strings.Contains(output, natRule) { 179 log.Warn().Err(err).Msgf("Failed to create pfctl rule") 180 return err 181 } 182 } 183 log.Info().Msg("NAT rules applied") 184 return nil 185 } 186 187 func (service *servicePFCtl) disableRules() { 188 _, err := cmdutil.ExecOutput("/sbin/pfctl", "-F", "nat") 189 if err != nil { 190 log.Warn().Err(err).Msgf("Failed cleanup NAT rules (pfctl)") 191 } else { 192 log.Info().Msg("NAT rules cleared") 193 } 194 } 195 196 func untypedPfctlRules(rules []string) []interface{} { 197 res := make([]interface{}, len(rules)) 198 for i := range rules { 199 res[i] = rules[i] 200 } 201 return res 202 } 203 204 func typedPfctlRules(rules []interface{}) []string { 205 res := make([]string, len(rules)) 206 for i := range rules { 207 res[i] = rules[i].(string) 208 } 209 return res 210 }