github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/firewall/incoming_firewall_iptables.go (about) 1 /* 2 * Copyright (C) 2020 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 firewall 19 20 import ( 21 "net" 22 "net/url" 23 "strings" 24 "time" 25 26 "github.com/mysteriumnetwork/node/firewall/ipset" 27 "github.com/mysteriumnetwork/node/firewall/iptables" 28 "github.com/rs/zerolog/log" 29 ) 30 31 const ( 32 incomingFirewallChain = "MYST_PROVIDER_FIREWALL" 33 incomingFirewallIpset = "myst-provider-dst-whitelist" 34 ) 35 36 // incomingFirewallIptables allows incoming traffic blocking in IP granularity. 37 type incomingFirewallIptables struct{} 38 39 func (ibi *incomingFirewallIptables) Setup() error { 40 if err := ibi.checkIpsetVersion(); err != nil { 41 return err 42 } 43 44 // Clean up setups from previous runs, just in case 45 if err := ibi.cleanupStaleRules(); err != nil { 46 return err 47 } 48 ipset.Exec(ipset.OpDelete(incomingFirewallIpset)) 49 50 op := ipset.OpCreate(incomingFirewallIpset, ipset.SetTypeHashIP, 24*time.Hour, nil, 0) 51 if _, err := ipset.Exec(op); err != nil { 52 return err 53 } 54 return ibi.setupFirewallChain() 55 } 56 57 func (ibi *incomingFirewallIptables) Teardown() { 58 if err := ibi.cleanupStaleRules(); err != nil { 59 log.Warn().Err(err).Msg("Error cleaning up iptables rules, you might want to do it yourself") 60 } 61 if errOutput, err := ipset.Exec(ipset.OpDelete(incomingFirewallIpset)); err != nil { 62 log.Warn().Err(err).Msgf("Error deleting ipset table. %s", strings.Join(errOutput, "")) 63 } 64 } 65 66 func (ibi *incomingFirewallIptables) BlockIncomingTraffic(network net.IPNet) (IncomingRuleRemove, error) { 67 remover, err := iptables.AddRuleWithRemoval( 68 iptables.AppendTo("FORWARD").RuleSpec("-s", network.String(), "-j", incomingFirewallChain), 69 ) 70 if err != nil { 71 return nil, err 72 } 73 return func() error { 74 remover() 75 return nil 76 }, nil 77 } 78 79 // AllowURLAccess adds URL based exception. 80 func (ibi *incomingFirewallIptables) AllowURLAccess(rawURLs ...string) (IncomingRuleRemove, error) { 81 var ruleRemovers []func() 82 removeAll := func() error { 83 for _, ruleRemover := range ruleRemovers { 84 ruleRemover() 85 } 86 return nil 87 } 88 89 for _, rawURL := range rawURLs { 90 parsed, err := url.Parse(rawURL) 91 if err != nil { 92 removeAll() 93 return nil, err 94 } 95 96 remover, err := iptables.AddRuleWithRemoval( 97 iptables.InsertAt(incomingFirewallChain, 1).RuleSpec("-d", parsed.Hostname(), "-j", "ACCEPT"), 98 ) 99 if err != nil { 100 removeAll() 101 return nil, err 102 } 103 ruleRemovers = append(ruleRemovers, remover) 104 } 105 return removeAll, nil 106 } 107 108 func (ibi *incomingFirewallIptables) AllowIPAccess(ip net.IP) (IncomingRuleRemove, error) { 109 if _, err := ipset.Exec(ipset.OpIPAdd(incomingFirewallIpset, ip, true)); err != nil { 110 return nil, err 111 } 112 return func() error { 113 _, err := ipset.Exec(ipset.OpIPRemove(incomingFirewallIpset, ip)) 114 return err 115 }, nil 116 } 117 118 func (ibi *incomingFirewallIptables) checkIpsetVersion() error { 119 output, err := ipset.Exec(ipset.OpVersion()) 120 if err != nil { 121 return err 122 } 123 for _, line := range output { 124 log.Info().Msg("[version check] " + line) 125 } 126 return nil 127 } 128 129 func (ibi *incomingFirewallIptables) setupFirewallChain() error { 130 // Add chain 131 if _, err := iptables.Exec("-N", incomingFirewallChain); err != nil { 132 return err 133 } 134 135 // Append rule - packets going to firewall with these destination IPs are whitelisted 136 if _, err := iptables.Exec("-A", incomingFirewallChain, "-m", "set", "--match-set", incomingFirewallIpset, "dst", "-j", "ACCEPT"); err != nil { 137 return err 138 } 139 140 // Append rule - by default all packets going to firewall chain are rejected 141 if _, err := iptables.Exec("-A", incomingFirewallChain, "-j", "REJECT"); err != nil { 142 return err 143 } 144 145 return nil 146 } 147 148 func (ibi *incomingFirewallIptables) cleanupStaleRules() error { 149 // List rules 150 rules, err := iptables.Exec("-S", "FORWARD") 151 if err != nil { 152 return err 153 } 154 for _, rule := range rules { 155 // detect if any references exist in FORWARD chain like -j MYST_PROVIDER_FIREWALL 156 if strings.HasSuffix(rule, incomingFirewallChain) { 157 deleteRule := strings.Replace(rule, "-A", "-D", 1) 158 deleteRuleArgs := strings.Split(deleteRule, " ") 159 if _, err := iptables.Exec(deleteRuleArgs...); err != nil { 160 return err 161 } 162 } 163 } 164 165 // List chain rules 166 if _, err := iptables.Exec("-L", incomingFirewallChain); err != nil { 167 // error means no such chain - log error just in case and bail out 168 log.Info().Err(err).Msg("[setup] Got error while listing kill switch chain rules. Probably nothing to worry about") 169 return nil 170 } 171 172 // Remove chain rules 173 if _, err := iptables.Exec("-F", incomingFirewallChain); err != nil { 174 return err 175 } 176 177 // Remove chain 178 _, err = iptables.Exec("-X", incomingFirewallChain) 179 return err 180 } 181 182 var _ IncomingTrafficFirewall = &incomingFirewallIptables{}