chutney.rb 5.89 KB
Newer Older
anonym's avatar
anonym committed
1
2
3
4
def chutney_src_dir
  "#{GIT_DIR}/submodules/chutney"
end

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def ensure_chutney_is_running
  # Ensure that a fresh chutney instance is running, and that it will
  # be cleaned upon exit. We only do it once, though, since the same
  # setup can be used throughout the same test suite run.
  if not($chutney_initialized)
    chutney_listen_address = $vmnet.bridge_ip_addr
    chutney_script = "#{chutney_src_dir}/chutney"
    assert(
      File.executable?(chutney_script),
      "It does not look like '#{chutney_src_dir}' is the Chutney source tree"
    )
    network_definition = "#{GIT_DIR}/features/chutney/test-network"
    env = {
      'CHUTNEY_LISTEN_ADDRESS' => chutney_listen_address,
      'CHUTNEY_DATA_DIR' => "#{$config['TMPDIR']}/chutney-data/"
    }
21
22
23
24
25
26
27
28

    chutney_data_dir_cleanup = Proc.new do
      if File.directory?(env['CHUTNEY_DATA_DIR'])
        FileUtils.rm_r(env['CHUTNEY_DATA_DIR'])
      end
    end

    chutney_cmd = Proc.new do |cmd|
29
      Dir.chdir(chutney_src_dir) do
30
        cmd_helper([chutney_script, cmd, network_definition], env)
31
32
      end
    end
33
34

    if KEEP_SNAPSHOTS
35
      begin
36
        chutney_cmd.call('start')
37
      rescue Test::Unit::AssertionFailedError
38
39
40
41
42
43
44
45
46
        if File.directory?(env['CHUTNEY_DATA_DIR'])
          raise "You are running with --keep-snapshots but Chutney failed " +
                "to start with its current data directory. To recover you " +
                "likely want to delete '#{env['CHUTNEY_DATA_DIR']}' and " +
                "all test suite snapshots and then start over."
        else
          chutney_cmd.call('configure')
          chutney_cmd.call('start')
        end
47
      end
48
49
50
51
52
    else
      chutney_cmd.call('stop')
      chutney_data_dir_cleanup.call
      chutney_cmd.call('configure')
      chutney_cmd.call('start')
53
    end
54
55
56
57
58
59

    at_exit do
      chutney_cmd.call('stop')
      chutney_data_dir_cleanup.call unless KEEP_SNAPSHOTS
    end

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
    $chutney_initialized = true
  end
end

When /^I configure Tails to use a simulated Tor network$/ do
  # At the moment this step essentially assumes that we boot with 'the
  # network is unplugged', run this step, and then 'the network is
  # plugged'. I believe we can make this pretty transparent without
  # the need of a dedicated step by using tags (e.g. @fake_tor or
  # whatever -- possibly we want the opposite, @real_tor,
  # instead).
  #
  # There are two time points where we for a scenario must ensure that
  # the client configuration below is enabled if and only if the
  # scenario is tagged, and that is:
  #
  # 1. During a proper boot, as soon as the remote shell is up in the
  #    'the computer boots Tails' step.
  #
  # 2. When restoring a snapshot, in restore_background().
  #
  # If we do this, it doesn't even matter if a snapshot is made of an
  # untagged scenario (without the conf), and we later restore it with
  # a tagged scenario.
  #
  # Note: We probably have to clear the /var/lib/tor data dir when we
  # switch mode. Possibly there are other such problems that make this
  # abstraction impractical and it's better that we avoid it an go
  # with the more explicit, step-based approach.

  assert(not($vm.execute('service tor status').success?),
         "Running this step when Tor is running is probably not intentional")
  ensure_chutney_is_running
  # Most of these lines are taken from chutney's client template.
  client_torrc_lines = [
    'TestingTorNetwork 1',
    'AssumeReachable 1',
    'PathsNeededToBuildCircuits 0.25',
98
    'TestingBridgeDownloadSchedule 0, 5',
99
100
101
102
103
104
105
    'TestingClientConsensusDownloadSchedule 0, 5',
    'TestingClientDownloadSchedule 0, 5',
    'TestingDirAuthVoteExit *',
    'TestingDirAuthVoteGuard *',
    'TestingDirAuthVoteHSDir *',
    'TestingMinExitFlagThreshold 0',
    'V3AuthNIntervalsValid 2',
intrigeri's avatar
intrigeri committed
106
    # Enabling TestingTorNetwork disables ClientRejectInternalAddresses
107
    # so the Tor client will happily try LAN connections. Coupled with
intrigeri's avatar
intrigeri committed
108
    # that TestingTorNetwork is enabled on all exits, and their
109
110
111
112
113
114
    # ExitPolicyRejectPrivate is disabled, we will allow exiting to
    # LAN hosts. We have at least one test that tries to make sure
    # that is *not* possible (Scenario: The Tor Browser cannot access
    # the LAN) so we cannot allow it. We'll have to rethink all this
    # if we ever want to run all services locally as well (#9520).
    'ClientRejectInternalAddresses 1',
115
116
117
  ]
  # We run one client in chutney so we easily can grep the generated
  # DirAuthority lines and use them.
anonym's avatar
anonym committed
118
119
120
121
  client_torrcs = Dir.glob(
    "#{$config['TMPDIR']}/chutney-data/nodes/*client/torrc"
  )
  dir_auth_lines = open(client_torrcs.first) do |f|
122
    f.grep(/^(Alternate)?(Dir|Bridge)Authority\s/)
123
124
125
126
127
128
129
130
  end
  client_torrc_lines.concat(dir_auth_lines)
  $vm.file_append('/etc/tor/torrc', client_torrc_lines)
end

When /^Tails is using the real Tor network$/ do
  assert($vm.execute('grep "TestingTorNetwork 1" /etc/torrc').failure?)
end
131
132
133
134
135
136
137
138
139
140
141

def chutney_onionservice_info
  hs_hostname_file_path = Dir.glob(
    "#{$config['TMPDIR']}/chutney-data/nodes/*hs/hidden_service/hostname"
  ).first
  hs_hostname = open(hs_hostname_file_path, 'r') do |f|
    f.read.chomp
  end
  hs_torrc_path = Dir.glob(
    "#{$config['TMPDIR']}/chutney-data/nodes/*hs/torrc"
  ).first
142
  _, hs_port, local_address_port = open(hs_torrc_path, 'r') do |f|
143
144
    f.grep(/^HiddenServicePort/).first.split
  end
145
146
  local_address, local_port  = local_address_port.split(':')
  [local_address, local_port, hs_hostname, hs_port]
147
148
149
end

def chutney_onionservice_redir(remote_address, remote_port)
150
  kill_redir = Proc.new do
151
    begin
152
      Process.kill("TERM", $chutney_onionservice_job.pid)
153
154
155
156
    rescue
      # noop
    end
  end
157
158
159
160
161
162
163
164
165
166
167
  if $chutney_onionservice_job
    kill_redir.call
  end
  local_address, local_port, _ = chutney_onionservice_info
  $chutney_onionservice_job = IO.popen(
    ['/usr/bin/redir',
     "#{local_address}:#{local_port}",
     "#{remote_address}:#{remote_port}"]
  )
  add_after_scenario_hook { kill_redir.call }
  return $chutney_onionservice_job
168
end