summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkwadronaut <kwadronaut@leap.se>2018-11-07 15:43:41 +0100
committerkwadronaut <kwadronaut@leap.se>2018-11-07 15:43:41 +0100
commite598088c2f9263e7682e399b65376d7a2c529ded (patch)
treeaa628edf0ad55dc2adea8bc6f96bf516aaba0a30
parentd30831f7214da266ad943cf4013f0632e8b15d48 (diff)
parent77070c6bf97ecd6fc860cae6120dca86fa910e5a (diff)
Merge remote-tracking branch 'shared/master'HEADmaster
-rw-r--r--.gitignore8
-rw-r--r--.gitlab-ci.yml42
-rw-r--r--Gemfile35
-rw-r--r--README.md41
-rw-r--r--Rakefile32
-rwxr-xr-xfiles/munin/tor_connections162
-rw-r--r--files/munin/tor_openfds38
-rw-r--r--files/polipo/polipo.conf164
-rw-r--r--lib/facter/tor_hidden_services.rb19
-rw-r--r--lib/puppet/parser/functions/generate_onion_key.rb48
-rw-r--r--lib/puppet/parser/functions/onion_address.rb30
-rw-r--r--manifests/arm.pp4
-rw-r--r--manifests/base.pp10
-rw-r--r--manifests/compact.pp7
-rw-r--r--manifests/daemon.pp10
-rw-r--r--manifests/daemon/base.pp66
-rw-r--r--manifests/daemon/bridge.pp15
-rw-r--r--manifests/daemon/control.pp20
-rw-r--r--manifests/daemon/directory.pp20
-rw-r--r--manifests/daemon/dns.pp15
-rw-r--r--manifests/daemon/exit_policy.pp15
-rw-r--r--manifests/daemon/hidden_service.pp23
-rw-r--r--manifests/daemon/map_address.pp15
-rw-r--r--manifests/daemon/onion_service.pp65
-rw-r--r--manifests/daemon/params.pp19
-rw-r--r--manifests/daemon/relay.pp27
-rw-r--r--manifests/daemon/snippet.pp14
-rw-r--r--manifests/daemon/socks.pp17
-rw-r--r--manifests/daemon/transparent.pp13
-rw-r--r--manifests/daemon/transport_plugin.pp15
-rw-r--r--manifests/init.pp2
-rw-r--r--manifests/munin.pp10
-rw-r--r--manifests/onionbalance.pp82
-rw-r--r--manifests/onionbalance/key.pp25
-rw-r--r--manifests/onionbalance/keys.pp11
-rw-r--r--manifests/polipo.pp9
-rw-r--r--manifests/polipo/base.pp22
-rw-r--r--manifests/polipo/debian.pp7
-rw-r--r--manifests/repo.pp5
-rw-r--r--manifests/torsocks.pp6
-rw-r--r--metadata.json25
-rw-r--r--spec/acceptance/nodesets/default.yml19
-rw-r--r--spec/classes/base_spec.rb44
-rw-r--r--spec/classes/daemon_spec.rb31
-rw-r--r--spec/classes/init_spec.rb27
-rw-r--r--spec/classes/onionbalance_spec.rb60
-rw-r--r--spec/defines/daemon_onion_service_spec.rb120
-rw-r--r--spec/functions/generate_onion_key_spec.rb73
-rw-r--r--spec/functions/onion_address_spec.rb48
-rw-r--r--spec/puppetlabs_spec_helper_clone.rb34
-rwxr-xr-x[-rw-r--r--]spec/spec_helper.rb48
-rw-r--r--spec/spec_helper_acceptance.rb18
-rw-r--r--spec/tmp/.keep0
-rw-r--r--templates/onionbalance/Debian.torrc.erb5
-rw-r--r--templates/onionbalance/RedHat.torrc.erb13
-rw-r--r--templates/onionbalance/config.yaml.erb10
-rw-r--r--templates/torrc.global.erb20
-rw-r--r--templates/torrc.onion_service.erb (renamed from templates/torrc.hidden_service.erb)16
58 files changed, 1134 insertions, 665 deletions
diff --git a/.gitignore b/.gitignore
index 1377554..e8928b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,9 @@
+.tmp_*~
*.swp
+.librarian
+.tmp
+spec/fixtures/modules
+spec/fixtures/manifests
+spec/tmp
+!spec/tmp/.keep
+*.lock
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 611058c..268210d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,39 +1,21 @@
-image: ruby:2.1
-before_script:
- - bundle install --jobs $(nproc) --without system_tests --path=/var/cache/gitlab-runner/ "${FLAGS[@]}"
+image: ruby:2.3
-.job_template: &job_definition
+# Test job template
+.test_template: &test_definition
+ stage: test
script:
- # don't fail on lint warnings
- - bundle exec rake lint || /bin/true
- - bundle exec rake syntax
- - bundle exec rake validate
+ - bundle install --jobs $(nproc) --without docs --path vendor
+ - bundle exec rake tests
- bundle exec rake spec
-# Default debian jessie versions
-test:puppet37:
- variables:
- PUPPET_VERSION: "~> 3.7.2"
- FACTER_VERSION: '~> 2.2.0'
- <<: *job_definition
-
-test:puppet38_future_parser:
- variables:
- PUPPET_VERSION: '~> 3.8'
- FACTER_VERSION: '~> 2.2.0'
- FUTURE_PARSER: 'yes'
- <<: *job_definition
-
-# Default debian stretch versions
+# Test with version present on Debian stable
test:puppet48:
variables:
PUPPET_VERSION: "~> 4.8.2"
FACTER_VERSION: '~> 2.4.6'
- <<: *job_definition
+ HIERA_VERSION: '~> 3.2.0'
+ <<: *test_definition
-# Latest greatest
-test:puppet4x:
- variables:
- PUPPET_VERSION: "> 4"
- FACTER_VERSION: '> 2'
- <<: *job_definition
+# Test with latest Puppet release
+test:puppetlatest:
+ <<: *test_definition
diff --git a/Gemfile b/Gemfile
index 7a16b94..9c1b803 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,23 +1,18 @@
-source "https://rubygems.org"
+# vim:ft=ruby
+source 'https://rubygems.org'
-group :development, :unit_tests do
- gem "rake"
- gem "rspec-puppet", "~> 2.1", :require => false
- gem "rspec-core"
- gem "puppetlabs_spec_helper"
- gem "semantic_puppet"
- gem "metadata-json-lint"
- gem "rspec-puppet-facts"
- gem "mocha"
-end
-
-group :system_tests do
- gem 'beaker'
- gem 'beaker-rspec'
- gem 'beaker_spec_helper'
- gem 'serverspec'
-end
+gem 'rake'
+# 5.3.4 is currently broken
+# https://github.com/rodjek/rspec-puppet/issues/647
+gem 'puppet', ENV['PUPPET_VERSION'] || '< 5.3.4'
-gem "puppet", ENV['PUPPET_VERSION'] || ENV['GEM_PUPPET_VERSION'] || ENV['PUPPET_GEM_VERSION'] || '~> 3.7.0'
-gem "facter", ENV['FACTER_VERSION'] || ENV['GEM_FACTER_VERSION'] || ENV['FACTER_GEM_VERSION'] || '~> 2.2.0'
+gem 'base32'
+group :tests do
+ gem 'facter', ENV['FACTER_VERSION']
+ gem 'hiera', ENV['HIERA_VERSION']
+ gem 'puppetlabs_spec_helper'
+ gem 'librarian-puppet'
+ gem 'metadata-json-lint'
+ gem 'semantic_puppet'
+end
diff --git a/README.md b/README.md
index 2cf89df..b4ea4de 100644
--- a/README.md
+++ b/README.md
@@ -11,17 +11,17 @@
* [Installing torsocks](#installing-torsocks)
* [Configuring relays](#configuring-relays)
* [Configuring the control](#configuring-control)
- * [Configuring hidden services](#configuring-hidden-services)
+ * [Configuring onion services](#configuring-onion-services)
* [Configuring directories](#configuring-directories)
* [Configuring exit policies](#configuring-exit-policies)
* [Configuring transport plugins](#configuring-transport-plugins)
-* [Polipo](#polipo)
+* [Functions](#functions)
* [Munin](#munin)
# Overview<a name="overview"></a>
This module tries to manage tor, making sure it is installed, running, has
-munin graphs if desired and allows for configuration of relays, hidden services,
+munin graphs if desired and allows for configuration of relays, onion services,
exit policies, etc.
## Upgrade Notice<a name="upgrade-notice"></a>
@@ -71,8 +71,7 @@ To install tor, simply include the 'tor' class in your manifests:
class { 'tor': }
-You can specify the `$ensure_version` class parameter to get a specific
-version installed.
+You can specify the `$version` class parameter to get a specific version installed.
However, if you want to make configuration changes to your tor daemon, you will
want to instead include the `tor::daemon` class in your manifests, which will
@@ -126,8 +125,7 @@ To install torsocks, simply include the `torsocks` class in your manifests:
class { 'tor::torsocks': }
-You can specify the `$ensure_version` class parameter to get a specific
-version installed.
+You can specify the `$version` class parameter to get a specific version installed.
# Configuring relays<a name="configuring-relays"></a>
@@ -175,24 +173,28 @@ To pass parameters to configure the `ControlPort` and the
Note: you must pass a hashed password to the control port, if you are going to
use it.
-## Configuring hidden services<a name="configuring-hidden-services"></a>
+## Configuring onion services<a name="configuring-onion-services"></a>
-To configure a tor hidden service you can do something like the following:
+To configure a tor onion service you can do something like the following:
- tor::daemon::hidden_service { "hidden_ssh":
+ tor::daemon::onion_service { "onion_ssh":
ports => 22;
}
The `HiddenServiceDir` is set to the `${data_dir}/${name}`, but you can override
it with the parameter `datadir`.
-If you wish to enable v3-style hidden services to correspond with the v2-style
-hidden services (the same configuration will be applied to both), you can pass
+If you wish to enable v3-style onion services to correspond with the v2-style
+onion services (the same configuration will be applied to both), you can pass
the parameter `v3 => true`. The default is `false`.
If you wish to enable single-hop onion addresses, you can enable them by
passing `single_hop => true`. The default is `false`.
+Onion services used to be called hidden services, so an old interface
+`tor::daemon::hidden_service` is still available, with the feature
+set of that time.
+
## Configuring directories<a name="configuring-directories"></a>
An example directory configuration:
@@ -229,15 +231,18 @@ default:
$servertransport_listenaddr #Set a different address for the transport plugin mechanism
$servertransport_options #Pass a k=v parameters to the transport proxy
-# Polipo<a name="polipo"></a>
+# Functions<a name="functions"></a>
-Polipo support can be enabled by doing:
+This module comes with 2 functions specific to tor support. They require the base32 gem to be installed on the master or wherever they are executed.
- include tor::polipo
+## onion_address
+
+This function takes a 1024bit RSA private key as an argument and returns the onion address for an onion service for that key.
+
+## generate_onion_key
+
+This function takes a path (on the puppetmaster!) and an identifier for a key and returns an array containing the matching onion address and the private key. The private key either exists under the supplied `path/key_identifier` or is being generated on the fly and stored under that path for the next execution.
-This will inherit the `tor` class by default, remove `privoxy` if it's
-installed, and install `polipo`, making sure it is running.
-
# Munin<a name="munin"></a>
If you are using `munin`, and have the puppet munin module installed, you can
diff --git a/Rakefile b/Rakefile
index 85326bb..ed73446 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,19 +1,23 @@
+require 'rubygems'
+# keep for compatibility for now
require 'puppetlabs_spec_helper/rake_tasks'
-require 'puppet-lint/tasks/puppet-lint'
-PuppetLint.configuration.send('disable_80chars')
-PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"]
+task :tests do
+ # run syntax checks on manifests, templates and hiera data
+ # also runs :metadata_lint
+ Rake::Task[:validate].invoke
-desc "Validate manifests, templates, and ruby files"
-task :validate do
- Dir['manifests/**/*.pp'].each do |manifest|
- sh "puppet parser validate --noop #{manifest}"
- end
- Dir['spec/**/*.rb','lib/**/*.rb'].each do |ruby_file|
- sh "ruby -c #{ruby_file}" unless ruby_file =~ /spec\/fixtures/
- end
- Dir['templates/**/*.erb'].each do |template|
- sh "erb -P -x -T '-' #{template} | ruby -c"
+ # runs puppet-lint
+ Rake::Task[:lint].invoke
+end
+
+# use librarian-puppet to manage fixtures instead of .fixtures.yml
+# offers more possibilities like explicit version management, forge downloads,...
+task :librarian_spec_prep do
+ sh "librarian-puppet install --path=spec/fixtures/modules/"
+ pwd = `pwd`.strip
+ unless File.directory?("#{pwd}/spec/fixtures/modules/tor")
+ sh "ln -s #{pwd} #{pwd}/spec/fixtures/modules/tor"
end
end
-task :test => [:lint, :syntax , :validate, :spec]
+task :spec_prep => :librarian_spec_prep
diff --git a/files/munin/tor_connections b/files/munin/tor_connections
deleted file mode 100755
index c1d0a92..0000000
--- a/files/munin/tor_connections
+++ /dev/null
@@ -1,162 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Munin plugin to monitor Tor
-#
-# Author: Ge van Geldorp <ge@gse.nl>
-#
-# Parameters understood:
-#
-# host - Change which host to graph (default localhost)
-# port - Change which port to connect to (default 9051)
-# password - Plain-text control channel password (see torrc
-# HashedControlPassword parameter)
-# cookiefile - Name of the file containing the control channel cookie
-# (see torrc CookieAuthentication parameter)
-#
-# Using HashedControlPassword authentication has the problem that you must
-# include the plain-text password in the munin config file. To have any
-# effect, that file shouldn't be world-readable.
-# If you're using CookieAuthentication, you should run this plugin as a user
-# which has read access to the tor datafiles. Also note that bugs in versions
-# upto and including 0.1.1.20 prevent CookieAuthentication from working.
-#
-# Usage: place in /etc/munin/node.d/ (or link it there using ln -s)
-#
-# Parameters understood:
-# config (required)
-# autoconf (optional - used by munin-config)
-#
-#
-# Magic markers - optional - used by installation scripts and
-# munin-config:
-#
-#%# family=contrib
-#%# capabilities=autoconf
-
-use strict;
-use IO::Socket::INET;
-
-# Config
-our $address = $ENV{host} || "localhost"; # Default: localhost
-our $port = $ENV{port} || 9051; # Default: 9051
-
-# Don't edit below this line
-
-sub Authenticate
-{
- my ($socket) = @_;
- my $authline = "AUTHENTICATE";
- if (defined($ENV{cookiefile})) {
- if (open(COOKIE, "<$ENV{cookiefile}")) {
- binmode COOKIE;
- my $cookie;
- $authline .= " ";
- while (read(COOKIE, $cookie, 32)) {
- foreach my $byte (unpack "C*", $cookie) {
- $authline .= sprintf "%02x", $byte;
- }
- }
- close COOKIE;
- }
- } elsif (defined($ENV{password})) {
- $authline .= ' "' . $ENV{password} . '"';
- }
- print $socket "$authline\r\n";
- my $replyline = <$socket>;
- if (substr($replyline, 0, 1) != '2') {
- $replyline =~ s/\s*$//;
- return "Failed to authenticate: $replyline";
- }
-
- return;
-}
-
-if ($ARGV[0] and $ARGV[0] eq "autoconf") {
- # Try to connect to the daemon
- my $socket = IO::Socket::INET->new("$address:$port")
- or my $failed = 1;
-
- if ($failed) {
- print "no (failed to connect to $address port $port)\n";
- exit 1;
- }
-
- my $msg = Authenticate($socket);
- if (defined($msg)) {
- print $socket "QUIT\r\n";
- close($socket);
- print "no ($msg)\n";
- exit 1;
- }
-
- print $socket "QUIT\r\n";
- close($socket);
- print "yes\n";
- exit 0;
-}
-
-my %connections = ("new", 0,
- "launched", 0,
- "connected", 0,
- "failed", 0,
- "closed", 0);
-
-if ($ARGV[0] and $ARGV[0] eq "config") {
- print "graph_title Connections\n";
- print "graph_args -l 0 --base 1000\n";
- print "graph_vlabel connections\n";
- print "graph_category Tor\n";
- print "graph_period second\n";
- print "graph_info This graph shows the number of Tor OR connections.\n";
-
- foreach my $status (keys %connections) {
- print "$status.label $status\n";
- print "$status.type GAUGE\n";
- print "$status.max 50000\n";
- print "$status.min 0\n";
- }
-
- exit 0;
-}
-
-my $socket = IO::Socket::INET->new("$address:$port")
- or die("Couldn't connect to $address port $port: $!");
-
-my $msg = Authenticate($socket);
-if (defined($msg)) {
- print $socket "QUIT\r\n";
- close($socket);
- die "$msg\n";
-}
-
-print $socket "GETINFO orconn-status\r\n";
-my $replyline = <$socket>;
-if (substr($replyline, 0, 1) != '2') {
- print $socket "QUIT\r\n";
- close($socket);
- $replyline =~ s/\s*$//;
- die "Failed to get orconn-status info: $replyline\n";
-}
-
-while (! (($replyline = <$socket>) =~ /^\.\s*$/)) {
- my @reply = split(/\s+/, $replyline);
- $connections{lc($reply[1])}++;
-}
-$replyline = <$socket>;
-if (substr($replyline, 0, 1) != '2') {
- print $socket "QUIT\r\n";
- close($socket);
- $replyline =~ s/\s*$//;
- die "Failed to authenticate: $replyline\n";
-}
-
-print $socket "QUIT\r\n";
-close($socket);
-
-while (my ($status, $count) = each(%connections)) {
- print "$status.value $count\n";
-}
-
-exit 0;
-
-# vim:syntax=perl
diff --git a/files/munin/tor_openfds b/files/munin/tor_openfds
new file mode 100644
index 0000000..9c14852
--- /dev/null
+++ b/files/munin/tor_openfds
@@ -0,0 +1,38 @@
+#!/usr/bin/perl -w
+# https://lists.torproject.org/pipermail/tor-talk/2006-June/010486.html
+
+use strict;
+
+# Script to monitor the amount of FDs used by
+# the Tor process (var/run/tor/tor.pid)
+
+if ($ARGV[0] and $ARGV[0] =~ /^\s*config\s*$/i)
+{
+ print "graph_title Open file descriptors for Tor\n";
+ print "graph_args --base 1000 -l 0\n";
+ print "graph_vlabel open FDs\n";
+ print "graph_category Tor\n";
+ print "count.label TorFDs\n";
+ exit 0;
+}
+
+my $pidfile = "/var/run/tor/tor.pid";
+my $pid = '';
+if (-e $pidfile) {
+ open (PID, $pidfile) or exit 1;
+ $pid = <PID>;
+ close PID;
+} else {
+ $pid = `pidof tor`;
+}
+chomp $pid;
+
+$pid =~ /^[0-9]+$/ or exit 1;
+
+opendir (FDS, "/proc/$pid/fd") or exit 1;
+my @fds = readdir(FDS);
+closedir FDS;
+
+my $count = scalar @fds - 2;
+
+print "count.value $count\n";
diff --git a/files/polipo/polipo.conf b/files/polipo/polipo.conf
deleted file mode 100644
index 12b10c4..0000000
--- a/files/polipo/polipo.conf
+++ /dev/null
@@ -1,164 +0,0 @@
-# Polipo Configuration from https://svn.torproject.org/svn/torbrowser/trunk/build-scripts/config/polipo.conf
-# Managed by puppet.
-
-### Basic configuration
-### *******************
-
-# Uncomment one of these if you want to allow remote clients to
-# connect:
-
-# proxyAddress = "::0" # both IPv4 and IPv6
-# proxyAddress = "0.0.0.0" # IPv4 only
-
-proxyAddress = "127.0.0.1"
-proxyPort = 8118
-
-# If you do that, you'll want to restrict the set of hosts allowed to
-# connect:
-
-# allowedClients = "127.0.0.1, 134.157.168.57"
-# allowedClients = "127.0.0.1, 134.157.168.0/24"
-
-allowedClients = 127.0.0.1
-allowedPorts = 1-65535
-
-# Uncomment this if you want your Polipo to identify itself by
-# something else than the host name:
-
-proxyName = "localhost"
-
-# Uncomment this if there's only one user using this instance of Polipo:
-
-cacheIsShared = false
-
-# Uncomment this if you want to use a parent proxy:
-
-# parentProxy = "squid.example.org:3128"
-
-# Uncomment this if you want to use a parent SOCKS proxy:
-
-socksParentProxy = "localhost:9050"
-socksProxyType = socks5
-
-
-### Memory
-### ******
-
-# Uncomment this if you want Polipo to use a ridiculously small amount
-# of memory (a hundred C-64 worth or so):
-
-# chunkHighMark = 819200
-# objectHighMark = 128
-
-# Uncomment this if you've got plenty of memory:
-
-# chunkHighMark = 50331648
-# objectHighMark = 16384
-
-chunkHighMark = 67108864
-
-### On-disk data
-### ************
-
-# Uncomment this if you want to disable the on-disk cache:
-
-diskCacheRoot = ""
-
-# Uncomment this if you want to put the on-disk cache in a
-# non-standard location:
-
-# diskCacheRoot = "~/.polipo-cache/"
-
-# Uncomment this if you want to disable the local web server:
-
-localDocumentRoot = ""
-
-# Uncomment this if you want to enable the pages under /polipo/index?
-# and /polipo/servers?. This is a serious privacy leak if your proxy
-# is shared.
-
-# disableIndexing = false
-# disableServersList = false
-
-disableLocalInterface = true
-disableConfiguration = true
-
-### Domain Name System
-### ******************
-
-# Uncomment this if you want to contact IPv4 hosts only (and make DNS
-# queries somewhat faster):
-#
-# dnsQueryIPv6 = no
-
-# Uncomment this if you want Polipo to prefer IPv4 to IPv6 for
-# double-stack hosts:
-#
-# dnsQueryIPv6 = reluctantly
-
-# Uncomment this to disable Polipo's DNS resolver and use the system's
-# default resolver instead. If you do that, Polipo will freeze during
-# every DNS query:
-
-dnsUseGethostbyname = yes
-
-
-### HTTP
-### ****
-
-# Uncomment this if you want to enable detection of proxy loops.
-# This will cause your hostname (or whatever you put into proxyName
-# above) to be included in every request:
-
-disableVia = true
-
-# Uncomment this if you want to slightly reduce the amount of
-# information that you leak about yourself:
-
-# censoredHeaders = from, accept-language
-# censorReferer = maybe
-
-censoredHeaders = from,accept-language,x-pad,link
-censorReferer = maybe
-
-# Uncomment this if you're paranoid. This will break a lot of sites,
-# though:
-
-# censoredHeaders = set-cookie, cookie, cookie2, from, accept-language
-# censorReferer = true
-
-# Uncomment this if you want to use Poor Man's Multiplexing; increase
-# the sizes if you're on a fast line. They should each amount to a few
-# seconds' worth of transfer; if pmmSize is small, you'll want
-# pmmFirstSize to be larger.
-
-# Note that PMM is somewhat unreliable.
-
-# pmmFirstSize = 16384
-# pmmSize = 8192
-
-# Uncomment this if your user-agent does something reasonable with
-# Warning headers (most don't):
-
-# relaxTransparency = maybe
-
-# Uncomment this if you never want to revalidate instances for which
-# data is available (this is not a good idea):
-
-# relaxTransparency = yes
-
-# Uncomment this if you have no network:
-
-# proxyOffline = yes
-
-# Uncomment this if you want to avoid revalidating instances with a
-# Vary header (this is not a good idea):
-
-# mindlesslyCacheVary = true
-
-# Suggestions from Incognito configuration
-maxConnectionAge = 5m
-maxConnectionRequests = 120
-serverMaxSlots = 8
-serverSlots = 2
-tunnelAllowedPorts = 1-65535
diff --git a/lib/facter/tor_hidden_services.rb b/lib/facter/tor_hidden_services.rb
new file mode 100644
index 0000000..c2a6cca
--- /dev/null
+++ b/lib/facter/tor_hidden_services.rb
@@ -0,0 +1,19 @@
+Facter.add(:tor_hidden_services) do
+ confine :kernel => "Linux"
+ setcode do
+ config_file = '/etc/tor/torrc'
+ if File.exists?(config_file)
+ dirs = File.read(config_file).split("\n").select{|l|
+ l =~ /^HiddenServiceDir/
+ }.collect{|l| l.sub(/^HiddenServiceDir /,'') }
+ dirs.inject({}) { |res,d|
+ if File.exists?(h=File.join(d,'hostname'))
+ res[File.basename(d)] = File.read(h).chomp
+ end
+ res
+ }
+ else
+ {}
+ end
+ end
+end
diff --git a/lib/puppet/parser/functions/generate_onion_key.rb b/lib/puppet/parser/functions/generate_onion_key.rb
new file mode 100644
index 0000000..9ee5351
--- /dev/null
+++ b/lib/puppet/parser/functions/generate_onion_key.rb
@@ -0,0 +1,48 @@
+module Puppet::Parser::Functions
+ newfunction(:generate_onion_key, :type => :rvalue, :doc => <<-EOS
+Generates or loads a rsa private key for an onion service, returns they onion
+onion address and the private key content.
+
+Requires a location to load and store the private key, as well an identifier, which will be used as a filename in the location.
+
+Example:
+
+ res = generate_onion_key('/tmp','my_secret_key')
+ notice "Onion Address: \${res[0]"
+ notice "Priavte Key: \${res[1]"
+
+
+It will also store the onion address under /tmp/my_secret_key.hostname.
+If /tmp/my_secret_key.key exists, but not the hostname file. Then the function will be loaded and the onion address will be generated from it.
+
+ EOS
+ ) do |args|
+ location = args.shift
+ identifier = args.shift
+
+ raise(Puppet::ParseError, "generate_onion_key(): requires 2 arguments") unless [location,identifier].all?{|i| !i.nil? }
+
+ raise(Puppet::ParseError, "generate_onion_key(): requires location (#{location}) to be a directory") unless File.directory?(location)
+ path = File.join(location,identifier)
+
+ private_key = if File.exists?(kf="#{path}.key")
+ pk = OpenSSL::PKey::RSA.new(File.read(kf))
+ raise(Puppet::ParseError, "generate_onion_key(): key in path #{kf} must have a length of 1024bit") unless (pk.n.num_bytes * 8) == 1024
+ pk
+ else
+ # 1024 is hardcoded by tor
+ pk = OpenSSL::PKey::RSA.generate(1024)
+ File.open(kf,'w'){|f| f << pk.to_s }
+ pk
+ end
+ onion_address = if File.exists?(hf="#{path}.hostname")
+ File.read(hf)
+ else
+ oa = function_onion_address([private_key])
+ File.open(hf,'w'){|f| f << oa.to_s }
+ oa
+ end
+
+ [ onion_address, private_key.to_s ]
+ end
+end
diff --git a/lib/puppet/parser/functions/onion_address.rb b/lib/puppet/parser/functions/onion_address.rb
new file mode 100644
index 0000000..a6f9755
--- /dev/null
+++ b/lib/puppet/parser/functions/onion_address.rb
@@ -0,0 +1,30 @@
+require 'base32'
+module Puppet::Parser::Functions
+ newfunction(:onion_address, :type => :rvalue, :doc => <<-EOS
+Generates an onion address from a 1024-bit RSA private key.
+
+Example:
+
+ onion_address("-----BEGIN RSA PRIVATE KEY-----
+MII....
+-----END RSA PRIVATE KEY-----")
+
+Returns the onionadress for that key, *without* the .onion suffix.
+ EOS
+ ) do |args|
+ key = args.shift
+ raise(Puppet::ParseError, "onion_address(): requires 1 argument") unless key && args.empty?
+ private_key = key.is_a?(OpenSSL::PKey::RSA) ? key : OpenSSL::PKey::RSA.new(key)
+
+ # the onion address are a base32 encoded string of the first half of the sha1 over the
+ # der format of the public key
+ # https://trac.torproject.org/projects/tor/wiki/doc/HiddenServiceNames#Howare.onionnamescreated
+ # We can skip the first 22 bits of the der format as they are ignored by tor
+ # https://timtaubert.de/blog/2014/11/using-the-webcrypto-api-to-generate-onion-names-for-tor-hidden-services/
+ # https://gitweb.torproject.org/torspec.git/tree/rend-spec.txt#n525
+ # Except for Ruby 1.8.7 where the first 22 are not present at all
+ start = RUBY_VERSION.to_f < 1.9 ? 0 : 22
+ public_key_der = private_key.public_key.to_der
+ Base32.encode(Digest::SHA1.digest(public_key_der[start..-1]))[0..15].downcase
+ end
+end
diff --git a/manifests/arm.pp b/manifests/arm.pp
index 44ddcbb..dfea7c8 100644
--- a/manifests/arm.pp
+++ b/manifests/arm.pp
@@ -1,9 +1,9 @@
# manage tor-arm
class tor::arm (
- $ensure_version = 'installed'
+ $version = 'installed'
){
include ::tor
package{'tor-arm':
- ensure => $ensure_version,
+ ensure => $version,
}
}
diff --git a/manifests/base.pp b/manifests/base.pp
index b98451b..13d8507 100644
--- a/manifests/base.pp
+++ b/manifests/base.pp
@@ -1,7 +1,13 @@
# basic management of resources for tor
class tor::base {
- package { [ 'tor', 'tor-geoipdb' ]:
- ensure => $tor::ensure_version,
+ package {'tor':
+ ensure => $tor::version,
+ }
+ if $facts['osfamily'] == 'Debian' {
+ package {'tor-geoipdb':
+ ensure => $tor::version,
+ before => Service['tor'],
+ }
}
service { 'tor':
diff --git a/manifests/compact.pp b/manifests/compact.pp
deleted file mode 100644
index c0f5919..0000000
--- a/manifests/compact.pp
+++ /dev/null
@@ -1,7 +0,0 @@
-# manage a complete tor
-# installation with all the basics
-class tor::compact {
- include ::tor
- include tor::polipo
- include tor::torsocks
-}
diff --git a/manifests/daemon.pp b/manifests/daemon.pp
index 2522b2c..55c881d 100644
--- a/manifests/daemon.pp
+++ b/manifests/daemon.pp
@@ -1,6 +1,5 @@
# manage a snippet based tor installation
class tor::daemon (
- $ensure_version = 'installed',
$use_munin = false,
$data_dir = '/var/lib/tor',
$config_file = '/etc/tor/torrc',
@@ -10,13 +9,10 @@ class tor::daemon (
$safe_logging = 1,
) {
- class{'tor':
- ensure_version => $ensure_version,
- }
-
- include tor::daemon::base
+ include ::tor
+ include ::tor::daemon::base
if $use_munin {
- include tor::munin
+ include ::tor::munin
}
}
diff --git a/manifests/daemon/base.pp b/manifests/daemon/base.pp
index f3bbc37..86156af 100644
--- a/manifests/daemon/base.pp
+++ b/manifests/daemon/base.pp
@@ -1,70 +1,62 @@
# extend basic tor things with a snippet based daemon configuration
class tor::daemon::base inherits tor::base {
- # packages, user, group
- Service['tor'] {
- subscribe => Concat[$tor::daemon::config_file],
- }
- Package[ 'tor' ] {
- require => File[$tor::daemon::data_dir],
- }
+ include ::tor::daemon::params
- group { 'debian-tor':
- ensure => present,
- allowdupe => false,
- }
+ if $tor::daemon::params::manage_user {
+ group { $tor::daemon::params::group:
+ ensure => present,
+ allowdupe => false,
+ }
- user { 'debian-tor':
- ensure => present,
- allowdupe => false,
- comment => 'tor user,,,',
- home => $tor::daemon::data_dir,
- shell => '/bin/false',
- gid => 'debian-tor',
- require => Group['debian-tor'],
+ user { $tor::daemon::params::user:
+ ensure => present,
+ allowdupe => false,
+ comment => 'tor user,,,',
+ home => $tor::daemon::data_dir,
+ shell => '/bin/false',
+ gid => $tor::daemon::params::group,
+ require => Group[$tor::daemon::params::group],
+ }
}
# directories
file { $tor::daemon::data_dir:
ensure => directory,
- mode => '0700',
- owner => 'debian-tor',
- group => 'debian-tor',
- require => User['debian-tor'],
+ mode => $tor::daemon::params::data_dir_mode,
+ owner => $tor::daemon::params::user,
+ group => 'root',
+ require => Package['tor'],
}
file { '/etc/tor':
ensure => directory,
mode => '0755',
- owner => 'debian-tor',
- group => 'debian-tor',
- require => User['debian-tor'],
- }
-
- file { '/var/lib/puppet/modules/tor':
- ensure => absent,
- recurse => true,
- force => true,
+ owner => 'root',
+ group => 'root',
+ require => Package['tor'],
}
# tor configuration file
concat { $tor::daemon::config_file:
- mode => '0600',
- owner => 'debian-tor',
- group => 'debian-tor',
+ mode => '0640',
+ owner => 'root',
+ group => $tor::daemon::params::group,
+ require => Package['tor'],
+ notify => Service['tor'],
}
# config file headers
concat::fragment { '00.header':
content => template('tor/torrc.header.erb'),
- order => 00,
+ order => '00',
target => $tor::daemon::config_file,
}
# global configurations
concat::fragment { '01.global':
content => template('tor/torrc.global.erb'),
- order => 01,
+ order => '01',
target => $tor::daemon::config_file,
}
}
diff --git a/manifests/daemon/bridge.pp b/manifests/daemon/bridge.pp
index a448f82..e09f4f7 100644
--- a/manifests/daemon/bridge.pp
+++ b/manifests/daemon/bridge.pp
@@ -2,12 +2,15 @@
define tor::daemon::bridge(
$ip,
$port,
- $fingerprint = false ) {
-
- concat::fragment { "10.bridge.${name}":
- content => template('tor/torrc.bridge.erb'),
- order => 10,
- target => $tor::daemon::config_file,
+ $fingerprint = false,
+ $ensure = 'present',
+) {
+ if $ensure == 'present' {
+ concat::fragment { "10.bridge.${name}":
+ content => template('tor/torrc.bridge.erb'),
+ order => '10',
+ target => $tor::daemon::config_file,
+ }
}
}
diff --git a/manifests/daemon/control.pp b/manifests/daemon/control.pp
index 4137c3e..ee4a1fd 100644
--- a/manifests/daemon/control.pp
+++ b/manifests/daemon/control.pp
@@ -1,26 +1,26 @@
# control definition
define tor::daemon::control(
+ $ensure = 'present',
$port = 0,
$hashed_control_password = '',
$cookie_authentication = 0,
$cookie_auth_file = '',
$cookie_auth_file_group_readable = '',
- $ensure = present ) {
+) {
- if $cookie_authentication == '0'
- and $hashed_control_password == ''
- and $ensure != 'absent' {
+ if $ensure == 'present' {
+ if $cookie_authentication == '0' and $hashed_control_password == '' {
fail('You need to define the tor control password')
}
- if $cookie_authentication == 0
- and ($cookie_auth_file != '' or $cookie_auth_file_group_readable != '') {
+ if $cookie_authentication == 0 and ($cookie_auth_file != '' or $cookie_auth_file_group_readable != '') { # lint:ignore:80chars
notice('You set a tor cookie authentication option, but do not have cookie_authentication on') # lint:ignore:80chars
}
- concat::fragment { '04.control':
- content => template('tor/torrc.control.erb'),
- order => 04,
- target => $tor::daemon::config_file,
+ concat::fragment { '04.control':
+ content => template('tor/torrc.control.erb'),
+ order => '04',
+ target => $tor::daemon::config_file,
+ }
}
}
diff --git a/manifests/daemon/directory.pp b/manifests/daemon/directory.pp
index 3eaef9f..b2f77fe 100644
--- a/manifests/daemon/directory.pp
+++ b/manifests/daemon/directory.pp
@@ -1,22 +1,24 @@
# directory advertising
define tor::daemon::directory (
+ $ensure = 'present',
$port = 0,
$port_front_page = '/etc/tor/tor-exit-notice.html',
- $ensure = present ) {
-
- concat::fragment { '06.directory':
- content => template('tor/torrc.directory.erb'),
- order => 06,
- target => $tor::daemon::config_file,
+) {
+ if $ensure == 'present' {
+ concat::fragment { '06.directory':
+ content => template('tor/torrc.directory.erb'),
+ order => '06',
+ target => $tor::daemon::config_file,
+ }
}
+ include ::tor::daemon::params
file { '/etc/tor/tor-exit-notice.html':
ensure => $ensure,
source => 'puppet:///modules/tor/tor-exit-notice.html',
require => File['/etc/tor'],
- owner => 'debian-tor',
- group => 'debian-tor',
+ owner => $tor::daemon::params::user,
+ group => $tor::daemon::params::group,
mode => '0644',
}
}
-
diff --git a/manifests/daemon/dns.pp b/manifests/daemon/dns.pp
index 599abd8..899f920 100644
--- a/manifests/daemon/dns.pp
+++ b/manifests/daemon/dns.pp
@@ -1,11 +1,14 @@
# DNS definition
define tor::daemon::dns(
- $port = 0 ) {
-
- concat::fragment { "08.dns.${name}":
- content => template('tor/torrc.dns.erb'),
- order => '08',
- target => $tor::daemon::config_file,
+ $ensure = 'present',
+ $port = 0,
+){
+ if $ensure == 'present' {
+ concat::fragment { "08.dns.${name}":
+ content => template('tor/torrc.dns.erb'),
+ order => '08',
+ target => $tor::daemon::config_file,
+ }
}
}
diff --git a/manifests/daemon/exit_policy.pp b/manifests/daemon/exit_policy.pp
index c117d5d..62876c7 100644
--- a/manifests/daemon/exit_policy.pp
+++ b/manifests/daemon/exit_policy.pp
@@ -1,13 +1,16 @@
# exit policies
define tor::daemon::exit_policy(
+ $ensure = 'present',
$accept = [],
$reject = [],
- $reject_private = 1 ) {
-
- concat::fragment { "07.exit_policy.${name}":
- content => template('tor/torrc.exit_policy.erb'),
- order => 07,
- target => $tor::daemon::config_file,
+ $reject_private = 1,
+) {
+ if $ensure == 'present' {
+ concat::fragment { "07.exit_policy.${name}":
+ content => template('tor/torrc.exit_policy.erb'),
+ order => '07',
+ target => $tor::daemon::config_file,
+ }
}
}
diff --git a/manifests/daemon/hidden_service.pp b/manifests/daemon/hidden_service.pp
index 9e053cc..8a062c5 100644
--- a/manifests/daemon/hidden_service.pp
+++ b/manifests/daemon/hidden_service.pp
@@ -3,19 +3,14 @@ define tor::daemon::hidden_service(
$ports = [],
$single_hop = false,
$v3 = false,
- $data_dir = $tor::daemon::data_dir ) {
-
-
-
- if $single_hop {
- file { "${$data_dir}/${$name}/onion_service_non_anonymous":
- ensure => 'present',
- }
- }
-
- concat::fragment { "05.hidden_service.${name}":
- content => template('tor/torrc.hidden_service.erb'),
- order => 05,
- target => $tor::daemon::config_file,
+ $data_dir = $tor::daemon::data_dir,
+) {
+ info("Using tor::daemon::hidden_service is deprecated, please use tor::daemon::onion_service for ${name}")
+ tor::daemon::onion_service{
+ $name:
+ ports => $ports,
+ single_hop => $single_hop,
+ v3 => $v3,
+ data_dir => $data_dir,
}
}
diff --git a/manifests/daemon/map_address.pp b/manifests/daemon/map_address.pp
index 1829eae..ca21ed9 100644
--- a/manifests/daemon/map_address.pp
+++ b/manifests/daemon/map_address.pp
@@ -1,12 +1,15 @@
# map address definition
define tor::daemon::map_address(
+ $ensure = 'present',
$address = '',
- $newaddress = '' ) {
-
- concat::fragment { "08.map_address.${name}":
- content => template('tor/torrc.map_address.erb'),
- order => '08',
- target => $tor::daemon::config_file,
+ $newaddress = '',
+) {
+ if $ensure == 'present' {
+ concat::fragment { "08.map_address.${name}":
+ content => template('tor/torrc.map_address.erb'),
+ order => '08',
+ target => $tor::daemon::config_file,
+ }
}
}
diff --git a/manifests/daemon/onion_service.pp b/manifests/daemon/onion_service.pp
new file mode 100644
index 0000000..cb55d06
--- /dev/null
+++ b/manifests/daemon/onion_service.pp
@@ -0,0 +1,65 @@
+# onion services definition
+define tor::daemon::onion_service(
+ $ensure = 'present',
+ $ports = [],
+ $data_dir = $tor::daemon::data_dir,
+ $v3 = false,
+ $single_hop = false,
+ $private_key = undef,
+ $private_key_name = $name,
+ $private_key_store_path = undef,
+) {
+
+ $data_dir_path = "${data_dir}/${name}"
+ if $ensure == 'present' {
+ include ::tor::daemon::params
+ concat::fragment { "05.onion_service.${name}":
+ content => template('tor/torrc.onion_service.erb'),
+ order => '05',
+ target => $tor::daemon::config_file,
+ }
+ if $single_hop {
+ file { "${$data_dir_path}/onion_service_non_anonymous":
+ ensure => 'present',
+ notify => Service['tor'];
+ }
+ }
+ }
+ if $private_key or ($private_key_name and $private_key_store_path) {
+ if $private_key and ($private_key_name and $private_key_store_path) {
+ fail('Either private_key OR (private_key_name AND private_key_store_path) must be set, but not all three of them')
+ }
+ if $private_key_store_path and $private_key_name {
+ $tmp = generate_onion_key($private_key_store_path,$private_key_name)
+ $os_hostname = $tmp[0]
+ $real_private_key = $tmp[1]
+ } else {
+ $os_hostname = onion_address($private_key)
+ $real_private_key = $private_key
+ }
+ file{
+ $data_dir_path:
+ ensure => directory,
+ purge => true,
+ force => true,
+ recurse => true,
+ owner => $tor::daemon::params::user,
+ group => $tor::daemon::params::group,
+ mode => '0600',
+ require => Package['tor'];
+ "${data_dir_path}/private_key":
+ content => $real_private_key,
+ owner => $tor::daemon::params::user,
+ group => $tor::daemon::params::group,
+ mode => '0600',
+ notify => Service['tor'];
+ "${data_dir_path}/hostname":
+ content => "${os_hostname}.onion\n",
+ owner => $tor::daemon::params::user,
+ group => $tor::daemon::params::group,
+ mode => '0600',
+ notify => Service['tor'];
+ }
+ }
+}
+
diff --git a/manifests/daemon/params.pp b/manifests/daemon/params.pp
new file mode 100644
index 0000000..39126ee
--- /dev/null
+++ b/manifests/daemon/params.pp
@@ -0,0 +1,19 @@
+# setup variables for different distributions
+class tor::daemon::params {
+ case $facts['osfamily'] {
+ 'RedHat': {
+ $user = 'toranon'
+ $group = 'toranon'
+ $manage_user = false
+ $data_dir_mode = '0750'
+ }
+ 'Debian': {
+ $user = 'debian-tor'
+ $group = 'debian-tor'
+ $manage_user = true
+ $data_dir_mode = '0700'
+ }
+ default: { fail("No support for osfamily ${facts['osfamily']}") }
+ }
+
+}
diff --git a/manifests/daemon/relay.pp b/manifests/daemon/relay.pp
index fa908f5..bc72dd0 100644
--- a/manifests/daemon/relay.pp
+++ b/manifests/daemon/relay.pp
@@ -1,5 +1,6 @@
# relay definition
define tor::daemon::relay(
+ $ensure = 'present',
$port = 0,
$outbound_bindaddresses = [],
$portforwarding = 0,
@@ -13,25 +14,27 @@ define tor::daemon::relay(
$relay_bandwidth_burst = 0,
# GB, 0 for no limit
$accounting_max = 0,
- $accounting_start = "month 1 0:00",
+ $accounting_start = 'month 1 0:00',
$contact_info = '',
# TODO: autofill with other relays
$my_family = '',
$address = "tor.${::domain}",
$bridge_relay = 0,
- $ensure = present ) {
+) {
- $nickname = $name
+ if $ensure == 'present' {
+ $nickname = $name
- if $outbound_bindaddresses == [] {
- $real_outbound_bindaddresses = []
- } else {
- $real_outbound_bindaddresses = $outbound_bindaddresses
- }
+ if $outbound_bindaddresses == [] {
+ $real_outbound_bindaddresses = []
+ } else {
+ $real_outbound_bindaddresses = $outbound_bindaddresses
+ }
- concat::fragment { '03.relay':
- content => template('tor/torrc.relay.erb'),
- order => 03,
- target => $tor::daemon::config_file,
+ concat::fragment { '03.relay':
+ content => template('tor/torrc.relay.erb'),
+ order => '03',
+ target => $tor::daemon::config_file,
+ }
}
}
diff --git a/manifests/daemon/snippet.pp b/manifests/daemon/snippet.pp
index 5a4c091..195ed77 100644
--- a/manifests/daemon/snippet.pp
+++ b/manifests/daemon/snippet.pp
@@ -1,11 +1,15 @@
# Arbitrary torrc snippet definition
define tor::daemon::snippet(
- $content = '' ) {
+ $ensure = 'present',
+ $content = '',
+) {
- concat::fragment { "99.snippet.${name}":
- content => $content,
- order => 99,
- target => $tor::daemon::config_file,
+ if $ensure == 'present' {
+ concat::fragment { "99.snippet.${name}":
+ content => $content,
+ order => '99',
+ target => $tor::daemon::config_file,
+ }
}
}
diff --git a/manifests/daemon/socks.pp b/manifests/daemon/socks.pp
index cb130d9..e36d91e 100644
--- a/manifests/daemon/socks.pp
+++ b/manifests/daemon/socks.pp
@@ -1,11 +1,14 @@
# socks definition
define tor::daemon::socks(
- $port = 0,
- $policies = [] ) {
-
- concat::fragment { '02.socks':
- content => template('tor/torrc.socks.erb'),
- order => 02,
- target => $tor::daemon::config_file,
+ $ensure = 'present',
+ $port = 0,
+ $policies = [],
+) {
+ if $ensure == 'present' {
+ concat::fragment { '02.socks':
+ content => template('tor/torrc.socks.erb'),
+ order => '02',
+ target => $tor::daemon::config_file,
+ }
}
}
diff --git a/manifests/daemon/transparent.pp b/manifests/daemon/transparent.pp
index 90c0142..0d4620a 100644
--- a/manifests/daemon/transparent.pp
+++ b/manifests/daemon/transparent.pp
@@ -1,11 +1,14 @@
# Transparent proxy definition
define tor::daemon::transparent(
- $port = 0 ) {
+ $ensure = 'present',
+ $port = 0) {
- concat::fragment { "09.transparent.${name}":
- content => template('tor/torrc.transparent.erb'),
- order => '09',
- target => $tor::daemon::config_file,
+ if $ensure == 'present' {
+ concat::fragment { "09.transparent.${name}":
+ content => template('tor/torrc.transparent.erb'),
+ order => '09',
+ target => $tor::daemon::config_file,
+ }
}
}
diff --git a/manifests/daemon/transport_plugin.pp b/manifests/daemon/transport_plugin.pp
index 1921282..4f7bbf2 100644
--- a/manifests/daemon/transport_plugin.pp
+++ b/manifests/daemon/transport_plugin.pp
@@ -1,13 +1,16 @@
# transport plugin
define tor::daemon::transport_plugin(
+ $ensure = 'present',
$servertransport_plugin = '',
$servertransport_listenaddr = '',
$servertransport_options = '',
- $ext_port = '' ) {
-
- concat::fragment { '11.transport_plugin':
- content => template('tor/torrc.transport_plugin.erb'),
- order => 11,
- target => $tor::daemon::config_file,
+ $ext_port = '',
+) {
+ if $ensure == 'present' {
+ concat::fragment { '11.transport_plugin':
+ content => template('tor/torrc.transport_plugin.erb'),
+ order => 11,
+ target => $tor::daemon::config_file,
+ }
}
}
diff --git a/manifests/init.pp b/manifests/init.pp
index 9c19c64..ad584aa 100644
--- a/manifests/init.pp
+++ b/manifests/init.pp
@@ -1,6 +1,6 @@
# manage a basic tor installation
class tor (
- $ensure_version = 'installed'
+ $version = 'installed'
){
include tor::base
}
diff --git a/manifests/munin.pp b/manifests/munin.pp
index 2a01175..f718c37 100644
--- a/manifests/munin.pp
+++ b/manifests/munin.pp
@@ -2,17 +2,19 @@
class tor::munin {
tor::daemon::control{
'control_port_for_munin':
- port => 19051,
+ port => 9001,
cookie_authentication => 1,
cookie_auth_file => '/var/run/tor/control.authcookie',
}
+ include ::tor::daemon::params
Munin::Plugin::Deploy {
- config => "user debian-tor\n env.cookiefile /var/run/tor/control.authcookie\n env.port 19051" # lint:ignore:80chars
+ config => "user ${tor::daemon::params::user}\n env.cookiefile /var/run/tor/control.authcookie\n env.port 9001" # lint:ignore:80chars
}
munin::plugin::deploy {
- 'tor_connections':
- source => 'tor/munin/tor_connections';
+ 'tor_openfds':
+ config => 'user root',
+ source => 'tor/munin/tor_openfds';
'tor_routers':
source => 'tor/munin/tor_routers';
'tor_traffic':
diff --git a/manifests/onionbalance.pp b/manifests/onionbalance.pp
new file mode 100644
index 0000000..1921754
--- /dev/null
+++ b/manifests/onionbalance.pp
@@ -0,0 +1,82 @@
+# manages an onionbalance installation
+#
+# Parameters:
+#
+# services: a hash of onionbalance service instances
+# services => {
+# keyname_of_service1 => {
+# name1 => onionservice_addr_3,
+# name2 => onionservice_addr_2,
+# _key_content => content_of_key_of_onionbalanced_service1,
+# },
+# }
+#
+class tor::onionbalance(
+ $services,
+) {
+
+ include ::tor
+
+ case $facts['osfamily'] {
+ 'Debian': {
+ $pkg_name = 'onionbalance'
+ $instance_file = '/etc/tor/instances/onionbalance/torrc'
+ $instance_user = '_tor-onionbalance'
+ exec{'/usr/sbin/tor-instance-create onionbalance':
+ creates => '/etc/tor/instances/onionbalance',
+ require => Package['tor'],
+ before => File[$instance_file],
+ } -> augeas{"manage_onionbalance_in_group_${instance_user}":
+ context => '/files/etc/group',
+ changes => [ "set ${instance_user}/user[last()+1] onionbalance" ],
+ onlyif => "match ${instance_user}/*[../user='onionbalance'] size == 0",
+ require => Package['onionbalance'],
+ }
+ }
+ 'RedHat': {
+ $instance_file = '/etc/tor/onionbalance.torrc'
+ $instance_user = 'toranon'
+ $pkg_name = 'python2-onionbalance'
+ }
+ default: {
+ fail("OSFamily ${facts['osfamily']} not (yet) supported for onionbalance")
+ }
+ }
+
+ package{$pkg_name:
+ ensure => 'installed',
+ tag => 'onionbalance',
+ } -> file{
+ '/etc/onionbalance/config.yaml':
+ content => template('tor/onionbalance/config.yaml.erb'),
+ owner => root,
+ group => $instance_user,
+ mode => '0640',
+ notify => Service['onionbalance'];
+ $instance_file:
+ content => template("tor/onionbalance/${facts['osfamily']}.torrc.erb"),
+ owner => root,
+ group => 0,
+ mode => '0644',
+ require => Package['tor'],
+ notify => Service['tor@onionbalance'],
+ }
+
+ $keys = keys($services)
+ tor::onionbalance::keys{
+ $keys:
+ values => $services,
+ group => $instance_user,
+ }
+
+ service{
+ 'tor@onionbalance':
+ ensure => running,
+ enable => true;
+ 'onionbalance':
+ ensure => running,
+ enable => true,
+ subscribe => Service['tor@onionbalance'];
+ }
+
+}
diff --git a/manifests/onionbalance/key.pp b/manifests/onionbalance/key.pp
new file mode 100644
index 0000000..e0016fc
--- /dev/null
+++ b/manifests/onionbalance/key.pp
@@ -0,0 +1,25 @@
+# manage onionbalance keys
+#
+# key_content will be treated as path
+# to a file containing the key content
+# if the value starts with a /
+#
+define tor::onionbalance::key(
+ $key_content,
+ $group,
+){
+
+ if $key_content =~ /^\// {
+ $content = file($key_content)
+ } else {
+ $content = $key_content
+ }
+ Package<| tag == 'onionbalance' |> -> file{
+ "/etc/onionbalance/${name}.key":
+ content => $content,
+ owner => root,
+ group => $group,
+ mode => '0640',
+ notify => Service['onionbalance'];
+ }
+}
diff --git a/manifests/onionbalance/keys.pp b/manifests/onionbalance/keys.pp
new file mode 100644
index 0000000..e3040f5
--- /dev/null
+++ b/manifests/onionbalance/keys.pp
@@ -0,0 +1,11 @@
+# a wrapper to manage onionbalance keys
+define tor::onionbalance::keys(
+ $values,
+ $group,
+) {
+ tor::onionbalance::key{
+ $name:
+ key_content => $values[$name]['_key_content'],
+ group => $group,
+ }
+}
diff --git a/manifests/polipo.pp b/manifests/polipo.pp
deleted file mode 100644
index 73dc226..0000000
--- a/manifests/polipo.pp
+++ /dev/null
@@ -1,9 +0,0 @@
-# manage the polipo proxy service
-class tor::polipo {
- include ::tor
-
- case $::operatingsystem {
- 'debian': { include tor::polipo::debian }
- default: { include tor::polipo::base }
- }
-}
diff --git a/manifests/polipo/base.pp b/manifests/polipo/base.pp
deleted file mode 100644
index df2d6ea..0000000
--- a/manifests/polipo/base.pp
+++ /dev/null
@@ -1,22 +0,0 @@
-# manage polipo resources
-class tor::polipo::base {
- package{'polipo':
- ensure => present,
- }
-
- file { '/etc/polipo/config':
- ensure => present,
- owner => root,
- group => root,
- mode => '0644',
- source => 'puppet:///modules/tor/polipo/polipo.conf',
- require => Package['polipo'],
- notify => Service['polipo'],
- }
-
- service { 'polipo':
- ensure => running,
- enable => true,
- require => [ Package['polipo'], Service['tor'] ],
- }
-}
diff --git a/manifests/polipo/debian.pp b/manifests/polipo/debian.pp
deleted file mode 100644
index 607b361..0000000
--- a/manifests/polipo/debian.pp
+++ /dev/null
@@ -1,7 +0,0 @@
-# manage polipo on debian
-class tor::polipo::debian inherits tor::polipo::base {
- Service['polipo'] {
- hasstatus => false,
- pattern => '/usr/bin/polipo',
- }
-}
diff --git a/manifests/repo.pp b/manifests/repo.pp
index 9549219..d9d43ec 100644
--- a/manifests/repo.pp
+++ b/manifests/repo.pp
@@ -1,4 +1,4 @@
-# setup repository for tor
+# add upstream repositories of torproject
class tor::repo (
$ensure = present,
$source_name = 'torproject.org',
@@ -10,6 +10,9 @@ class tor::repo (
$location = 'https://deb.torproject.org/torproject.org/'
class { 'tor::repo::debian': }
}
+ 'RedHat': {
+ # no need as EPEL is the relevant reference
+ }
default: {
fail("Unsupported managed repository for osfamily: ${::osfamily}, operatingsystem: ${::operatingsystem}, module ${module_name} currently only supports managing repos for osfamily Debian and Ubuntu") # lint:ignore:80chars
}
diff --git a/manifests/torsocks.pp b/manifests/torsocks.pp
index e9fc75b..e1ba8a9 100644
--- a/manifests/torsocks.pp
+++ b/manifests/torsocks.pp
@@ -1,9 +1,9 @@
# manage torsocks
class tor::torsocks (
- $ensure_version = 'installed'
+ $version = 'installed'
){
- include ::tor
+ include ::tor::daemon
package{'torsocks':
- ensure => $ensure_version,
+ ensure => $version,
}
}
diff --git a/metadata.json b/metadata.json
index 72e3701..b5a2d9f 100644
--- a/metadata.json
+++ b/metadata.json
@@ -1,6 +1,6 @@
{
"name": "smash-tor",
- "version": "1.0.0",
+ "version": "1.1.0",
"author": "SMASH",
"summary": "This project tracks the tor puppet module that manages the tor onion routing system.",
"license": "GPL-3.0",
@@ -17,12 +17,33 @@
"8",
"9"
]
+ },
+ {
+ "operatingsystem": "CentOS",
+ "operatingsystemrelease": [
+ "7",
+ "6"
+ ]
}
],
"requirements": [
{
"name": "puppet",
- "version_requirement": "4.X"
+ "version_requirement": ">= 4.7.0 < 6.0.0"
+ }
+ ],
+ "dependencies": [
+ {
+ "name": "puppetlabs/stdlib",
+ "version_requirement": ">= 4.24.0 < 5.0.0"
+ },
+ {
+ "name": "puppetlabs/concat",
+ "version_requirement": ">= 4.2.0 < 5.0.0"
+ },
+ {
+ "name": "puppetlabs/apt",
+ "version_requirement": ">= 4.5.1 < 5.0.0"
}
],
"description": "This module tries to manage tor, making sure it is installed, running, has munin graphs if desired and allows for configuration of relays, hidden services, exit policies, etc."
diff --git a/spec/acceptance/nodesets/default.yml b/spec/acceptance/nodesets/default.yml
deleted file mode 100644
index f09ad62..0000000
--- a/spec/acceptance/nodesets/default.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-HOSTS:
- debian-8-x64:
- platform: debian-8-amd64
- image: debian:8
- hypervisor: docker
- docker_preserve_image: true
- docker_cmd: '["/sbin/init"]'
- docker_image_commands:
- - 'apt-get install -y wget locales-all puppet git'
- - 'rm -f /usr/sbin/policy-rc.d'
-
-CONFIG:
- type: foss
- #log_level: verbose
- #log_level: debug
-
-ssh:
- password: root
- auth_methods: ["password"]
diff --git a/spec/classes/base_spec.rb b/spec/classes/base_spec.rb
new file mode 100644
index 0000000..7f288ec
--- /dev/null
+++ b/spec/classes/base_spec.rb
@@ -0,0 +1,44 @@
+require File.expand_path(File.join(File.dirname(__FILE__),'../spec_helper'))
+
+describe 'tor::base', :type => 'class' do
+ let(:default_facts) {
+ {
+ :osfamily => 'RedHat',
+ :operatingsystem => 'CentOS',
+ }
+ }
+ let(:facts){ default_facts }
+ let(:pre_condition){'include ::tor
+ Exec{path => "/bin"}' }
+ describe 'with standard' do
+ it { is_expected.to compile.with_all_deps }
+
+ it { is_expected.to contain_package('tor').with_ensure('installed') }
+ it { is_expected.to_not contain_package('tor-geoipdb').with_ensure('installed') }
+ it { is_expected.to contain_service('tor').with(
+ :ensure => 'running',
+ :enable => 'true',
+ :hasrestart => 'true',
+ :hasstatus => 'true',
+ :require => 'Package[tor]',
+ ) }
+ context 'on Debian' do
+ let(:facts) {
+ {
+ :osfamily => 'Debian',
+ :operatingsystem => 'Debian',
+ }
+ }
+ it { is_expected.to compile.with_all_deps }
+ it { is_expected.to contain_package('tor').with_ensure('installed') }
+ it { is_expected.to contain_package('tor-geoipdb').with_ensure('installed') }
+ it { is_expected.to contain_service('tor').with(
+ :ensure => 'running',
+ :enable => 'true',
+ :hasrestart => 'true',
+ :hasstatus => 'true',
+ :require => 'Package[tor]',
+ ) }
+ end
+ end
+end
diff --git a/spec/classes/daemon_spec.rb b/spec/classes/daemon_spec.rb
new file mode 100644
index 0000000..db5291d
--- /dev/null
+++ b/spec/classes/daemon_spec.rb
@@ -0,0 +1,31 @@
+require File.expand_path(File.join(File.dirname(__FILE__),'../spec_helper'))
+
+describe 'tor::daemon', :type => 'class' do
+ let(:default_facts) {
+ {
+ :osfamily => 'RedHat',
+ :operatingsystem => 'CentOS',
+ }
+ }
+ let(:facts){ default_facts }
+ let(:pre_condition){'Exec{path => "/bin"}' }
+ describe 'with standard' do
+ it { is_expected.to compile.with_all_deps }
+
+ it { is_expected.to contain_class('tor') }
+ it { is_expected.to contain_class('tor::daemon::base') }
+ it { is_expected.to_not contain_class('tor::munin') }
+ context 'on Debian' do
+ let(:facts) {
+ {
+ :osfamily => 'Debian',
+ :operatingsystem => 'Debian',
+ }
+ }
+ it { is_expected.to compile.with_all_deps }
+ it { is_expected.to contain_class('tor') }
+ it { is_expected.to contain_class('tor::daemon::base') }
+ it { is_expected.to_not contain_class('tor::munin') }
+ end
+ end
+end
diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb
new file mode 100644
index 0000000..be4f30b
--- /dev/null
+++ b/spec/classes/init_spec.rb
@@ -0,0 +1,27 @@
+require File.expand_path(File.join(File.dirname(__FILE__),'../spec_helper'))
+
+describe 'tor', :type => 'class' do
+ let(:default_facts) {
+ {
+ :osfamily => 'RedHat',
+ :operatingsystem => 'CentOS',
+ }
+ }
+ let(:facts){ default_facts }
+ let(:pre_condition){'Exec{path => "/bin"}' }
+ describe 'with standard' do
+ it { is_expected.to compile.with_all_deps }
+
+ it { is_expected.to contain_class('tor::base') }
+ context 'on Debian' do
+ let(:facts) {
+ {
+ :osfamily => 'Debian',
+ :operatingsystem => 'Debian',
+ }
+ }
+ it { is_expected.to compile.with_all_deps }
+ it { is_expected.to contain_class('tor::base') }
+ end
+ end
+end
diff --git a/spec/classes/onionbalance_spec.rb b/spec/classes/onionbalance_spec.rb
new file mode 100644
index 0000000..ff9bb27
--- /dev/null
+++ b/spec/classes/onionbalance_spec.rb
@@ -0,0 +1,60 @@
+require File.expand_path(File.join(File.dirname(__FILE__),'../spec_helper'))
+
+describe 'tor::onionbalance', :type => 'class' do
+ let(:default_facts) {
+ {
+ :osfamily => 'RedHat',
+ :operatingsystem => 'CentOS',
+ }
+ }
+ let(:facts){ default_facts }
+ let(:pre_condition){'Exec{path => "/bin"}' }
+ let(:params){
+ {
+ :services => {
+ 'keyname_of_service1' => {
+ 'name1' => 'onionservice_addr_3',
+ 'name2' => 'onionservice_addr_2',
+ '_key_content' => 'content_of_key_of_onionbalanced_service1',
+ },
+ },
+ }
+ }
+ describe 'with standard' do
+ it { is_expected.to compile.with_all_deps }
+
+ it { is_expected.to contain_package('python2-onionbalance').with(
+ :ensure => 'installed',
+ ) }
+ it { is_expected.to contain_service('tor@onionbalance').with(
+ :ensure => 'running',
+ :enable => true,
+ ) }
+ it { is_expected.to contain_service('onionbalance').with(
+ :ensure => 'running',
+ :enable => true,
+ :subscribe => 'Service[tor@onionbalance]',
+ ) }
+ context 'on Debian' do
+ let(:facts) {
+ {
+ :osfamily => 'Debian',
+ :operatingsystem => 'Debian',
+ }
+ }
+ it { is_expected.to compile.with_all_deps }
+ it { is_expected.to contain_package('onionbalance').with(
+ :ensure => 'installed',
+ ) }
+ it { is_expected.to contain_service('tor@onionbalance').with(
+ :ensure => 'running',
+ :enable => true,
+ ) }
+ it { is_expected.to contain_service('onionbalance').with(
+ :ensure => 'running',
+ :enable => true,
+ :subscribe => 'Service[tor@onionbalance]',
+ ) }
+ end
+ end
+end
diff --git a/spec/defines/daemon_onion_service_spec.rb b/spec/defines/daemon_onion_service_spec.rb
new file mode 100644
index 0000000..95be8c4
--- /dev/null
+++ b/spec/defines/daemon_onion_service_spec.rb
@@ -0,0 +1,120 @@
+require File.expand_path(File.join(File.dirname(__FILE__),'../spec_helper'))
+require 'openssl'
+
+describe 'tor::daemon::onion_service', :type => 'define' do
+ let(:default_facts) {
+ {
+ :osfamily => 'RedHat',
+ :operatingsystem => 'CentOS',
+ }
+ }
+ let(:title){ 'test_os' }
+ let(:facts){ default_facts }
+ let(:pre_condition){'Exec{path => "/bin"}
+ include tor::daemon' }
+ describe 'with standard' do
+ it { is_expected.to compile.with_all_deps }
+
+ it { is_expected.to contain_concat__fragment('05.onion_service.test_os').with(
+ :content => /HiddenServiceDir \/var\/lib\/tor\/test_os/,
+ :order => '05',
+ :target => '/etc/tor/torrc',
+ )}
+ it { is_expected.to_not contain_concat__fragment('05.onion_service.test_os').with_content(/^HiddenServicePort/) }
+ it { is_expected.to_not contain_file('/var/lib/tor/test_os') }
+ context 'on Debian' do
+ let(:facts) {
+ {
+ :osfamily => 'Debian',
+ :operatingsystem => 'Debian',
+ }
+ }
+ it { is_expected.to compile.with_all_deps }
+ it { is_expected.to contain_concat__fragment('05.onion_service.test_os').with(
+ :content => /HiddenServiceDir \/var\/lib\/tor\/test_os/,
+ :order => '05',
+ :target => '/etc/tor/torrc',
+ )}
+ it { is_expected.to_not contain_concat__fragment('05.onion_service.test_os').with_content(/^HiddenServicePort/) }
+ it { is_expected.to_not contain_file('/var/lib/tor/test_os') }
+ end
+ context 'with differt port params' do
+ let(:params){
+ {
+ :ports => ['25','443 192.168.0.1:8443']
+ }
+ }
+ it { is_expected.to compile.with_all_deps }
+ it { is_expected.to contain_concat__fragment('05.onion_service.test_os').with_content(/^HiddenServicePort 25 127.0.0.1:25/) }
+ it { is_expected.to contain_concat__fragment('05.onion_service.test_os').with_content(/^HiddenServicePort 443 192.168.0.1:8443/) }
+ it { is_expected.to_not contain_file('/var/lib/tor/test_os') }
+ end
+ context 'with private_key' do
+ let(:params){
+ {
+ :ports => ['80'],
+ :private_key => OpenSSL::PKey::RSA.generate(1024).to_s,
+ }
+ }
+ it { is_expected.to compile.with_all_deps }
+ it { is_expected.to contain_concat__fragment('05.onion_service.test_os').with_content(/^HiddenServicePort 80 127.0.0.1:80/) }
+ it { is_expected.to contain_file('/var/lib/tor/test_os').with(
+ :ensure => 'directory',
+ :purge => true,
+ :force => true,
+ :recurse => true,
+ :owner => 'toranon',
+ :group => 'toranon',
+ :mode => '0600',
+ :require => 'Package[tor]',
+ )}
+ it { is_expected.to contain_file('/var/lib/tor/test_os/hostname').with(
+ :content => /^[a-z2-7]{16}\.onion\n/,
+ :owner => 'toranon',
+ :group => 'toranon',
+ :mode => '0600',
+ :notify => 'Service[tor]',
+ )}
+ it { is_expected.to contain_file('/var/lib/tor/test_os/private_key').with(
+ :owner => 'toranon',
+ :group => 'toranon',
+ :mode => '0600',
+ :notify => 'Service[tor]',
+ )}
+ end
+ context 'with private key to generate' do
+ let(:params){
+ {
+ :ports => ['80'],
+ :private_key_name => 'test_os',
+ :private_key_store_path => File.expand_path(File.join(File.dirname(__FILE__),'..','tmp')),
+ }
+ }
+ it { is_expected.to compile.with_all_deps }
+ it { is_expected.to contain_concat__fragment('05.onion_service.test_os').with_content(/^HiddenServicePort 80 127.0.0.1:80/) }
+ it { is_expected.to contain_file('/var/lib/tor/test_os').with(
+ :ensure => 'directory',
+ :purge => true,
+ :force => true,
+ :recurse => true,
+ :owner => 'toranon',
+ :group => 'toranon',
+ :mode => '0600',
+ :require => 'Package[tor]',
+ )}
+ it { is_expected.to contain_file('/var/lib/tor/test_os/hostname').with(
+ :content => /^[a-z2-7]{16}\.onion\n/,
+ :owner => 'toranon',
+ :group => 'toranon',
+ :mode => '0600',
+ :notify => 'Service[tor]',
+ )}
+ it { is_expected.to contain_file('/var/lib/tor/test_os/private_key').with(
+ :owner => 'toranon',
+ :group => 'toranon',
+ :mode => '0600',
+ :notify => 'Service[tor]',
+ )}
+ end
+ end
+end
diff --git a/spec/functions/generate_onion_key_spec.rb b/spec/functions/generate_onion_key_spec.rb
new file mode 100644
index 0000000..355f862
--- /dev/null
+++ b/spec/functions/generate_onion_key_spec.rb
@@ -0,0 +1,73 @@
+require 'spec_helper'
+require 'fileutils'
+
+describe 'generate_onion_key' do
+ before(:all) do
+ @tmp_path = File.expand_path(File.join(File.dirname(__FILE__),'..','fixtures','tmp'))
+ @test_path = File.join(@tmp_path,'test.key')
+ @drpsyff5srkctr7h_str = "-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQC9OUBOkL73n43ogC/Jma54/ZZDEpoisqpkGJHgbcRGJIxcqqfL
+PbnT3hD5SUCVXxLnzWDCTwTe2VOzIUlBXmslwVXnCJh/XGZg9NHiNU3EAZTwu1g9
+8gNmmG1bymaoEBkuC1osijOj+CN+gzLzApiMbDxddpxTn70LWaSqMDbfdQIDAQAB
+An88nBn9EGAa8QCDeIvWB2PbXV7EHTFB6/ioFzairIYx8YMEK6WTdDIRqw/EybHm
+Jo3nseFMXAMzXmlw9zh/t76ZzE7ooYocSPIEzpu4gDRsa5/mqRCGajs8A8ooiHN5
+Tc9cHzIfhjOYhu3VxF0G9LTAC8nKdWQkHm+h+J6A6+wBAkEA2E6GcIdPGTSfaNRS
+BHOpKUUSvH7W0e5fyYe221EhESdTFjVkaO5YN9HvcqYh27nik0azKgNj6PiE01FC
+0q4fgQJBAN/ycGS3dX5WRXEOpbQ04LKyxCFMVgS+tN5ueDgbv/SxWAxidLYcVfbg
+CcUA+L2OaQ95S97CxYlCLda10vIPOfUCQQCUvQJzFIgOlAHdqsovJ3011Lp6hVmg
+h6K0SK8zhkkPq5PVnKdMBEEDOUfG9XgoyFyF20LN7ADirSlgyesCRhuBAkEAmuCE
+MmNecn0fkUzb9IENVQik85JjeuyZEau8oLEwU/3CMu50YO2/1fijSQee/xlaN0Vf
+3zM8geyu3urodFdrcQJBAMBcecMvo4ddZ/GnwpKJuXEhKSwQfPOeb8lK12NvKuVE
+znq+qT/KbJlwy/27X/auCAzD5rJ9VVzyWiu8nnwICS8=
+-----END RSA PRIVATE KEY-----\n"
+ end
+ describe 'signature validation' do
+ it { is_expected.not_to eq(nil) }
+ it { is_expected.to run.with_params().and_raise_error(Puppet::ParseError, /requires 2 arguments/) }
+ it { is_expected.to run.with_params(1).and_raise_error(Puppet::ParseError, /requires 2 arguments/) }
+ it { is_expected.to run.with_params('/etc/passwd','test').and_raise_error(Puppet::ParseError, /requires location \(\/etc\/passwd\) to be a directory/) }
+ describe 'with a key bigger than 1024' do
+ before(:each) do
+ FileUtils.mkdir_p(@tmp_path) unless File.directory?(@tmp_path)
+ File.open(@test_path,'w'){|f| f << OpenSSL::PKey::RSA.generate(2048) }
+ end
+ it { is_expected.to run.with_params(@tmp_path,'test').and_raise_error(Puppet::ParseError, /must have a length of 1024bit/) }
+ end
+ end
+
+ describe 'normal operation' do
+ before(:all) do
+ FileUtils.rm_rf(@tmp_path) if File.exists?(@tmp_path)
+ FileUtils.mkdir_p(@tmp_path)
+ end
+ after(:all) do
+ FileUtils.rm_rf(@tmp_path) if File.exists?(@tmp_path)
+ end
+ let(:return_value) {
+ scope.function_generate_onion_key([@tmp_path,'test'])
+ }
+ context 'without an existing key' do
+ it 'returns an onion address and a key ' do
+ expect(return_value.size).to be(2)
+ end
+ it 'creates and stores the key' do
+ expect(return_value.last).to be_eql(File.read(File.join(@tmp_path,'test.key')))
+ end
+ it 'returns a proper onion address' do
+ expect(return_value.first).to be_eql(scope.function_onion_address([File.read(File.join(@tmp_path,'test.key'))]))
+ end
+ it 'does not recreate a key once created' do
+ expect(scope.function_generate_onion_key([@tmp_path,'test'])).to be_eql(scope.function_generate_onion_key([@tmp_path,'test']))
+ end
+ it 'creates to different keys for different names' do
+ expect(scope.function_generate_onion_key([@tmp_path,'test']).first).to_not be_eql(scope.function_generate_onion_key([@tmp_path,'test2']))
+ end
+ end
+ context 'with an existing key' do
+ before(:all) do
+ File.open(File.join(@tmp_path,'test3.key'),'w'){|f| f << @drpsyff5srkctr7h_str }
+ end
+ it { is_expected.to run.with_params(@tmp_path,'test3').and_return(['drpsyff5srkctr7h',@drpsyff5srkctr7h_str]) }
+ end
+ end
+end
diff --git a/spec/functions/onion_address_spec.rb b/spec/functions/onion_address_spec.rb
new file mode 100644
index 0000000..942d022
--- /dev/null
+++ b/spec/functions/onion_address_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe 'onion_address' do
+ describe 'signature validation' do
+ it { is_expected.not_to eq(nil) }
+ it { is_expected.to run.with_params().and_raise_error(Puppet::ParseError, /requires 1 argument/) }
+ it { is_expected.to run.with_params(1,2).and_raise_error(Puppet::ParseError, /requires 1 argument/) }
+ end
+
+ describe 'normal operation' do
+ it { is_expected.to run.with_params(
+"-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQC9OUBOkL73n43ogC/Jma54/ZZDEpoisqpkGJHgbcRGJIxcqqfL
+PbnT3hD5SUCVXxLnzWDCTwTe2VOzIUlBXmslwVXnCJh/XGZg9NHiNU3EAZTwu1g9
+8gNmmG1bymaoEBkuC1osijOj+CN+gzLzApiMbDxddpxTn70LWaSqMDbfdQIDAQAB
+An88nBn9EGAa8QCDeIvWB2PbXV7EHTFB6/ioFzairIYx8YMEK6WTdDIRqw/EybHm
+Jo3nseFMXAMzXmlw9zh/t76ZzE7ooYocSPIEzpu4gDRsa5/mqRCGajs8A8ooiHN5
+Tc9cHzIfhjOYhu3VxF0G9LTAC8nKdWQkHm+h+J6A6+wBAkEA2E6GcIdPGTSfaNRS
+BHOpKUUSvH7W0e5fyYe221EhESdTFjVkaO5YN9HvcqYh27nik0azKgNj6PiE01FC
+0q4fgQJBAN/ycGS3dX5WRXEOpbQ04LKyxCFMVgS+tN5ueDgbv/SxWAxidLYcVfbg
+CcUA+L2OaQ95S97CxYlCLda10vIPOfUCQQCUvQJzFIgOlAHdqsovJ3011Lp6hVmg
+h6K0SK8zhkkPq5PVnKdMBEEDOUfG9XgoyFyF20LN7ADirSlgyesCRhuBAkEAmuCE
+MmNecn0fkUzb9IENVQik85JjeuyZEau8oLEwU/3CMu50YO2/1fijSQee/xlaN0Vf
+3zM8geyu3urodFdrcQJBAMBcecMvo4ddZ/GnwpKJuXEhKSwQfPOeb8lK12NvKuVE
+znq+qT/KbJlwy/27X/auCAzD5rJ9VVzyWiu8nnwICS8=
+-----END RSA PRIVATE KEY-----"
+ ).and_return("drpsyff5srkctr7h")}
+ end
+ describe 'by getting an RSA key' do
+ it { is_expected.to run.with_params(OpenSSL::PKey::RSA.new(
+"-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDbvYjbtJB9vTnEygyq4Bzp0xxtTl3ZYKC6JbxgRzP8uLv1HoxX
+20EmQUZ/LNBXHebc6frlObhtpKULFuBzAy5LpdKI9CUErkl3D3AigFgP3XP/PtdP
+m11TuxdBoKL6Jbo54NpUVOGQ5SJJaNEOfhmgMSCtlyyI9DBni3PLO2P0sQIDAQAB
+AoGAPTlt7Gk+6QnUErSJGwMeize67+mp1GtL3RGujtTH8141YHKGf+QjHtmJHt4J
+nnxCWsMGmN+gN0xsf8578w+r0fvDjZ3e5lVUpR/8ds90a654Lr/pgqLc3H1EZ9Pr
+GDFjPdaMtdTSX5hSAB2EDLfDUU19bdFRK+k71mglrMLpdQECQQDmJt3mmX67kAzH
+w2I/BEbmOlonmn3c98VyawoNrk0fKAluoYWHxxk9SuCu2ZDQyyPKPQuZbgdPnUNp
+kV3PuQ6ZAkEA9GtTjMfceX8ArLTmOMIMVP2t8yzbcK2uqukMG79JiPZbYKIstjho
+XUpO/jZhTb9p8M4NV/09z091gMTOF6Fd2QJBAM1I7bS6ROhX3I5yIDfFQNgqRC//
+BTULa/par2T0i6W2uHMNb2VkmYaqOy66sQkLqKjDOo1oLu08gNyw5NRbZEECQQCr
+FDR25a28nNisCjLap3haRPXssAko5WjM2DJReaLO6yEqklkZcoIaSljgNtAEy2Yr
+1w4f+HG7GbL1XsuiXqCBAkAeYljaIVhqGOOez0ORaCm0FCLoTJ6/fn7009os/qgr
+n2xsVGUNm+E0pvAMT0LIx2KvpLxe2Y0Xx497/vyM6e7G
+-----END RSA PRIVATE KEY-----")
+ ).and_return("d3ep6pcs4to4hbwo") }
+ end
+end
diff --git a/spec/puppetlabs_spec_helper_clone.rb b/spec/puppetlabs_spec_helper_clone.rb
new file mode 100644
index 0000000..6a94a3b
--- /dev/null
+++ b/spec/puppetlabs_spec_helper_clone.rb
@@ -0,0 +1,34 @@
+#This file pulls in only the minimum necessary to let unmigrated specs still work
+
+# Define the main module namespace for use by the helper modules
+module PuppetlabsSpec
+ # FIXTURE_DIR represents the standard locations of all fixture data. Normally
+ # this represents <project>/spec/fixtures. This will be used by the fixtures
+ # library to find relative fixture data.
+ FIXTURE_DIR = File.join("spec", "fixtures") unless defined?(FIXTURE_DIR)
+end
+
+# Require all necessary helper libraries so they can be used later
+require 'puppetlabs_spec_helper/puppetlabs_spec/files'
+require 'puppetlabs_spec_helper/puppetlabs_spec/fixtures'
+#require 'puppetlabs_spec_helper/puppetlabs_spec/puppet_internals'
+require 'puppetlabs_spec_helper/puppetlabs_spec/matchers'
+
+RSpec.configure do |config|
+ # Include PuppetlabsSpec helpers so they can be called at convenience
+ config.extend PuppetlabsSpec::Files
+ config.extend PuppetlabsSpec::Fixtures
+ config.include PuppetlabsSpec::Fixtures
+
+ config.parser = 'future' if ENV['FUTURE_PARSER'] == 'yes'
+ config.strict_variables = true if ENV['STRICT_VARIABLES'] == 'yes'
+ config.stringify_facts = false if ENV['STRINGIFY_FACTS'] == 'no'
+ config.trusted_node_data = true if ENV['TRUSTED_NODE_DATA'] == 'yes'
+ config.ordering = ENV['ORDERING'] if ENV['ORDERING']
+
+ # This will cleanup any files that were created with tmpdir or tmpfile
+ config.after :each do
+ PuppetlabsSpec::Files.cleanup
+ end
+end
+
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 3cca63a..66cfb3b 100644..100755
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,12 +1,48 @@
-# https://puppetlabs.com/blog/testing-modules-in-the-puppet-forge
+#! /usr/bin/env ruby -S rspec
+dir = File.expand_path(File.dirname(__FILE__))
+$LOAD_PATH.unshift File.join(dir, 'lib')
+
+# So everyone else doesn't have to include this base constant.
+module PuppetSpec
+ FIXTURE_DIR = File.join(dir = File.expand_path(File.dirname(__FILE__)), "fixtures") unless defined?(FIXTURE_DIR)
+end
+
+require 'puppet'
+require 'rspec-puppet'
require 'puppetlabs_spec_helper/module_spec_helper'
+require 'mocha/api'
+#require 'puppetlabs_spec_helper/module_spec_helper'
+require 'puppetlabs_spec_helper_clone'
+
+# hack to enable all the expect syntax (like allow_any_instance_of) in rspec-puppet examples
+RSpec::Mocks::Syntax.enable_expect(RSpec::Puppet::ManifestMatchers)
-fixture_path = File.expand_path(File.join(__FILE__, '..', 'fixtures'))
+RSpec.configure do |config|
+ config.module_path = File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures', 'modules')
+ config.manifest_dir = File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures', 'manifests')
+ config.environmentpath = spec_path = File.expand_path(File.join(Dir.pwd, 'spec'))
-RSpec.configure do |c|
+ config.add_setting :puppet_future
+ #config.puppet_future = (ENV['FUTURE_PARSER'] == 'yes' or Puppet.version.to_f >= 4.0)
+ config.puppet_future = Puppet.version.to_f >= 4.0
- c.manifest_dir = File.join(fixture_path, 'manifests')
- c.module_path = File.join(fixture_path, 'modules')
+ config.before :each do
+ # Ensure that we don't accidentally cache facts and environment between
+ # test cases. This requires each example group to explicitly load the
+ # facts being exercised with something like
+ # Facter.collection.loader.load(:ipaddress)
+ Facter.clear
+ Facter.clear_messages
+
+ RSpec::Mocks.setup
+ end
+
+ config.after :each do
+ RSpec::Mocks.verify
+ RSpec::Mocks.teardown
+ end
+end
- c.color = true
+# Helper class to test handling of arguments which are derived from string
+class AlsoString < String
end
diff --git a/spec/spec_helper_acceptance.rb b/spec/spec_helper_acceptance.rb
deleted file mode 100644
index e6a2cd8..0000000
--- a/spec/spec_helper_acceptance.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require 'beaker-rspec'
-
-RSpec.configure do |c|
- module_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
- module_name = module_root.split('-').last
-
- # Readable test descriptions
- c.formatter = :documentation
-
- # Configure all nodes in nodeset
- c.before :suite do
- # Install module and dependencies
- puppet_module_install(:source => module_root, :module_name => module_name)
- hosts.each do |host|
- shell('git clone https://github.com/puppetlabs/puppetlabs-concat.git /etc/puppet/modules/concat')
- end
- end
-end
diff --git a/spec/tmp/.keep b/spec/tmp/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/spec/tmp/.keep
diff --git a/templates/onionbalance/Debian.torrc.erb b/templates/onionbalance/Debian.torrc.erb
new file mode 100644
index 0000000..4b1afc4
--- /dev/null
+++ b/templates/onionbalance/Debian.torrc.erb
@@ -0,0 +1,5 @@
+# Tor config for the onionbalance management server
+# ---
+# The onionbalance service must be able to access the Tor control port.
+ControlPort 9051
+SocksPort 0
diff --git a/templates/onionbalance/RedHat.torrc.erb b/templates/onionbalance/RedHat.torrc.erb
new file mode 100644
index 0000000..023748b
--- /dev/null
+++ b/templates/onionbalance/RedHat.torrc.erb
@@ -0,0 +1,13 @@
+# Tor config for the onionbalance management server
+# ---
+# The onionbalance service must be able to access the Tor control port.
+
+DataDirectory /var/lib/tor/onionbalance-data
+
+ControlPort 9051
+CookieAuthentication 1
+SocksPort 0
+
+CookieAuthFileGroupReadable 1
+CookieAuthFile /run/tor/onionbalance.control.authcookie
+
diff --git a/templates/onionbalance/config.yaml.erb b/templates/onionbalance/config.yaml.erb
new file mode 100644
index 0000000..b1d4d80
--- /dev/null
+++ b/templates/onionbalance/config.yaml.erb
@@ -0,0 +1,10 @@
+# OnionBalance Config File
+services:
+<% @services.keys.sort.each do |key| -%>
+ - key: /etc/onionbalance/<%= key %>.key
+ instances:
+<% (@services[key].keys - ['_key_content']).sort.each do |inst| -%>
+ - address: <%= @services[key][inst] %>
+ name: <%= inst %>
+<% end -%>
+<% end -%>
diff --git a/templates/torrc.global.erb b/templates/torrc.global.erb
index c29b76b..763e63f 100644
--- a/templates/torrc.global.erb
+++ b/templates/torrc.global.erb
@@ -1,24 +1,24 @@
# runtime
RunAsDaemon 1
-<% if (v=scope.lookupvar('tor::daemon::data_dir')) != '/var/lib/tor' -%>
+<% if (v=scope['tor::daemon::data_dir']) != '/var/lib/tor' -%>
DataDirectory <%= v %>
<% end -%>
# log
-<% if (rules=scope.lookupvar('tor::daemon::log_rules')).empty? -%>
+<% if (rules=scope['tor::daemon::log_rules']).empty? -%>
Log notice syslog
<% else -%>
<% Array(rules).each do |log_rule| -%>
Log <%= log_rule %>
-<% end -%>
-<% end -%>
-<%- if (v=scope.lookupvar('tor::daemon::safe_logging')) != '1' then -%>
+<% end
+end -%>
+<% if (v=scope['tor::daemon::safe_logging']) != 1 -%>
SafeLogging <%= v %>
-<%- end -%>
+<% end -%>
-<% if (v=scope.lookupvar('tor::daemon::automap_hosts_on_resolve')) != '0' -%>
+<% if (v=scope['tor::daemon::automap_hosts_on_resolve']) != 0 -%>
AutomapHostsOnResolve <%= v %>
-<% end -%>
-<% if (v=scope.lookupvar('tor::daemon::use_bridges')) != '0' -%>
+<% end -%>
+<% if (v=scope['tor::daemon::use_bridges']) != 0 -%>
UseBridges <%= v %>
-<%- end -%>
+<% end -%>
diff --git a/templates/torrc.hidden_service.erb b/templates/torrc.onion_service.erb
index c7dbe9e..9b05c43 100644
--- a/templates/torrc.hidden_service.erb
+++ b/templates/torrc.onion_service.erb
@@ -1,20 +1,20 @@
-<% if @single_hop != false %>
+<% if @single_hop != false -%>
HiddenServiceSingleHopMode 1
HiddenServiceNonAnonymousMode 1
SOCKSPort 0
-<% end %>
+<% end -%>
# hidden service <%= @name %>
-HiddenServiceDir <%= @data_dir %>/<%= @name %>
+HiddenServiceDir <%= @data_dir_path %>
<% Array(@ports).each do |port| -%>
-HiddenServicePort <%= port %>
+HiddenServicePort <%= port =~ /^\d+$/ ? "#{port} 127.0.0.1:#{port}" : port %>
<% end -%>
-<% if @v3 != false %>
+<% if @v3 != false -%>
# hidden service v3 static
-HiddenServiceDir <%= @data_dir %>/<%= @name -%>3
+HiddenServiceDir <%= @data_dir_path %>3
HiddenServiceVersion 3
<% Array(@ports).each do |port| -%>
HiddenServicePort <%= port %>
-<% end -%>
-<% end -%>
+<% end
+end -%>