github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/firewall/outgoing_firewall_iptables_test.go (about)

     1  /*
     2   * Copyright (C) 2019 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  	"testing"
    22  
    23  	"github.com/mysteriumnetwork/node/firewall/iptables"
    24  	"github.com/stretchr/testify/assert"
    25  )
    26  
    27  func Test_outgoingFirewallIptables_BlocksAllOutgoingTraffic(t *testing.T) {
    28  	mockedExec := iptablesExecMock{
    29  		mocks: map[string]iptablesExecResult{},
    30  	}
    31  	iptables.Exec = mockedExec.Exec
    32  
    33  	fw := &outgoingFirewallIptables{
    34  		referenceTracker: make(map[string]refCount),
    35  	}
    36  
    37  	removeRuleFunc, err := fw.BlockOutgoingTraffic("test-scope", "1.1.1.1")
    38  	assert.NoError(t, err)
    39  	assert.True(t, mockedExec.VerifyCalledWithArgs("-A", "OUTPUT", "-s", "1.1.1.1", "-j", killswitchChain))
    40  
    41  	removeRuleFunc()
    42  	assert.True(t, mockedExec.VerifyCalledWithArgs("-D", "OUTPUT", "-s", "1.1.1.1", "-j", killswitchChain))
    43  }
    44  
    45  func Test_outgoingFirewallIptables_SessionTrafficBlockIsNoopWhenGlobalBlockWasCalled(t *testing.T) {
    46  	mockedExec := iptablesExecMock{
    47  		mocks: map[string]iptablesExecResult{},
    48  	}
    49  	iptables.Exec = mockedExec.Exec
    50  
    51  	fw := &outgoingFirewallIptables{
    52  		referenceTracker: make(map[string]refCount),
    53  	}
    54  
    55  	removeGlobalBlock, err := fw.BlockOutgoingTraffic(Global, "1.1.1.1")
    56  	assert.NoError(t, err)
    57  	assert.Equal(t, 1, fw.referenceTracker["block-traffic"].count)
    58  	assert.True(t, mockedExec.VerifyCalledWithArgs("-A", "OUTPUT", "-s", "1.1.1.1", "-j", killswitchChain))
    59  
    60  	removeSessionRule, _ := fw.BlockOutgoingTraffic(Session, "1.1.1.1")
    61  	assert.Equal(t, 1, fw.referenceTracker["block-traffic"].count)
    62  
    63  	removeSessionRule()
    64  	assert.Equal(t, 1, fw.referenceTracker["block-traffic"].count)
    65  
    66  	removeGlobalBlock()
    67  	assert.Equal(t, 0, fw.referenceTracker["block-traffic"].count)
    68  }
    69  
    70  func Test_outgoingFirewallIptables_AllowIPAccessIsAddedAndRemoved(t *testing.T) {
    71  	fw := &outgoingFirewallIptables{
    72  		referenceTracker: make(map[string]refCount),
    73  	}
    74  
    75  	removeRule, _ := fw.AllowIPAccess("test-ip")
    76  	assert.Equal(t, 1, fw.referenceTracker["allow:test-ip"].count)
    77  	removeRule()
    78  	assert.Equal(t, 0, fw.referenceTracker["allow:test-ip"].count)
    79  }
    80  
    81  func Test_outgoingFirewallIptables_HostsFromMultipleURLsAreAllowed(t *testing.T) {
    82  	fw := &outgoingFirewallIptables{
    83  		referenceTracker: make(map[string]refCount),
    84  	}
    85  
    86  	removeRules, _ := fw.AllowURLAccess("http://url1", "my-schema://url2:500/ignoredpath?ignoredQuery=true")
    87  	assert.Equal(t, 1, fw.referenceTracker["allow:url1"].count)
    88  	assert.Equal(t, 1, fw.referenceTracker["allow:url2"].count)
    89  	removeRules()
    90  	assert.Equal(t, 0, fw.referenceTracker["allow:url1"].count)
    91  	assert.Equal(t, 0, fw.referenceTracker["allow:url2"].count)
    92  }
    93  
    94  func Test_outgoingFirewallIptables_RuleIsRemovedOnlyAfterLastRemovalCall(t *testing.T) {
    95  	fw := &outgoingFirewallIptables{
    96  		referenceTracker: make(map[string]refCount),
    97  	}
    98  
    99  	//two independent allow requests for the same service
   100  	removalRequest1, _ := fw.AllowIPAccess("service")
   101  	removalRequest2, _ := fw.AllowIPAccess("service")
   102  	//make sure allow ip was called once
   103  	assert.Equal(t, 1, fw.referenceTracker["allow:service"].count)
   104  	//first removal should have no effect
   105  	removalRequest1()
   106  	assert.Equal(t, 0, fw.referenceTracker["allow:service"].count)
   107  	//second removal removes added rule
   108  	removalRequest2()
   109  	assert.Equal(t, 0, fw.referenceTracker["allow:service"].count)
   110  }
   111  
   112  func Test_outgoingFirewallIptables_SetupIsSuccessful(t *testing.T) {
   113  	mockedExec := iptablesExecMock{
   114  		mocks: map[string]iptablesExecResult{
   115  			"--version": {
   116  				output: []string{"iptables v1.6.0"},
   117  			},
   118  			"-S OUTPUT": {
   119  				output: []string{
   120  					"-P OUTPUT ACCEPT",
   121  				},
   122  			},
   123  		},
   124  	}
   125  	iptables.Exec = mockedExec.Exec
   126  
   127  	fw := &outgoingFirewallIptables{
   128  		referenceTracker: make(map[string]refCount),
   129  	}
   130  	assert.NoError(t, fw.Setup())
   131  	assert.True(t, mockedExec.VerifyCalledWithArgs("-N", killswitchChain))
   132  	assert.True(t, mockedExec.VerifyCalledWithArgs("-A", killswitchChain, "-m", "conntrack", "--ctstate", "NEW", "-j", "REJECT"))
   133  }
   134  
   135  func Test_outgoingFirewallIptables_SetupIsSucessfulIfPreviousCleanupFailed(t *testing.T) {
   136  	mockedExec := iptablesExecMock{
   137  		mocks: map[string]iptablesExecResult{
   138  			"--version": {
   139  				output: []string{"iptables v1.6.0"},
   140  			},
   141  			"-S OUTPUT": {
   142  				output: []string{
   143  					"-P OUTPUT ACCEPT",
   144  					// leftover - kill switch is still enabled
   145  					"-A OUTPUT -s 5.5.5.5 -j MYST_CONSUMER_KILL_SWITCH",
   146  				},
   147  			},
   148  			// kill switch chain still exists
   149  			"-S MYST_CONSUMER_KILL_SWITCH": {
   150  				output: []string{
   151  					// with some allowed ips
   152  					"-A MYST_CONSUMER_KILL_SWITCH -d 2.2.2.2 -j ACCEPT",
   153  					"-A MYST_CONSUMER_KILL_SWITCH -j REJECT",
   154  				},
   155  			},
   156  		},
   157  	}
   158  	iptables.Exec = mockedExec.Exec
   159  
   160  	fw := &outgoingFirewallIptables{
   161  		referenceTracker: make(map[string]refCount),
   162  	}
   163  	assert.NoError(t, fw.Setup())
   164  	assert.True(t, mockedExec.VerifyCalledWithArgs("-D", "OUTPUT", "-s", "5.5.5.5", "-j", killswitchChain))
   165  	assert.True(t, mockedExec.VerifyCalledWithArgs("-F", killswitchChain))
   166  	assert.True(t, mockedExec.VerifyCalledWithArgs("-X", killswitchChain))
   167  	assert.True(t, mockedExec.VerifyCalledWithArgs("-N", killswitchChain))
   168  	assert.True(t, mockedExec.VerifyCalledWithArgs("-A", killswitchChain, "-m", "conntrack", "--ctstate", "NEW", "-j", "REJECT"))
   169  
   170  }
   171  
   172  func Test_outgoingFirewallIptables_ResetIsSuccessful(t *testing.T) {
   173  	mockedExec := iptablesExecMock{
   174  		mocks: map[string]iptablesExecResult{
   175  			"-S OUTPUT": {
   176  				output: []string{
   177  					"-P OUTPUT ACCEPT",
   178  					// kill switch is enabled
   179  					"-A OUTPUT -s 1.1.1.1 -j MYST_CONSUMER_KILL_SWITCH",
   180  				},
   181  			},
   182  			"-S MYST_CONSUMER_KILL_SWITCH": {
   183  				output: []string{
   184  					//first allowed address
   185  					"-A MYST_CONSUMER_KILL_SWITCH -d 2.2.2.2 -j ACCEPT",
   186  					//second allowed address
   187  					"-A MYST_CONSUMER_KILL_SWITCH -d 3.3.3.3 -j ACCEPT",
   188  					//drop everything else
   189  					"-A MYST_CONSUMER_KILL_SWITCH -j REJECT",
   190  				},
   191  			},
   192  		},
   193  	}
   194  	iptables.Exec = mockedExec.Exec
   195  
   196  	fw := &outgoingFirewallIptables{
   197  		referenceTracker: make(map[string]refCount),
   198  	}
   199  	fw.Teardown()
   200  	assert.True(t, mockedExec.VerifyCalledWithArgs("-D", "OUTPUT", "-s", "1.1.1.1", "-j", killswitchChain))
   201  	assert.True(t, mockedExec.VerifyCalledWithArgs("-F", killswitchChain))
   202  	assert.True(t, mockedExec.VerifyCalledWithArgs("-X", killswitchChain))
   203  }
   204  
   205  func Test_outgoingFirewallIptables_AddsAllowedIP(t *testing.T) {
   206  	mockedExec := iptablesExecMock{
   207  		mocks: map[string]iptablesExecResult{},
   208  	}
   209  	iptables.Exec = mockedExec.Exec
   210  
   211  	fw := &outgoingFirewallIptables{
   212  		referenceTracker: make(map[string]refCount),
   213  	}
   214  
   215  	removeRuleFunc, err := fw.AllowIPAccess("2.2.2.2")
   216  	assert.NoError(t, err)
   217  	assert.True(t, mockedExec.VerifyCalledWithArgs("-I", killswitchChain, "1", "-d", "2.2.2.2", "-j", "ACCEPT"))
   218  
   219  	removeRuleFunc()
   220  	assert.True(t, mockedExec.VerifyCalledWithArgs("-D", killswitchChain, "-d", "2.2.2.2", "-j", "ACCEPT"))
   221  
   222  }