2 "#{GIT_DIR}/submodules/chutney"
5 def ensure_chutney_is_running
6 # Ensure that a fresh chutney instance is running, and that it will
7 # be cleaned upon exit. We only do it once, though, since the same
8 # setup can be used throughout the same test suite run.
9 if not($chutney_initialized)
10 chutney_listen_address = $vmnet.bridge_ip_addr
11 chutney_script = "#{chutney_src_dir}/chutney"
13 File.executable?(chutney_script),
14 "It does not look like '#{chutney_src_dir}' is the Chutney source tree"
16 network_definition = "#{GIT_DIR}/features/chutney/test-network"
18 'CHUTNEY_LISTEN_ADDRESS' => chutney_listen_address,
19 'CHUTNEY_DATA_DIR' => "#{$config['TMPDIR']}/chutney-data/"
22 chutney_data_dir_cleanup = Proc.new do
23 if File.directory?(env['CHUTNEY_DATA_DIR'])
24 FileUtils.rm_r(env['CHUTNEY_DATA_DIR'])
28 chutney_cmd = Proc.new do |cmd|
29 Dir.chdir(chutney_src_dir) do
30 cmd_helper([chutney_script, cmd, network_definition], env)
36 chutney_cmd.call('start')
37 rescue Test::Unit::AssertionFailedError
38 if File.directory?(env['CHUTNEY_DATA_DIR'])
39 raise "You are running with --keep-snapshots but Chutney failed " +
40 "to start with its current data directory. To recover you " +
41 "likely want to delete '#{env['CHUTNEY_DATA_DIR']}' and " +
42 "all test suite snapshots and then start over."
44 chutney_cmd.call('configure')
45 chutney_cmd.call('start')
49 chutney_cmd.call('stop')
50 chutney_data_dir_cleanup.call
51 chutney_cmd.call('configure')
52 chutney_cmd.call('start')
56 chutney_cmd.call('stop')
57 chutney_data_dir_cleanup.call unless KEEP_SNAPSHOTS
60 # We have to sanity check that all nodes are running because
61 # `chutney start` will return success even if some nodes fail.
62 running, total = 0, -1
63 status = chutney_cmd.call('status')
64 match = Regexp.new('^(\d+)/(\d+) nodes are running$').match(status)
65 assert_not_nil(match, "Chutney's status did not contain the expected " +
66 "string listing the number of running nodes")
67 running, total = match[1,2].map { |x| x.to_i }
69 total, running, "Chutney is only running #{running}/#{total} nodes"
72 $chutney_initialized = true
76 When /^I configure Tails to use a simulated Tor network$/ do
77 # At the moment this step essentially assumes that we boot with 'the
78 # network is unplugged', run this step, and then 'the network is
79 # plugged'. I believe we can make this pretty transparent without
80 # the need of a dedicated step by using tags (e.g. @fake_tor or
81 # whatever -- possibly we want the opposite, @real_tor,
84 # There are two time points where we for a scenario must ensure that
85 # the client configuration below is enabled if and only if the
86 # scenario is tagged, and that is:
88 # 1. During a proper boot, as soon as the remote shell is up in the
89 # 'the computer boots Tails' step.
91 # 2. When restoring a snapshot, in restore_background().
93 # If we do this, it doesn't even matter if a snapshot is made of an
94 # untagged scenario (without the conf), and we later restore it with
97 # Note: We probably have to clear the /var/lib/tor data dir when we
98 # switch mode. Possibly there are other such problems that make this
99 # abstraction impractical and it's better that we avoid it an go
100 # with the more explicit, step-based approach.
102 assert(not($vm.execute('service tor status').success?),
103 "Running this step when Tor is running is probably not intentional")
104 ensure_chutney_is_running
105 # Most of these lines are taken from chutney's client template.
106 client_torrc_lines = [
107 'TestingTorNetwork 1',
109 'PathsNeededToBuildCircuits 0.25',
110 'TestingBridgeDownloadSchedule 0, 5',
111 'TestingClientConsensusDownloadSchedule 0, 5',
112 'TestingClientDownloadSchedule 0, 5',
113 'TestingDirAuthVoteExit *',
114 'TestingDirAuthVoteGuard *',
115 'TestingDirAuthVoteHSDir *',
116 'TestingMinExitFlagThreshold 0',
117 'V3AuthNIntervalsValid 2',
118 # Enabling TestingTorNetwork disables ClientRejectInternalAddresses
119 # so the Tor client will happily try LAN connections. Coupled with
120 # that TestingTorNetwork is enabled on all exits, and their
121 # ExitPolicyRejectPrivate is disabled, we will allow exiting to
122 # LAN hosts. We have at least one test that tries to make sure
123 # that is *not* possible (Scenario: The Tor Browser cannot access
124 # the LAN) so we cannot allow it. We'll have to rethink all this
125 # if we ever want to run all services locally as well (#9520).
126 'ClientRejectInternalAddresses 1',
128 # We run one client in chutney so we easily can grep the generated
129 # DirAuthority lines and use them.
130 client_torrcs = Dir.glob(
131 "#{$config['TMPDIR']}/chutney-data/nodes/*client/torrc"
133 dir_auth_lines = open(client_torrcs.first) do |f|
134 f.grep(/^(Alternate)?(Dir|Bridge)Authority\s/)
136 client_torrc_lines.concat(dir_auth_lines)
137 $vm.file_append('/etc/tor/torrc', client_torrc_lines)
140 When /^Tails is using the real Tor network$/ do
141 assert($vm.execute('grep "TestingTorNetwork 1" /etc/torrc').failure?)
144 def chutney_onionservice_info
145 hs_hostname_file_path = Dir.glob(
146 "#{$config['TMPDIR']}/chutney-data/nodes/*hs/hidden_service/hostname"
148 hs_hostname = open(hs_hostname_file_path, 'r') do |f|
151 hs_torrc_path = Dir.glob(
152 "#{$config['TMPDIR']}/chutney-data/nodes/*hs/torrc"
154 _, hs_port, local_address_port = open(hs_torrc_path, 'r') do |f|
155 f.grep(/^HiddenServicePort/).first.split
157 local_address, local_port = local_address_port.split(':')
158 [local_address, local_port, hs_hostname, hs_port]
161 def chutney_onionservice_redir(remote_address, remote_port)
162 kill_redir = Proc.new do
164 Process.kill("TERM", $chutney_onionservice_job.pid)
169 if $chutney_onionservice_job
172 local_address, local_port, _ = chutney_onionservice_info
173 # XXX:Stretch: revert the commit introducing this command once we
174 # stop supporting the test suite on Debian Jessie.
175 redir_cmd = ['/usr/bin/redir']
176 redir_version = Gem::Version.new(cmd_helper(redir_cmd + ['--version']))
177 if redir_version < Gem::Version.new('3.0')
179 "--laddr", local_address,
180 "--lport", local_port,
181 "--caddr", remote_address,
182 "--cport", remote_port,
186 "#{local_address}:#{local_port}",
187 "#{remote_address}:#{remote_port}",
190 $chutney_onionservice_job = IO.popen(redir_cmd)
191 add_after_scenario_hook { kill_redir.call }
192 return $chutney_onionservice_job