Skip to content

Commit

Permalink
Decouple Iptables utility from from kill switch feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Waldz committed Jan 31, 2020
1 parent 158f11d commit 90a7d48
Show file tree
Hide file tree
Showing 14 changed files with 554 additions and 686 deletions.
17 changes: 12 additions & 5 deletions cmd/di.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ import (
"github.com/mysteriumnetwork/node/eventbus"
"github.com/mysteriumnetwork/node/feedback"
"github.com/mysteriumnetwork/node/firewall"
"github.com/mysteriumnetwork/node/firewall/vnd"
"github.com/mysteriumnetwork/node/identity"
"github.com/mysteriumnetwork/node/identity/registry"
identity_registry "github.com/mysteriumnetwork/node/identity/registry"
Expand Down Expand Up @@ -822,13 +821,21 @@ func (di *Dependencies) bootstrapNATComponents(options node.Options) {
}

func (di *Dependencies) bootstrapFirewall(options node.OptionsFirewall) error {
fwVendor, err := vnd.SetupVendor()
if err != nil {
firewall.DefaultTrackingBlocker = firewall.NewTrackingBlocker()
if err := firewall.DefaultTrackingBlocker.Setup(); err != nil {
return err
}
firewall.Configure(fwVendor)

if options.BlockAlways {
_, err := firewall.BlockNonTunnelTraffic(firewall.Global)
bindAddress := "0.0.0.0"
httpClient := requests.NewHTTPClient(bindAddress, requests.DefaultTimeout)
resolver := ip.NewResolver(httpClient, bindAddress, "")
outboundIP, err := resolver.GetOutboundIPAsString()
if err != nil {
return err
}

_, err = firewall.BlockNonTunnelTraffic(firewall.Global, outboundIP)
return err
}
return nil
Expand Down
7 changes: 6 additions & 1 deletion core/connection/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,12 @@ func (manager *connectionManager) setupTrafficBlock(disableKillSwitch bool) erro
return nil
}

removeRule, err := firewall.BlockNonTunnelTraffic(firewall.Session)
outboundIP, err := manager.ipResolver.GetOutboundIPAsString()
if err != nil {
return err
}

removeRule, err := firewall.BlockNonTunnelTraffic(firewall.Session, outboundIP)
if err != nil {
return err
}
Expand Down
15 changes: 7 additions & 8 deletions firewall/vnd/setup_vendor_android.go → firewall/factory.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//+build !linux

/*
* Copyright (C) 2018 The "MysteriumNetwork/node" Authors.
* Copyright (C) 2020 The "MysteriumNetwork/node" Authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand All @@ -15,12 +17,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package vnd

import (
"github.com/mysteriumnetwork/node/firewall"
)
package firewall

func SetupVendor() (firewall.Vendor, error) {
return firewall.NoopVendor{}, nil
// NewTrackingBlocker create instance of traffic blocker
func NewTrackingBlocker() *noopTrafficBlocker {
return &noopTrafficBlocker{}
}
19 changes: 8 additions & 11 deletions firewall/vnd/setup_vendor.go → firewall/factory_linux.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
//+build !linux

/*
* Copyright (C) 2019 The "MysteriumNetwork/node" Authors.
* Copyright (C) 2020 The "MysteriumNetwork/node" Authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand All @@ -17,13 +15,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package vnd

import (
"github.com/mysteriumnetwork/node/firewall"
)
package firewall

// SetupVendor initializes default vendor or OSes which do not support it
func SetupVendor() (firewall.Vendor, error) {
return firewall.NoopVendor{}, nil
// NewTrackingBlocker create instance of traffic blocker
func NewTrackingBlocker() *iptablesTrafficBlocker {
return &iptablesTrafficBlocker{
referenceTracker: make(map[string]refCount),
trafficLockScope: none,
}
}
6 changes: 3 additions & 3 deletions firewall/iptables/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ type Rule struct {
func AppendTo(chainName string) Rule {
return Rule{
chainName: chainName,
action: []string{appendRule, chainName},
action: []string{"-A", chainName},
}
}

// InsertAt creates a new rule to be inserted into the specified chain.
func InsertAt(chainName string, line int) Rule {
return Rule{
chainName: chainName,
action: []string{insertRule, chainName, strconv.Itoa(line)},
action: []string{"-I", chainName, strconv.Itoa(line)},
}
}

Expand All @@ -55,7 +55,7 @@ func (r Rule) ApplyArgs() []string {

// RemoveArgs returns an argument list to be passed to the iptables executable to REMOVE the rule.
func (r Rule) RemoveArgs() []string {
return append([]string{removeRule, r.chainName}, r.ruleSpec...)
return append([]string{"-D", r.chainName}, r.ruleSpec...)
}

// Equals checks if two Rules are equal.
Expand Down
150 changes: 6 additions & 144 deletions firewall/iptables/iptables.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,46 +21,13 @@ import (
"bufio"
"bytes"
"os/exec"
"strings"

"github.com/mysteriumnetwork/node/firewall"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)

const (
outputChain = "OUTPUT"
killswitchChain = "CONSUMER_KILL_SWITCH"

addChain = "-N"
appendRule = "-A"
insertRule = "-I"
listRules = "-S"
removeRule = "-D"
removeChainRules = "-F"
removeChain = "-X"

jumpTo = "-j"
sourceIP = "-s"
destinationIP = "-d"
module = "-m"

protocol = "-p"
tcp = "tcp"
udp = "udp"
destinationPort = "--dport"

conntrack = "conntrack"
ctState = "--ctstate"
ctStateNew = "NEW"

reject = "REJECT"
accept = "ACCEPT"

version = "--version"
)

var iptablesExec = func(args ...string) ([]string, error) {
// Exec actives given args
var Exec = func(args ...string) ([]string, error) {
args = append([]string{"/sbin/iptables"}, args...)
log.Debug().Msgf("[cmd] %v", args)
output, err := exec.Command("sudo", args...).CombinedOutput()
Expand All @@ -76,120 +43,15 @@ var iptablesExec = func(args ...string) ([]string, error) {
return lines, outputScanner.Err()
}

func checkVersion() error {
output, err := iptablesExec(version)
if err != nil {
return err
}
for _, line := range output {
log.Info().Msg("[version check] " + line)
}
return nil
}

func cleanupStaleRules() error {
rules, err := iptablesExec(listRules, outputChain)
if err != nil {
return err
}
for _, rule := range rules {
//detect if any references exist in OUTPUT chain like -j CONSUMER_KILL_SWITCH
if strings.HasSuffix(rule, killswitchChain) {
deleteRule := strings.Replace(rule, appendRule, removeRule, 1)
deleteRuleArgs := strings.Split(deleteRule, " ")
if _, err := iptablesExec(deleteRuleArgs...); err != nil {
return err
}
}
}

if _, err := iptablesExec(listRules, killswitchChain); err != nil {
//error means no such chain - log error just in case and bail out
log.Info().Err(err).Msg("[setup] Got error while listing kill switch chain rules. Probably nothing to worry about")
return nil
}

if _, err := iptablesExec(removeChainRules, killswitchChain); err != nil {
return err
}

_, err = iptablesExec(removeChain, killswitchChain)
return err
}

func setupKillSwitchChain() error {
if _, err := iptablesExec(addChain, killswitchChain); err != nil {
return err
}
// by default all packets going to kill switch chain are rejected
if _, err := iptablesExec(appendRule, killswitchChain, module, conntrack, ctState, ctStateNew, jumpTo, reject); err != nil {
return err
}

// TODO for now always allow outgoing DNS traffic, BUT it should be exposed as separate firewall call
if _, err := iptablesExec(insertRule, killswitchChain, "1", protocol, udp, destinationPort, "53", jumpTo, accept); err != nil {
return err
}
// TCP DNS is not so popular - but for the sake of humanity, lets allow it too
if _, err := iptablesExec(insertRule, killswitchChain, "1", protocol, tcp, destinationPort, "53", jumpTo, accept); err != nil {
return err
}

return nil
}

// Iptables represent Iptables based implementation of firewall Vendor interface
type Iptables struct {
outboundIP string
}

// New initializes and returns Iptables with defined outboundIP
func New(outboundIP string) *Iptables {
return &Iptables{
outboundIP: outboundIP,
}
}

// BlockOutgoingTraffic starts blocking outgoing traffic and returns function to remove the block
func (b Iptables) BlockOutgoingTraffic() (firewall.RemoveRule, error) {
return addRuleWithRemoval(AppendTo(outputChain).RuleSpec(sourceIP, b.outboundIP, jumpTo, killswitchChain))
}

// Setup prepares Iptables default rules and chains
func (b Iptables) Setup() error {
if err := checkVersion(); err != nil {
return err
}

if err := cleanupStaleRules(); err != nil {
return err
}

return setupKillSwitchChain()
}

// Reset tries to cleanup all changes made by setup and leave system in the state before setup
func (Iptables) Reset() {
if err := cleanupStaleRules(); err != nil {
log.Warn().Err(err).Msg("Error cleaning up iptables rules, you might want to do it yourself")
}
}

func addRuleWithRemoval(rule Rule) (firewall.RemoveRule, error) {
if _, err := iptablesExec(rule.ApplyArgs()...); err != nil {
// AddRuleWithRemoval activates given rule
func AddRuleWithRemoval(rule Rule) (func(), error) {
if _, err := Exec(rule.ApplyArgs()...); err != nil {
return nil, err
}
return func() {
_, err := iptablesExec(rule.RemoveArgs()...)
_, err := Exec(rule.RemoveArgs()...)
if err != nil {
log.Warn().Err(err).Msgf("Error executing rule: %v you might wanna do it yourself", rule.RemoveArgs())
}
}, nil
}

// AllowIPAccess add ip to exceptions of blocked traffic and return function to remove exception
func (Iptables) AllowIPAccess(ip string) (firewall.RemoveRule, error) {
return addRuleWithRemoval(InsertAt(killswitchChain, 1).RuleSpec(destinationIP, ip, jumpTo, accept))
}

var _ firewall.Vendor = (*Iptables)(nil)

0 comments on commit 90a7d48

Please sign in to comment.