Commit 139c3ace authored by anonym's avatar anonym
Browse files

Make it possible to make Tails use a simulated Tor network.

... provided by Chutney (
This is enabled iff the local configuration contains something like:

    src_dir: "/path/to/chutney-src-tree"

otherwise we'll use the real Tor network as we done previously.

The main motivation here is improved robustness -- since the "Tor
network" we now use will exit from the host running the automated test
suite, we won't have to deal with Tor network blocking. Performance
should also be improved.

Will-fix: #9521
parent 0a1f6478
@product @check_tor_leaks
Feature: Simulate the Tor network with chutney
Scenario: We're not using the real Tor network
Given I have started Tails from DVD and logged in and the network is connected
When I start the Tor Browser
And the Tor Browser has started and loaded the startup page
And I open the address "" in the Tor Browser
Then I see "UnsafeBrowserTorCheckFail.png" after at most 30 seconds
Authority = Node(tag="a", authority=1, relay=1, torrc="authority.tmpl")
NonExitRelay = Node(tag="m", relay=1, torrc="relay-non-exit.tmpl")
ExitRelay = Node(tag="r", relay=1, exit=1, torrc="relay.tmpl")
Client = Node(tag="c", torrc="client.tmpl")
NODES = Authority.getN(5) + \
NonExitRelay.getN(20) + \
ExitRelay.getN(10) + \
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_src_dir = $config["Chutney"]["src_dir"]
"You must set 'src_dir' in the Chutney configuration section"
chutney_listen_address = $vmnet.bridge_ip_addr
chutney_script = "#{chutney_src_dir}/chutney"
"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/"
chutney_cleanup_hook = do
Dir.chdir(chutney_src_dir) do
cmd_helper([chutney_script, "stop", network_definition], env)
# Let's make sure we're initializing a fresh Chutney instance
at_exit { }
Dir.chdir(chutney_src_dir) do
cmd_helper([chutney_script, "start", network_definition], env)
rescue Test::Unit::AssertionFailedError
# Chutney will only fail on "start" if it is not configured
cmd_helper([chutney_script, "configure", network_definition], env)
cmd_helper([chutney_script, "start", network_definition], env)
$chutney_initialized = true
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")
# Most of these lines are taken from chutney's client template.
client_torrc_lines = [
'TestingTorNetwork 1',
'AssumeReachable 1',
'PathsNeededToBuildCircuits 0.25',
'TestingClientConsensusDownloadSchedule 0, 5',
'TestingClientDownloadSchedule 0, 5',
'TestingDirAuthVoteExit *',
'TestingDirAuthVoteGuard *',
'TestingDirAuthVoteHSDir *',
'TestingMinExitFlagThreshold 0',
'V3AuthNIntervalsValid 2',
# We run one client in chutney so we easily can grep the generated
# DirAuthority lines and use them.
chutney_src_dir = $config["Chutney"]["src_dir"]
dir_auth_lines = open("#{$config['TMPDIR']}/chutney-data/nodes/035c/torrc") do |f|
$vm.file_append('/etc/tor/torrc', client_torrc_lines)
$vm.execute_successfully('sed "s/Sandbox 1/Sandbox 0/" ' +
When /^Tails is using the real Tor network$/ do
assert($vm.execute('grep "TestingTorNetwork 1" /etc/torrc').failure?)
...@@ -106,6 +106,9 @@ def post_snapshot_restore_hook ...@@ -106,6 +106,9 @@ def post_snapshot_restore_hook
$vm.execute("rm -f /var/log/tor/log") $vm.execute("rm -f /var/log/tor/log")
$vm.execute("systemctl --no-block restart") $vm.execute("systemctl --no-block restart")
$vm.host_to_guest_time_sync $vm.host_to_guest_time_sync
if $config["Chutney"]
$vm.spawn("restart-tor") $vm.spawn("restart-tor")
wait_until_tor_is_working wait_until_tor_is_working
if $vm.file_content('/proc/cmdline').include?(' i2p') if $vm.file_content('/proc/cmdline').include?(' i2p')
...@@ -290,6 +293,10 @@ Given /^the computer (re)?boots Tails$/ do |reboot| ...@@ -290,6 +293,10 @@ Given /^the computer (re)?boots Tails$/ do |reboot|
@screen.wait('TailsGreeter.png', 30*60) @screen.wait('TailsGreeter.png', 30*60)
$vm.wait_until_remote_shell_is_up $vm.wait_until_remote_shell_is_up
activate_filesystem_shares activate_filesystem_shares
if $config["Chutney"]
step 'I configure Tails to use a simulated Tor network'
end end
Given /^I log in to a new session(?: in )?(|German)$/ do |lang| Given /^I log in to a new session(?: in )?(|German)$/ do |lang|
...@@ -177,13 +177,14 @@ def convert_from_bytes(size, unit) ...@@ -177,13 +177,14 @@ def convert_from_bytes(size, unit)
return size.to_f/convert_bytes_mod(unit).to_f return size.to_f/convert_bytes_mod(unit).to_f
end end
def cmd_helper(cmd) def cmd_helper(cmd, env = {})
if cmd.instance_of?(Array) if cmd.instance_of?(Array)
cmd << {:err => [:child, :out]} cmd << {:err => [:child, :out]}
elsif cmd.instance_of?(String) elsif cmd.instance_of?(String)
cmd += " 2>&1" cmd += " 2>&1"
end end
IO.popen(cmd) do |p| env = ENV.to_h.merge(env)
IO.popen(env, cmd) do |p|
out = p.readlines.join("\n") out = p.readlines.join("\n")
p.close p.close
ret = $? ret = $?
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment