github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/nat/service_iptables.go (about) 1 /* 2 * Copyright (C) 2017 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 "strconv" 23 "sync" 24 25 "github.com/pkg/errors" 26 "github.com/rs/zerolog/log" 27 28 "github.com/mysteriumnetwork/node/config" 29 "github.com/mysteriumnetwork/node/firewall/iptables" 30 "github.com/mysteriumnetwork/node/utils" 31 "github.com/mysteriumnetwork/node/utils/cmdutil" 32 ) 33 34 type serviceIPTables struct { 35 mu sync.Mutex 36 rules []iptables.Rule 37 ipForward serviceIPForward 38 } 39 40 const ( 41 chainMyst = "MYST" 42 chainInput = "INPUT" 43 chainForward = "FORWARD" 44 chainPreRouting = "PREROUTING" 45 chainPostRouting = "POSTROUTING" 46 ) 47 48 // Setup sets NAT/Firewall rules for the given NATOptions. 49 func (svc *serviceIPTables) Setup(opts Options) (appliedRules []interface{}, err error) { 50 log.Info().Msg("Setting up NAT/Firewall rules") 51 svc.mu.Lock() 52 defer svc.mu.Unlock() 53 54 // Store applied rules so we can remove if setup exits prematurely (one of the latter rules fails to apply) 55 var applied []iptables.Rule 56 defer func() { 57 if err == nil { 58 return 59 } 60 log.Warn().Msg("Error detected, clearing up rules that were already setup") 61 for _, rule := range applied { 62 if err := svc.removeRule(rule); err != nil { 63 log.Error().Err(err).Msg("Could not remove rule") 64 } 65 } 66 }() 67 68 for _, rule := range makeIPTablesRules(opts) { 69 if err := svc.applyRule(rule); err != nil { 70 return nil, err 71 } 72 applied = append(applied, rule) 73 } 74 log.Info().Msg("Setting up NAT/Firewall rules... done") 75 return untypedIptRules(applied), nil 76 } 77 78 // Del removes given NAT/Firewall rules that were previously set up. 79 func (svc *serviceIPTables) Del(rules []interface{}) (err error) { 80 log.Info().Msg("Deleting NAT/Firewall rules") 81 svc.mu.Lock() 82 defer svc.mu.Unlock() 83 84 errs := utils.ErrorCollection{} 85 for _, rule := range typedIptRules(rules) { 86 log.Trace().Msgf("Deleting rule: %v", rule) 87 if err := svc.removeRule(rule); err != nil { 88 errs.Add(err) 89 } 90 } 91 err = errs.Error() 92 log.Info().Err(err).Msg("Deleting NAT/Firewall rules... done") 93 return err 94 } 95 96 // Enable enables NAT service. 97 func (svc *serviceIPTables) Enable() error { 98 if config.GetBool(config.FlagUserMode) || config.GetBool(config.FlagUserspace) { 99 log.Info().Msg("Usermode active, nothing to do with iptables") 100 return nil 101 } 102 103 err := svc.prepare() 104 if err != nil { 105 log.Warn().Err(err).Msg("Failed to prepare iptables setup") 106 } 107 108 err = svc.ipForward.Enable() 109 if err != nil { 110 log.Warn().Err(err).Msg("Failed to enable IP forwarding") 111 } 112 return err 113 } 114 115 // Disable disables NAT service and deletes all rules. 116 func (svc *serviceIPTables) Disable() error { 117 if config.GetBool(config.FlagUserMode) || config.GetBool(config.FlagUserspace) { 118 log.Info().Msg("Usermode active, nothing to do with iptables") 119 return nil 120 } 121 122 svc.ipForward.Disable() 123 err := svc.Del(untypedIptRules(svc.rules)) 124 if err != nil { 125 return fmt.Errorf("failed to cleanup iptables rules") 126 } 127 128 err = svc.clean() 129 if err != nil { 130 return fmt.Errorf("failed to cleanup iptables chains") 131 } 132 133 return nil 134 } 135 136 func (svc *serviceIPTables) applyRule(rule iptables.Rule) error { 137 if err := iptablesExec(rule.ApplyArgs()...); err != nil { 138 return err 139 } 140 svc.rules = append(svc.rules, rule) 141 return nil 142 } 143 144 func (svc *serviceIPTables) removeRule(rule iptables.Rule) error { 145 if err := iptablesExec(rule.RemoveArgs()...); err != nil { 146 return err 147 } 148 for i := range svc.rules { 149 if svc.rules[i].Equals(rule) { 150 svc.rules = append(svc.rules[:i], svc.rules[i+1:]...) 151 break 152 } 153 } 154 return nil 155 } 156 157 func (svc *serviceIPTables) prepare() error { 158 err := iptablesExec("--new", chainMyst, "--table", "nat") 159 if err != nil { 160 return fmt.Errorf("failed to create MYST iptables chain: %w", err) 161 } 162 163 for _, ipNet := range protectedNetworks() { 164 // Protect private networks rule 165 err = svc.applyRule(iptables.AppendTo(chainMyst).RuleSpec( 166 "--destination", ipNet.String(), "--jump", "DNAT", "--to-destination", "240.0.0.1", "--table", "nat")) 167 if err != nil { 168 return fmt.Errorf("failed to create blackhole rule in the MYST iptables chain: %w", err) 169 } 170 } 171 172 return nil 173 } 174 175 func (svc *serviceIPTables) clean() error { 176 err := iptablesExec("--flush", chainMyst, "--table", "nat") 177 if err != nil { 178 return fmt.Errorf("failed to flush MYST iptables chain: %w", err) 179 } 180 181 err = iptablesExec("--delete-chain", chainMyst, "--table", "nat") 182 if err != nil { 183 return fmt.Errorf("failed to delete MYST iptables chain: %w", err) 184 } 185 186 return nil 187 } 188 189 func makeIPTablesRules(opts Options) (rules []iptables.Rule) { 190 vpnNetwork := opts.VPNNetwork.String() 191 192 rule := iptables.InsertAt(chainPreRouting, 1).RuleSpec( 193 "--source", vpnNetwork, "--jump", chainMyst, "--table", "nat") 194 rules = append(rules, rule) 195 196 // DNS port redirect rule (udp) 197 rule = iptables.InsertAt(chainMyst, 1).RuleSpec( 198 "--destination", opts.DNSIP.String(), "--protocol", "udp", "--dport", strconv.Itoa(53), 199 "--jump", "REDIRECT", 200 "--to-ports", strconv.Itoa(config.GetInt(config.FlagDNSListenPort)), 201 "--table", "nat", 202 ) 203 rules = append(rules, rule) 204 205 // DNS port redirect rule (tcp) 206 rule = iptables.InsertAt(chainMyst, 1).RuleSpec( 207 "--destination", opts.DNSIP.String(), "--protocol", "tcp", "--dport", strconv.Itoa(53), 208 "--jump", "REDIRECT", 209 "--to-ports", strconv.Itoa(config.GetInt(config.FlagDNSListenPort)), 210 "--table", "nat", 211 ) 212 rules = append(rules, rule) 213 214 // NAT forwarding rule 215 rule = iptables.AppendTo(chainPostRouting).RuleSpec("--source", vpnNetwork, "!", "--destination", vpnNetwork, 216 "--jump", "SNAT", "--to", opts.ProviderExtIP.String(), 217 "--table", "nat") 218 rules = append(rules, rule) 219 220 // ACCEPT forwarding rules 221 rules = append(rules, iptables.AppendTo(chainForward).RuleSpec("--source", vpnNetwork, "--jump", "ACCEPT")) 222 rules = append(rules, iptables.AppendTo(chainForward).RuleSpec("--destination", vpnNetwork, "--jump", "ACCEPT")) 223 224 return rules 225 } 226 227 func iptablesExec(args ...string) error { 228 args = append([]string{"/usr/sbin/iptables"}, args...) 229 if err := cmdutil.SudoExec(args...); err != nil { 230 return errors.Wrap(err, "error calling IPTables") 231 } 232 return nil 233 } 234 235 func untypedIptRules(rules []iptables.Rule) []interface{} { 236 res := make([]interface{}, len(rules)) 237 for i := range rules { 238 res[i] = rules[i] 239 } 240 return res 241 } 242 243 func typedIptRules(rules []interface{}) []iptables.Rule { 244 res := make([]iptables.Rule, len(rules)) 245 for i := range rules { 246 res[i] = rules[i].(iptables.Rule) 247 } 248 return res 249 }