summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph Kluenter <ckluente@thoughtworks.com>2014-12-04 12:09:10 +0100
committerChristoph Kluenter <ckluente@thoughtworks.com>2014-12-04 12:09:10 +0100
commitd063e35d3e29b3cedc810b8e5ca1855c841d8f9e (patch)
tree06e5110632156a35e6e879a9fa0455edf62f05bf
parent664dca31dec0c7935ee96359209d9dcefc03e38c (diff)
parentde51b83384d97a67cdbdf1992ba9ad771a292c5d (diff)
Merge remote-tracking branch 'leap/develop' into check_dhcp
-rwxr-xr-xbin/puppet_command5
-rwxr-xr-xbin/run_tests232
-rw-r--r--platform.rb30
-rw-r--r--provider_base/common.json4
-rw-r--r--provider_base/files/service-definitions/v1/eip-service.json.erb12
-rw-r--r--provider_base/lib/macros.rb1
-rw-r--r--provider_base/lib/macros/files.rb16
-rw-r--r--provider_base/lib/macros/keys.rb82
-rw-r--r--provider_base/provider.json3
-rw-r--r--provider_base/services/_couchdb_multimaster.json6
-rw-r--r--provider_base/services/monitor.json1
-rw-r--r--provider_base/services/openvpn.json3
-rw-r--r--provider_base/services/tor.json9
-rw-r--r--provider_base/services/webapp.json3
m---------puppet/modules/couchdb0
-rw-r--r--puppet/modules/site_apache/templates/vhosts.d/api.conf.erb6
-rw-r--r--puppet/modules/site_apache/templates/vhosts.d/common.conf.erb6
-rw-r--r--puppet/modules/site_apache/templates/vhosts.d/hidden_service.conf.erb33
-rw-r--r--puppet/modules/site_apt/files/Debian/50unattended-upgrades16
-rw-r--r--puppet/modules/site_apt/manifests/init.pp4
-rw-r--r--puppet/modules/site_apt/manifests/leap_repo.pp5
-rw-r--r--puppet/modules/site_apt/manifests/preferences/rsyslog.pp14
-rw-r--r--puppet/modules/site_apt/manifests/unattended_upgrades.pp10
-rw-r--r--puppet/modules/site_check_mk/files/host_contactgroups.mk3
-rw-r--r--puppet/modules/site_check_mk/manifests/server.pp18
-rw-r--r--puppet/modules/site_check_mk/templates/hostgroups.mk4
-rw-r--r--puppet/modules/site_config/manifests/default.pp6
-rw-r--r--puppet/modules/site_couchdb/files/runit_config6
-rw-r--r--puppet/modules/site_couchdb/manifests/bigcouch.pp18
-rw-r--r--puppet/modules/site_couchdb/manifests/init.pp8
-rw-r--r--puppet/modules/site_nagios/manifests/server.pp17
-rw-r--r--puppet/modules/site_nagios/manifests/server/hostgroup.pp3
-rw-r--r--puppet/modules/site_nickserver/templates/nickserver-proxy.conf.erb5
-rw-r--r--puppet/modules/site_obfsproxy/manifests/init.pp14
-rw-r--r--puppet/modules/site_openvpn/manifests/init.pp16
-rw-r--r--puppet/modules/site_openvpn/manifests/server_config.pp12
-rw-r--r--puppet/modules/site_shorewall/manifests/dnat_rule.pp12
-rw-r--r--puppet/modules/site_sshd/manifests/init.pp2
-rw-r--r--puppet/modules/site_static/templates/apache.conf.erb4
-rw-r--r--puppet/modules/site_stunnel/manifests/client.pp5
-rw-r--r--puppet/modules/site_stunnel/manifests/init.pp2
-rw-r--r--puppet/modules/site_stunnel/manifests/override_service.pp13
-rw-r--r--puppet/modules/site_stunnel/manifests/servers.pp5
-rw-r--r--puppet/modules/site_tor/manifests/init.pp28
-rw-r--r--puppet/modules/site_webapp/manifests/hidden_service.pp43
-rw-r--r--puppet/modules/site_webapp/manifests/init.pp8
-rw-r--r--puppet/modules/site_webapp/templates/config.yml.erb1
m---------puppet/modules/sshd0
-rw-r--r--tests/README.md27
-rw-r--r--tests/helpers/couchdb_helper.rb103
-rw-r--r--tests/helpers/files_helper.rb54
-rw-r--r--tests/helpers/http_helper.rb145
-rw-r--r--tests/helpers/network_helper.rb79
-rw-r--r--tests/helpers/os_helper.rb34
-rw-r--r--tests/helpers/srp_helper.rb171
-rw-r--r--tests/order.rb4
-rw-r--r--tests/white-box/couchdb.rb60
-rw-r--r--tests/white-box/mx.rb50
-rw-r--r--tests/white-box/openvpn.rb4
-rw-r--r--tests/white-box/soledad.rb17
-rw-r--r--tests/white-box/webapp.rb143
61 files changed, 1269 insertions, 376 deletions
diff --git a/bin/puppet_command b/bin/puppet_command
index 5e690bef..cdb0b027 100755
--- a/bin/puppet_command
+++ b/bin/puppet_command
@@ -14,6 +14,8 @@ PUPPET_BIN = '/usr/bin/puppet'
PUPPET_DIRECTORY = '/srv/leap'
PUPPET_PARAMETERS = '--color=false --detailed-exitcodes --libdir=puppet/lib --confdir=puppet'
SITE_MANIFEST = 'puppet/manifests/site.pp'
+SITE_MODULES = 'puppet/modules'
+CUSTOM_MODULES = ':files/puppet/modules'
DEFAULT_TAGS = 'leap_base,leap_service'
HIERA_FILE = '/etc/leap/hiera.yaml'
@@ -93,10 +95,11 @@ end
def puppet_apply(options={}, &block)
options = {:verbosity => @verbosity, :tags => @tags}.merge(options)
manifest = options[:manifest] || SITE_MANIFEST
+ modulepath = options[:module_path] || SITE_MODULES + CUSTOM_MODULES
fqdn = hiera_file['domain']['name']
domain = hiera_file['domain']['full_suffix']
Dir.chdir(PUPPET_DIRECTORY) do
- return run("FACTER_fqdn='#{fqdn}' FACTER_domain='#{domain}' #{PUPPET_BIN} apply #{custom_parameters(options)} #{PUPPET_PARAMETERS} #{manifest}", &block)
+ return run("FACTER_fqdn='#{fqdn}' FACTER_domain='#{domain}' #{PUPPET_BIN} apply #{custom_parameters(options)} --modulepath='#{modulepath}' #{PUPPET_PARAMETERS} #{manifest}", &block)
end
end
diff --git a/bin/run_tests b/bin/run_tests
index e026b5f7..4addc0c8 100755
--- a/bin/run_tests
+++ b/bin/run_tests
@@ -14,7 +14,6 @@
require 'minitest/unit'
require 'yaml'
require 'tsort'
-require 'net/http'
##
## EXIT CODES
@@ -37,6 +36,14 @@ def bail(code, msg=nil)
end
##
+## UTILITY
+##
+
+def service?(service)
+ $node["services"].include?(service.to_s)
+end
+
+##
## EXCEPTIONS
##
@@ -114,7 +121,10 @@ class LeapTest < MiniTest::Unit::TestCase
# the default fail() is part of the kernel and it just throws a runtime exception. for tests,
# we want the same behavior as assert(false)
#
- def fail(msg=nil)
+ def fail(msg=nil, exception=nil)
+ if DEBUG && exception && exception.respond_to?(:backtrace)
+ msg += MiniTest::filter_backtrace(exception.backtrace).join "\n"
+ end
assert(false, msg)
end
@@ -129,207 +139,6 @@ class LeapTest < MiniTest::Unit::TestCase
:alpha
end
- #
- # attempts a http GET on the url, yields |body, response, error|
- #
- def get(url, params=nil)
- uri = URI(url)
- if params
- uri.query = URI.encode_www_form(params)
- end
- http = Net::HTTP.new uri.host, uri.port
- if uri.scheme == 'https'
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
- http.use_ssl = true
- end
- http.start do |agent|
- request = Net::HTTP::Get.new uri.request_uri
- if uri.user
- request.basic_auth uri.user, uri.password
- end
- response = agent.request(request)
- if response.is_a?(Net::HTTPSuccess)
- yield response.body, response, nil
- else
- yield nil, response, nil
- end
- end
- rescue => exc
- yield nil, nil, exc
- end
-
- def assert_get(url, params=nil, options=nil)
- options ||= {}
- get(url, params) do |body, response, error|
- if body
- yield body if block_given?
- elsif response
- fail ["Expected a 200 status code from #{url}, but got #{response.code} instead.", options[:error_msg]].compact.join("\n")
- else
- fail ["Expected a response from #{url}, but got \"#{error}\" instead.", options[:error_msg]].compact.join("\n")
- end
- end
- end
-
- #
- # only a warning for now, should be a failure in the future
- #
- def assert_auth_fail(url, params)
- uri = URI(url)
- get(url, params) do |body, response, error|
- unless response.code.to_s == "401"
- warn "Expected a '401 Unauthorized' response, but got #{response.code} instead (GET #{uri.request_uri} with username '#{uri.user}')."
- return false
- end
- end
- true
- end
-
- #
- # test if a socket can be connected to
- #
-
- #
- # tcp connection helper with timeout
- #
- def try_tcp_connect(host, port, timeout = 5)
- addr = Socket.getaddrinfo(host, nil)
- sockaddr = Socket.pack_sockaddr_in(port, addr[0][3])
-
- Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0).tap do |socket|
- socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
- begin
- socket.connect_nonblock(sockaddr)
- rescue IO::WaitReadable
- if IO.select([socket], nil, nil, timeout) == nil
- raise "Connection timeout"
- else
- socket.connect_nonblock(sockaddr)
- end
- rescue IO::WaitWritable
- if IO.select(nil, [socket], nil, timeout) == nil
- raise "Connection timeout"
- else
- socket.connect_nonblock(sockaddr)
- end
- end
- return socket
- end
- end
-
- def try_tcp_write(socket, timeout = 5)
- begin
- socket.write_nonblock("\0")
- rescue IO::WaitReadable
- if IO.select([socket], nil, nil, timeout) == nil
- raise "Write timeout"
- else
- retry
- end
- rescue IO::WaitWritable
- if IO.select(nil, [socket], nil, timeout) == nil
- raise "Write timeout"
- else
- retry
- end
- end
- end
-
- def try_tcp_read(socket, timeout = 5)
- begin
- socket.read_nonblock(1)
- rescue IO::WaitReadable
- if IO.select([socket], nil, nil, timeout) == nil
- raise "Read timeout"
- else
- retry
- end
- rescue IO::WaitWritable
- if IO.select(nil, [socket], nil, timeout) == nil
- raise "Read timeout"
- else
- retry
- end
- end
- end
-
- def assert_tcp_socket(host, port, msg=nil)
- begin
- socket = try_tcp_connect(host, port, 1)
- #try_tcp_write(socket,1)
- #try_tcp_read(socket,1)
- rescue StandardError => exc
- fail ["Failed to open socket #{host}:#{port}", exc].join("\n")
- ensure
- socket.close if socket
- end
- end
-
- #
- # Matches the regexp in the file, and returns the first matched string (or fails if no match).
- #
- def file_match(filename, regexp)
- if match = File.read(filename).match(regexp)
- match.captures.first
- else
- fail "Regexp #{regexp.inspect} not found in file #{filename.inspect}."
- end
- end
-
- #
- # Matches the regexp in the file, and returns array of matched strings (or fails if no match).
- #
- def file_matches(filename, regexp)
- if match = File.read(filename).match(regexp)
- match.captures
- else
- fail "Regexp #{regexp.inspect} not found in file #{filename.inspect}."
- end
- end
-
- #
- # checks to make sure the given property path exists in $node (e.g. hiera.yaml)
- # and returns the value
- #
- def assert_property(property)
- latest = $node
- property.split('.').each do |segment|
- latest = latest[segment]
- fail "Required node property `#{property}` is missing." if latest.nil?
- end
- return latest
- end
-
- #
- # works like pgrep command line
- # return an array of hashes like so [{:pid => "1234", :process => "ls"}]
- #
- def pgrep(match)
- output = `pgrep --full --list-name '#{match}'`
- output.each_line.map{|line|
- pid = line.split(' ')[0]
- process = line.gsub(/(#{pid} |\n)/, '')
- if process =~ /pgrep --full --list-name/
- nil
- else
- {:pid => pid, :process => process}
- end
- }.compact
- end
-end
-
-def assert_running(process)
- assert pgrep(process).any?, "No running process for #{process}"
-end
-
-#
-# runs the specified command, failing on a non-zero exit status.
-#
-def assert_run(command)
- output = `#{command}`
- if $?.exitstatus != 0
- fail "Error running `#{command}`:\n#{output}"
- end
end
#
@@ -441,7 +250,7 @@ class LeapRunner < MiniTest::Unit
def report_line(prefix, klass, meth, e=nil, message=nil)
msg_txt = nil
if message
- message = message.sub(/http:\/\/([a-z_]+):([a-zA-Z0-9_]+)@/, "http://\\1:password@")
+ message = message.sub(/http:\/\/([a-z_]+):([a-zA-Z0-9_]+)@/, "http://\\1:REDACTED@")
if $output_format == :human
indent = "\n "
msg_txt = indent + message.split("\n").join(indent)
@@ -556,7 +365,8 @@ def print_help
" --test TEST Run only the test with name TEST.",
" --list-tests Prints the names of all available tests and exit.",
" --retry COUNT If the tests don't pass, retry COUNT additional times (default is zero)",
- " --wait SECONDS Wait for SECONDS between retries (default is 5)"].join("\n")
+ " --wait SECONDS Wait for SECONDS between retries (default is 5)",
+ " --debug Print out full stack trace on errors"].join("\n")
exit(0)
end
@@ -615,6 +425,9 @@ def main
# load all test classes
this_file = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
+ Dir[File.expand_path('../../tests/helpers/*.rb', this_file)].each do |helper|
+ require helper
+ end
Dir[File.expand_path('../../tests/white-box/*.rb', this_file)].each do |test_file|
begin
require test_file
@@ -636,10 +449,19 @@ def main
when '--list-tests' then list_tests
when '--retry' then ARGV.shift; $retry = ARGV.shift.to_i
when '--wait' then ARGV.shift; $wait = ARGV.shift.to_i
+ when '--debug' then ARGV.shift
+ when '-d' then ARGV.shift
else break
end
end
run_tests
end
+if ARGV.include?('--debug') || ARGV.include?('-d')
+ DEBUG=true
+ require 'debugger'
+else
+ DEBUG=false
+end
+
main()
diff --git a/platform.rb b/platform.rb
index 270dd25a..c37b6d29 100644
--- a/platform.rb
+++ b/platform.rb
@@ -4,8 +4,8 @@
#
Leap::Platform.define do
- self.version = "0.5.4.1"
- self.compatible_cli = "1.5.8".."1.99"
+ self.version = "0.6"
+ self.compatible_cli = "1.6.1".."1.99"
#
# the facter facts that should be gathered
@@ -13,7 +13,16 @@ Leap::Platform.define do
self.facts = ["ec2_local_ipv4", "ec2_public_ipv4"]
#
+ # absolute paths on the destination server
+ #
+ self.hiera_path = '/etc/leap/hiera.yaml'
+ self.leap_dir = '/srv/leap'
+ self.files_dir = '/srv/leap/files'
+ self.init_path = '/srv/leap/initialized'
+
+ #
# the named paths for this platform
+ # (relative to the provider directory)
#
self.paths = {
# directories
@@ -43,6 +52,11 @@ Leap::Platform.define do
:soledad_service_json_template => 'files/service-definitions/#{arg}/soledad-service.json.erb',
:smtp_service_json_template => 'files/service-definitions/#{arg}/smtp-service.json.erb',
+ # custom puppet
+ :custom_puppet_dir => 'files/puppet',
+ :custom_puppet_modules_dir => 'files/puppet/modules',
+ :custom_puppet_manifests_dir => 'files/puppet/manifests',
+
# output files
:facts => 'facts.json',
:user_ssh => 'users/#{arg}/#{arg}_ssh.pub',
@@ -63,10 +77,12 @@ Leap::Platform.define do
:vagrantfile => 'test/Vagrantfile',
# node output files
- :hiera => 'hiera/#{arg}.yaml',
- :node_ssh_pub_key => 'files/nodes/#{arg}/#{arg}_ssh.pub',
- :node_x509_key => 'files/nodes/#{arg}/#{arg}.key',
- :node_x509_cert => 'files/nodes/#{arg}/#{arg}.crt',
+ :hiera => 'hiera/#{arg}.yaml',
+ :node_ssh_pub_key => 'files/nodes/#{arg}/#{arg}_ssh.pub',
+ :node_x509_key => 'files/nodes/#{arg}/#{arg}.key',
+ :node_x509_cert => 'files/nodes/#{arg}/#{arg}.crt',
+ :node_tor_priv_key => 'files/nodes/#{arg}/tor.key',
+ :node_tor_pub_key => 'files/nodes/#{arg}/tor.pub',
# testing files
:test_client_key => 'test/cert/client.key',
@@ -85,5 +101,7 @@ Leap::Platform.define do
self.monitor_username = 'monitor'
self.reserved_usernames = ['monitor']
+
+ self.default_puppet_tags = ['leap_base','leap_service']
end
diff --git a/provider_base/common.json b/provider_base/common.json
index 87af2152..649db0d9 100644
--- a/provider_base/common.json
+++ b/provider_base/common.json
@@ -46,5 +46,9 @@
"stunnel": {
"clients": {},
"servers": {}
+ },
+ "platform": {
+ "version": "= Leap::Platform.version.to_s",
+ "major_version": "= Leap::Platform.major_version"
}
}
diff --git a/provider_base/files/service-definitions/v1/eip-service.json.erb b/provider_base/files/service-definitions/v1/eip-service.json.erb
index 3b8976fd..4bd220df 100644
--- a/provider_base/files/service-definitions/v1/eip-service.json.erb
+++ b/provider_base/files/service-definitions/v1/eip-service.json.erb
@@ -42,8 +42,14 @@
end
configuration = node.openvpn.configuration
end
- hsh["gateways"] = gateways.compact
- hsh["locations"] = locations
- hsh["openvpn_configuration"] = configuration
+ if gateways.any?
+ configuration = configuration.dup
+ if configuration['fragment'] && configuration['fragment'] == 1500
+ configuration.delete('fragment')
+ end
+ hsh["gateways"] = gateways.compact
+ hsh["locations"] = locations
+ hsh["openvpn_configuration"] = configuration
+ end
JSON.sorted_generate hsh
%> \ No newline at end of file
diff --git a/provider_base/lib/macros.rb b/provider_base/lib/macros.rb
index 854b92b5..ecc3e6ba 100644
--- a/provider_base/lib/macros.rb
+++ b/provider_base/lib/macros.rb
@@ -9,6 +9,7 @@ require_relative 'macros/core'
require_relative 'macros/files'
require_relative 'macros/haproxy'
require_relative 'macros/hosts'
+require_relative 'macros/keys'
require_relative 'macros/nodes'
require_relative 'macros/secrets'
require_relative 'macros/stunnel'
diff --git a/provider_base/lib/macros/files.rb b/provider_base/lib/macros/files.rb
index 0a491325..958958bc 100644
--- a/provider_base/lib/macros/files.rb
+++ b/provider_base/lib/macros/files.rb
@@ -48,13 +48,22 @@ module LeapCli
# * if the path does not exist locally, but exists in provider_base, then the default file from
# provider_base is copied locally. this is required for rsync to work correctly.
#
- def file_path(path)
+ def file_path(path, options={})
if path.is_a? Symbol
path = [path, @node.name]
+ elsif path.is_a? String
+ # ensure it prefixed with files/
+ unless path =~ /^files\//
+ path = "files/" + path
+ end
end
actual_path = Path.find_file(path)
if actual_path.nil?
- Util::log 2, :skipping, "file_path(\"#{path}\") because there is no such file."
+ if options[:missing]
+ raise FileMissing.new(Path.named_path(path), options)
+ else
+ Util::log 2, :skipping, "file_path(\"#{path}\") because there is no such file."
+ end
nil
else
if actual_path =~ /^#{Regexp.escape(Path.provider_base)}/
@@ -70,8 +79,9 @@ module LeapCli
actual_path += '/' # ensure directories end with /, important for building rsync command
end
relative_path = Path.relative_path(actual_path)
+ relative_path.sub!(/^files\//, '') # remove "files/" prefix
@node.file_paths << relative_path
- @node.manager.provider.hiera_sync_destination + '/' + relative_path
+ File.join(Leap::Platform.files_dir, relative_path)
end
end
diff --git a/provider_base/lib/macros/keys.rb b/provider_base/lib/macros/keys.rb
new file mode 100644
index 00000000..ea4c3df2
--- /dev/null
+++ b/provider_base/lib/macros/keys.rb
@@ -0,0 +1,82 @@
+# encoding: utf-8
+
+#
+# Macro for dealing with cryptographic keys
+#
+
+module LeapCli
+ module Macro
+
+ #
+ # return the path to the tor public key
+ # generating key if it is missing
+ #
+ def tor_public_key_path(path_name, key_type)
+ path = file_path(path_name)
+ if path.nil?
+ generate_tor_key(key_type)
+ file_path(path_name)
+ else
+ path
+ end
+ end
+
+ #
+ # return the path to the tor private key
+ # generating key if it is missing
+ #
+ def tor_private_key_path(path_name, key_type)
+ path = file_path(path_name)
+ if path.nil?
+ generate_tor_key(key_type)
+ file_path(path_name)
+ else
+ path
+ end
+ end
+
+ #
+ # on the command line an onion address can be created
+ # from an rsa public key using this:
+ #
+ # base64 -d < ./pubkey | sha1sum | awk '{print $1}' |
+ # perl -e '$l=<>; chomp $l; print pack("H*", $l)' |
+ # python -c 'import base64, sys; t=sys.stdin.read(); print base64.b32encode(t[:10]).lower()'
+ #
+ # path_name is the named path of the tor public key.
+ #
+ def onion_address(path_name)
+ require 'base32'
+ require 'base64'
+ require 'openssl'
+ path = Path.find_file([path_name, self.name])
+ if path && File.exists?(path)
+ public_key_str = File.readlines(path).grep(/^[^-]/).join
+ public_key = Base64.decode64(public_key_str)
+ sha1sum_string = Digest::SHA1.new.hexdigest(public_key)
+ sha1sum_binary = [sha1sum_string].pack('H*')
+ Base32.encode(sha1sum_binary.slice(0,10)).downcase
+ else
+ LeapCli.log :warning, 'Tor public key file "%s" does not exist' % tor_public_key_path
+ end
+ end
+
+ private
+
+ def generate_tor_key(key_type)
+ if key_type == 'RSA'
+ require 'certificate_authority'
+ keypair = CertificateAuthority::MemoryKeyMaterial.new
+ bit_size = 1024
+ LeapCli.log :generating, "%s bit RSA Tor key" % bit_size do
+ keypair.generate_key(bit_size)
+ LeapCli::Util.write_file! [:node_tor_priv_key, self.name], keypair.private_key.to_pem
+ LeapCli::Util.write_file! [:node_tor_pub_key, self.name], keypair.public_key.to_pem
+ end
+ else
+ LeapCli.bail! 'tor.key.type of %s is not yet supported' % key_type
+ end
+ end
+
+ end
+end
diff --git a/provider_base/provider.json b/provider_base/provider.json
index 743964ee..9ef0f76a 100644
--- a/provider_base/provider.json
+++ b/provider_base/provider.json
@@ -44,7 +44,7 @@
"digest": "SHA256",
"life_span": "10y",
"server_certificates": {
- "bit_size": 2048,
+ "bit_size": 4096,
"digest": "SHA256",
"life_span": "1y"
},
@@ -56,7 +56,6 @@
"unlimited_prefix": "UNLIMITED"
}
},
- "hiera_sync_destination": "/etc/leap",
"client_version": {
"min": "0.5",
"max": null
diff --git a/provider_base/services/_couchdb_multimaster.json b/provider_base/services/_couchdb_multimaster.json
index 8c433188..0f340e00 100644
--- a/provider_base/services/_couchdb_multimaster.json
+++ b/provider_base/services/_couchdb_multimaster.json
@@ -8,8 +8,8 @@
"ednp_server": "= stunnel_server(couch.bigcouch.ednp_port)"
},
"clients": {
- "epmd_clients": "= stunnel_client(nodes_like_me[:services => :couchdb], couch.bigcouch.epmd_port)",
- "ednp_clients": "= stunnel_client(nodes_like_me[:services => :couchdb], couch.bigcouch.ednp_port)"
+ "epmd_clients": "= stunnel_client(nodes_like_me['services' => 'couchdb']['couch.mode' => 'multimaster'], couch.bigcouch.epmd_port)",
+ "ednp_clients": "= stunnel_client(nodes_like_me['services' => 'couchdb']['couch.mode' => 'multimaster'], couch.bigcouch.ednp_port)"
}
},
"couch": {
@@ -18,7 +18,7 @@
"epmd_port": 4369,
"ednp_port": 9002,
"cookie": "= secret :bigcouch_cookie",
- "neighbors": "= nodes_like_me['services' => 'couchdb']['couch.master' => true].exclude(self).field('domain.full')"
+ "neighbors": "= nodes_like_me['services' => 'couchdb']['couch.mode' => 'multimaster'].exclude(self).field('domain.full')"
}
}
}
diff --git a/provider_base/services/monitor.json b/provider_base/services/monitor.json
index c24724bf..56ca015b 100644
--- a/provider_base/services/monitor.json
+++ b/provider_base/services/monitor.json
@@ -1,6 +1,7 @@
{
"nagios": {
"nagiosadmin_pw": "= secret :nagios_admin_password",
+ "domains_internal": "= global.tags.field('domain.internal_suffix').compact.uniq",
"hosts": "= (self.environment == 'local' ? nodes_like_me : nodes[:environment => '!local']).pick_fields('domain.internal', 'domain.full_suffix', 'ip_address', 'services', 'openvpn.gateway_address', 'ssh.port')"
},
"hosts": "= self.environment == 'local' ? hosts_file(nodes_like_me) : hosts_file(nodes[:environment => '!local'])",
diff --git a/provider_base/services/openvpn.json b/provider_base/services/openvpn.json
index 1906244c..11cb0dc2 100644
--- a/provider_base/services/openvpn.json
+++ b/provider_base/services/openvpn.json
@@ -24,7 +24,8 @@
"auth": "SHA1",
"cipher": "AES-128-CBC",
"keepalive": "10 30",
- "tun-ipv6": true
+ "tun-ipv6": true,
+ "fragment": 1500
}
},
"obfsproxy": {
diff --git a/provider_base/services/tor.json b/provider_base/services/tor.json
index fc365a19..55d3d2ee 100644
--- a/provider_base/services/tor.json
+++ b/provider_base/services/tor.json
@@ -3,6 +3,13 @@
"bandwidth_rate": 6550,
"contacts": "= [provider.contacts['tor'] || provider.contacts.default].flatten",
"nickname": "= (self.name + secret(:tor_family)).sub('_','')[0..18]",
- "family": "= nodes[:services => 'tor'][:environment => '!local'].field('tor.nickname').join(',')"
+ "family": "= nodes[:services => 'tor'][:environment => '!local'].field('tor.nickname').join(',')",
+ "hidden_service": {
+ "active": null,
+ "key_type": "RSA",
+ "public_key": "= tor_public_key_path(:node_tor_pub_key, tor.hidden_service.key_type) if tor.hidden_service.active",
+ "private_key": "= tor_private_key_path(:node_tor_priv_key, tor.hidden_service.key_type) if tor.hidden_service.active",
+ "address": "= onion_address(:node_tor_pub_key) if tor.hidden_service.active"
+ }
}
}
diff --git a/provider_base/services/webapp.json b/provider_base/services/webapp.json
index 3af0dade..67744f99 100644
--- a/provider_base/services/webapp.json
+++ b/provider_base/services/webapp.json
@@ -1,6 +1,7 @@
{
"webapp": {
"admins": [],
+ "forbidden_usernames": ["admin", "administrator", "arin-admin", "certmaster", "contact", "info", "maildrop", "postmaster", "ssladmin", "www-data"],
"domain": "= domain.full_suffix",
"modules": ["user", "billing", "help"],
"couchdb_webapp_user": {
@@ -21,7 +22,7 @@
"secure": false,
"git": {
"source": "https://leap.se/git/leap_web",
- "revision": "origin/master"
+ "revision": "origin/version/0.6"
},
"client_version": "= provider.client_version",
"nagios_test_user": {
diff --git a/puppet/modules/couchdb b/puppet/modules/couchdb
-Subproject f01b3586215bdc10f0067fa0f6d940be8e88bce
+Subproject 4c0d5673df02fe42e1bbadfee7d4ea1ca1f88e9
diff --git a/puppet/modules/site_apache/templates/vhosts.d/api.conf.erb b/puppet/modules/site_apache/templates/vhosts.d/api.conf.erb
index 3360ac59..e4732289 100644
--- a/puppet/modules/site_apache/templates/vhosts.d/api.conf.erb
+++ b/puppet/modules/site_apache/templates/vhosts.d/api.conf.erb
@@ -2,18 +2,20 @@
ServerName <%= api_domain %>
RewriteEngine On
RewriteRule ^.*$ https://<%= api_domain -%>:<%= api_port -%>%{REQUEST_URI} [R=permanent,L]
+ CustomLog ${APACHE_LOG_DIR}/other_vhosts_access.log common
</VirtualHost>
Listen 0.0.0.0:<%= api_port %>
<VirtualHost *:<%= api_port -%>>
ServerName <%= api_domain %>
+ CustomLog ${APACHE_LOG_DIR}/other_vhosts_access.log common
SSLEngine on
- SSLProtocol all -SSLv2
+ SSLProtocol all -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCompression off
- SSLCipherSuite "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK"
+ SSLCipherSuite "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK"
SSLCACertificatePath /etc/ssl/certs
SSLCertificateChainFile <%= scope.lookupvar('x509::variables::local_CAs') %>/<%= scope.lookupvar('site_config::params::ca_name') %>.crt
diff --git a/puppet/modules/site_apache/templates/vhosts.d/common.conf.erb b/puppet/modules/site_apache/templates/vhosts.d/common.conf.erb
index ed430510..a9733a97 100644
--- a/puppet/modules/site_apache/templates/vhosts.d/common.conf.erb
+++ b/puppet/modules/site_apache/templates/vhosts.d/common.conf.erb
@@ -3,18 +3,20 @@
ServerAlias www.<%= domain %>
RewriteEngine On
RewriteRule ^.*$ https://<%= domain -%>%{REQUEST_URI} [R=permanent,L]
+ CustomLog ${APACHE_LOG_DIR}/other_vhosts_access.log common
</VirtualHost>
<VirtualHost *:443>
ServerName <%= domain_name %>
ServerAlias <%= domain %>
ServerAlias www.<%= domain %>
+ CustomLog ${APACHE_LOG_DIR}/other_vhosts_access.log common
SSLEngine on
- SSLProtocol all -SSLv2
+ SSLProtocol all -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCompression off
- SSLCipherSuite "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK"
+ SSLCipherSuite "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK"
SSLCACertificatePath /etc/ssl/certs
SSLCertificateChainFile <%= scope.lookupvar('x509::variables::local_CAs') %>/<%= scope.lookupvar('site_config::params::commercial_ca_name') %>.crt
diff --git a/puppet/modules/site_apache/templates/vhosts.d/hidden_service.conf.erb b/puppet/modules/site_apache/templates/vhosts.d/hidden_service.conf.erb
new file mode 100644
index 00000000..0c6f3b8e
--- /dev/null
+++ b/puppet/modules/site_apache/templates/vhosts.d/hidden_service.conf.erb
@@ -0,0 +1,33 @@
+<VirtualHost 127.0.0.1:80>
+ ServerName <%= tor_domain %>
+
+ <IfModule mod_headers.c>
+ Header always unset X-Powered-By
+ Header always unset X-Runtime
+ </IfModule>
+
+<% if (defined? @services) and (@services.include? 'webapp') -%>
+ DocumentRoot /srv/leap/webapp/public
+
+ RewriteEngine On
+ # Check for maintenance file and redirect all requests
+ RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
+ RewriteCond %{SCRIPT_FILENAME} !maintenance.html
+ RewriteCond %{REQUEST_URI} !/images/maintenance.jpg
+ RewriteRule ^.*$ %{DOCUMENT_ROOT}/system/maintenance.html [L]
+
+ # http://www.modrails.com/documentation/Users%20guide%20Apache.html#_passengerallowencodedslashes_lt_on_off_gt
+ AllowEncodedSlashes on
+ PassengerAllowEncodedSlashes on
+ PassengerFriendlyErrorPages off
+ SetEnv TMPDIR /var/tmp
+
+ # Allow rails assets to be cached for a very long time (since the URLs change whenever the content changes)
+ <Location /assets/>
+ Header unset ETag
+ FileETag None
+ ExpiresActive On
+ ExpiresDefault "access plus 1 year"
+ </Location>
+<% end -%>
+</VirtualHost>
diff --git a/puppet/modules/site_apt/files/Debian/50unattended-upgrades b/puppet/modules/site_apt/files/Debian/50unattended-upgrades
new file mode 100644
index 00000000..f2f574fc
--- /dev/null
+++ b/puppet/modules/site_apt/files/Debian/50unattended-upgrades
@@ -0,0 +1,16 @@
+// this file is managed by puppet !
+
+Unattended-Upgrade::Allowed-Origins {
+ "${distro_id}:stable";
+ "${distro_id}:${distro_codename}-security";
+ "${distro_id}:${distro_codename}-updates";
+ "${distro_id} Backports:${distro_codename}-backports";
+ "leap.se:stable";
+};
+
+APT::Periodic::Update-Package-Lists "1";
+APT::Periodic::Download-Upgradeable-Packages "1";
+APT::Periodic::Unattended-Upgrade "1";
+
+Unattended-Upgrade::Mail "root";
+Unattended-Upgrade::MailOnlyOnError "true";
diff --git a/puppet/modules/site_apt/manifests/init.pp b/puppet/modules/site_apt/manifests/init.pp
index 9facf4cc..633ccf1e 100644
--- a/puppet/modules/site_apt/manifests/init.pp
+++ b/puppet/modules/site_apt/manifests/init.pp
@@ -1,4 +1,4 @@
-class site_apt {
+class site_apt {
class { 'apt':
custom_key_dir => 'puppet:///modules/site_apt/keys'
@@ -11,7 +11,7 @@ class site_apt {
content => 'Acquire::PDiffs "false";';
}
- include ::apt::unattended_upgrades
+ include ::site_apt::unattended_upgrades
apt::sources_list { 'secondary.list.disabled':
content => template('site_apt/secondary.list');
diff --git a/puppet/modules/site_apt/manifests/leap_repo.pp b/puppet/modules/site_apt/manifests/leap_repo.pp
index 6b3d9919..2d4ba0e1 100644
--- a/puppet/modules/site_apt/manifests/leap_repo.pp
+++ b/puppet/modules/site_apt/manifests/leap_repo.pp
@@ -1,6 +1,9 @@
class site_apt::leap_repo {
+ $platform = hiera_hash('platform')
+ $major_version = $platform['major_version']
+
apt::sources_list { 'leap.list':
- content => 'deb http://deb.leap.se/debian stable main',
+ content => "deb http://deb.leap.se/${major_version} wheezy main\n",
before => Exec[refresh_apt]
}
diff --git a/puppet/modules/site_apt/manifests/preferences/rsyslog.pp b/puppet/modules/site_apt/manifests/preferences/rsyslog.pp
index 132a6e24..bfeaa7da 100644
--- a/puppet/modules/site_apt/manifests/preferences/rsyslog.pp
+++ b/puppet/modules/site_apt/manifests/preferences/rsyslog.pp
@@ -1,9 +1,13 @@
class site_apt::preferences::rsyslog {
- apt::preferences_snippet { 'rsyslog_anon_depends':
- package => 'libestr0 librelp0 rsyslog*',
- priority => '999',
- pin => 'release a=wheezy-backports',
- before => Class['rsyslog::install']
+ apt::preferences_snippet {
+ 'rsyslog_anon_depends':
+ package => 'libestr0 librelp0 rsyslog*',
+ priority => '999',
+ pin => 'release a=wheezy-backports',
+ before => Class['rsyslog::install'];
+
+ 'fixed_rsyslog_anon_package':
+ ensure => absent;
}
}
diff --git a/puppet/modules/site_apt/manifests/unattended_upgrades.pp b/puppet/modules/site_apt/manifests/unattended_upgrades.pp
new file mode 100644
index 00000000..daebffab
--- /dev/null
+++ b/puppet/modules/site_apt/manifests/unattended_upgrades.pp
@@ -0,0 +1,10 @@
+class site_apt::unattended_upgrades inherits apt::unattended_upgrades {
+ # override unattended-upgrades package resource to make sure
+ # that it is upgraded on every deploy (#6245)
+
+ include ::apt::unattended_upgrades
+
+ Package['unattended-upgrades'] {
+ ensure => latest
+ }
+}
diff --git a/puppet/modules/site_check_mk/files/host_contactgroups.mk b/puppet/modules/site_check_mk/files/host_contactgroups.mk
new file mode 100644
index 00000000..e89323fb
--- /dev/null
+++ b/puppet/modules/site_check_mk/files/host_contactgroups.mk
@@ -0,0 +1,3 @@
+host_contactgroups = [
+ ( "admins", ALL_HOSTS ),
+]
diff --git a/puppet/modules/site_check_mk/manifests/server.pp b/puppet/modules/site_check_mk/manifests/server.pp
index e544ef0d..388ae94b 100644
--- a/puppet/modules/site_check_mk/manifests/server.pp
+++ b/puppet/modules/site_check_mk/manifests/server.pp
@@ -5,11 +5,12 @@ class site_check_mk::server {
$type = $ssh_hash['authorized_keys']['monitor']['type']
$seckey = $ssh_hash['monitor']['private_key']
- $nagios_hiera = hiera_hash('nagios')
- $nagios_hosts = $nagios_hiera['hosts']
+ $nagios_hiera = hiera_hash('nagios')
+ $nagios_hosts = $nagios_hiera['hosts']
- $hosts = hiera_hash('hosts')
- $all_hosts = inline_template ('<% @hosts.keys.sort.each do |key| -%>"<%= @hosts[key]["domain_internal"] %>", <% end -%>')
+ $hosts = hiera_hash('hosts')
+ $all_hosts = inline_template ('<% @hosts.keys.sort.each do |key| -%>"<%= @hosts[key]["domain_internal"] %>", <% end -%>')
+ $domains_internal = $nagios_hiera['domains_internal']
package { 'check-mk-server':
ensure => installed,
@@ -35,6 +36,14 @@ class site_check_mk::server {
content => template('site_check_mk/use_ssh.mk'),
notify => Exec['check_mk-refresh'],
require => Package['check-mk-server'];
+ '/etc/check_mk/conf.d/hostgroups.mk':
+ content => template('site_check_mk/hostgroups.mk'),
+ notify => Exec['check_mk-refresh'],
+ require => Package['check-mk-server'];
+ '/etc/check_mk/conf.d/host_contactgroups.mk':
+ source => 'puppet:///modules/site_check_mk/host_contactgroups.mk',
+ notify => Exec['check_mk-refresh'],
+ require => Package['check-mk-server'];
'/etc/check_mk/all_hosts_static':
content => $all_hosts,
notify => Exec['check_mk-refresh'],
@@ -59,6 +68,5 @@ class site_check_mk::server {
require => Package['nagios-plugins-basic'];
}
-
include check_mk::agent::local_checks
}
diff --git a/puppet/modules/site_check_mk/templates/hostgroups.mk b/puppet/modules/site_check_mk/templates/hostgroups.mk
new file mode 100644
index 00000000..79b7f92f
--- /dev/null
+++ b/puppet/modules/site_check_mk/templates/hostgroups.mk
@@ -0,0 +1,4 @@
+host_groups = [
+ <% @domains_internal.each do |domain| %>( '<%= domain %>', [<% @nagios_hosts.keys.sort.each do |key| -%><% if @nagios_hosts[key]['domain_internal'] == key+'.'+domain -%>'<%= key %>.<%= domain %>', <% end -%><% end -%>] ),
+ <% end -%>
+]
diff --git a/puppet/modules/site_config/manifests/default.pp b/puppet/modules/site_config/manifests/default.pp
index 42359a00..a20ffc3b 100644
--- a/puppet/modules/site_config/manifests/default.pp
+++ b/puppet/modules/site_config/manifests/default.pp
@@ -56,10 +56,10 @@ class site_config::default {
include site_postfix::satellite
}
- # if class site_custom exists, include it.
+ # if class custom exists, include it.
# possibility for users to define custom puppet recipes
- if defined( '::site_custom') {
- include ::site_custom
+ if defined( '::custom') {
+ include ::custom
}
include site_check_mk::agent
diff --git a/puppet/modules/site_couchdb/files/runit_config b/puppet/modules/site_couchdb/files/runit_config
new file mode 100644
index 00000000..169b4832
--- /dev/null
+++ b/puppet/modules/site_couchdb/files/runit_config
@@ -0,0 +1,6 @@
+#!/bin/bash
+exec 2>&1
+export HOME=/home/bigcouch
+ulimit -H -n 32768
+ulimit -S -n 32768
+exec chpst -u bigcouch /opt/bigcouch/bin/bigcouch
diff --git a/puppet/modules/site_couchdb/manifests/bigcouch.pp b/puppet/modules/site_couchdb/manifests/bigcouch.pp
index d3352000..82c85b52 100644
--- a/puppet/modules/site_couchdb/manifests/bigcouch.pp
+++ b/puppet/modules/site_couchdb/manifests/bigcouch.pp
@@ -1,12 +1,12 @@
class site_couchdb::bigcouch {
- $config = $couchdb_config['bigcouch']
+ $config = $::site_couchdb::couchdb_config['bigcouch']
$cookie = $config['cookie']
$ednp_port = $config['ednp_port']
class { 'couchdb':
- admin_pw => $couchdb_admin_pw,
- admin_salt => $couchdb_admin_salt,
+ admin_pw => $::site_couchdb::couchdb_admin_pw,
+ admin_salt => $::site_couchdb::couchdb_admin_salt,
bigcouch => true,
bigcouch_cookie => $cookie,
ednp_port => $ednp_port,
@@ -20,7 +20,7 @@ class site_couchdb::bigcouch {
-> Class['site_config::resolvconf']
-> Class['couchdb::bigcouch::package::cloudant']
-> Service['shorewall']
- -> Service['stunnel']
+ -> Exec['refresh_stunnel']
-> Class['site_couchdb::setup']
-> Class['site_couchdb::bigcouch::add_nodes']
-> Class['site_couchdb::bigcouch::settle_cluster']
@@ -32,4 +32,14 @@ class site_couchdb::bigcouch {
file { '/var/log/bigcouch':
ensure => directory
}
+
+ file { '/etc/sv/bigcouch/run':
+ ensure => present,
+ source => 'puppet:///modules/site_couchdb/runit_config',
+ owner => root,
+ group => root,
+ mode => '0755',
+ require => Package['couchdb'],
+ notify => Service['couchdb']
+ }
}
diff --git a/puppet/modules/site_couchdb/manifests/init.pp b/puppet/modules/site_couchdb/manifests/init.pp
index 5a4fb936..a11f6309 100644
--- a/puppet/modules/site_couchdb/manifests/init.pp
+++ b/puppet/modules/site_couchdb/manifests/init.pp
@@ -42,13 +42,13 @@ class site_couchdb {
$couchdb_backup = $couchdb_config['backup']
$couchdb_mode = $couchdb_config['mode']
- if $couchdb_mode == "multimaster" { include site_couchdb::bigcouch }
- if $couchdb_mode == "master" { include site_couchdb::master }
- if $couchdb_mode == "mirror" { include site_couchdb::mirror }
+ if $couchdb_mode == 'multimaster' { include site_couchdb::bigcouch }
+ if $couchdb_mode == 'master' { include site_couchdb::master }
+ if $couchdb_mode == 'mirror' { include site_couchdb::mirror }
Class['site_config::default']
-> Service['shorewall']
- -> Service['stunnel']
+ -> Exec['refresh_stunnel']
-> Class['couchdb']
-> Class['site_couchdb::setup']
diff --git a/puppet/modules/site_nagios/manifests/server.pp b/puppet/modules/site_nagios/manifests/server.pp
index 85443917..b195c880 100644
--- a/puppet/modules/site_nagios/manifests/server.pp
+++ b/puppet/modules/site_nagios/manifests/server.pp
@@ -3,12 +3,19 @@ class site_nagios::server inherits nagios::base {
# First, purge old nagios config (see #1467)
class { 'site_nagios::server::purge': }
- $nagios_hiera = hiera('nagios')
- $nagiosadmin_pw = htpasswd_sha1($nagios_hiera['nagiosadmin_pw'])
- $nagios_hosts = $nagios_hiera['hosts']
+ $nagios_hiera = hiera('nagios')
+ $nagiosadmin_pw = htpasswd_sha1($nagios_hiera['nagiosadmin_pw'])
+ $nagios_hosts = $nagios_hiera['hosts']
+ $domains_internal = $nagios_hiera['domains_internal']
- include nagios::defaults
include nagios::base
+ include nagios::defaults::commands
+ include nagios::defaults::contactgroups
+ include nagios::defaults::contacts
+ include nagios::defaults::templates
+ include nagios::defaults::timeperiods
+ include nagios::defaults::plugins
+
class {'nagios':
# don't manage apache class from nagios, cause we already include
# it in site_apache::common
@@ -55,4 +62,6 @@ class site_nagios::server inherits nagios::base {
'set missingok missingok', 'set ifempty notifempty',
'set copytruncate copytruncate' ]
}
+
+ ::site_nagios::server::hostgroup { $domains_internal: }
}
diff --git a/puppet/modules/site_nagios/manifests/server/hostgroup.pp b/puppet/modules/site_nagios/manifests/server/hostgroup.pp
new file mode 100644
index 00000000..035ba7d1
--- /dev/null
+++ b/puppet/modules/site_nagios/manifests/server/hostgroup.pp
@@ -0,0 +1,3 @@
+define site_nagios::server::hostgroup {
+ nagios_hostgroup { $name: }
+}
diff --git a/puppet/modules/site_nickserver/templates/nickserver-proxy.conf.erb b/puppet/modules/site_nickserver/templates/nickserver-proxy.conf.erb
index ae06410e..56a8d9f6 100644
--- a/puppet/modules/site_nickserver/templates/nickserver-proxy.conf.erb
+++ b/puppet/modules/site_nickserver/templates/nickserver-proxy.conf.erb
@@ -9,9 +9,10 @@ Listen 0.0.0.0:<%= @nickserver_port -%>
ServerAlias <%= @address_domain %>
SSLEngine on
- SSLProtocol -all +SSLv3 +TLSv1
- SSLCipherSuite HIGH:MEDIUM:!aNULL:!SSLv2:!MD5:@STRENGTH
+ SSLProtocol all -SSLv2 -SSLv3
SSLHonorCipherOrder on
+ SSLCompression off
+ SSLCipherSuite "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK"
SSLCACertificatePath /etc/ssl/certs
SSLCertificateChainFile <%= scope.lookupvar('x509::variables::local_CAs') %>/<%= scope.lookupvar('site_config::params::ca_name') %>.crt
diff --git a/puppet/modules/site_obfsproxy/manifests/init.pp b/puppet/modules/site_obfsproxy/manifests/init.pp
index 40b7fba8..6275ebee 100644
--- a/puppet/modules/site_obfsproxy/manifests/init.pp
+++ b/puppet/modules/site_obfsproxy/manifests/init.pp
@@ -11,13 +11,13 @@ class site_obfsproxy {
$dest_ip = $obfsproxy['gateway_address']
$dest_port = '443'
- if $::services =~ /\bopenvpn\b/ {
- $openvpn = hiera('openvpn')
- $bind_address = $openvpn['gateway_address']
- }
- elsif $::services =~ /\bobfsproxy\b/ {
- $bind_address = hiera('ip_address')
- }
+ if member($::services, 'openvpn') {
+ $openvpn = hiera('openvpn')
+ $bind_address = $openvpn['gateway_address']
+ }
+ elsif member($::services, 'obfsproxy') {
+ $bind_address = hiera('ip_address')
+ }
include site_apt::preferences::twisted
include site_apt::preferences::obfsproxy
diff --git a/puppet/modules/site_openvpn/manifests/init.pp b/puppet/modules/site_openvpn/manifests/init.pp
index b6331f12..d6f9150b 100644
--- a/puppet/modules/site_openvpn/manifests/init.pp
+++ b/puppet/modules/site_openvpn/manifests/init.pp
@@ -148,13 +148,17 @@ class site_openvpn {
exec { 'restart_openvpn':
command => '/etc/init.d/openvpn restart',
refreshonly => true,
- subscribe => File['/etc/openvpn'],
+ subscribe => [
+ File['/etc/openvpn'],
+ Class['Site_config::X509::Key'],
+ Class['Site_config::X509::Cert'],
+ Class['Site_config::X509::Ca_bundle'] ],
require => [
- Package['openvpn'],
- File['/etc/openvpn'],
- Class['Site_config::X509::Key'],
- Class['Site_config::X509::Cert'],
- Class['Site_config::X509::Ca_bundle'] ];
+ Package['openvpn'],
+ File['/etc/openvpn'],
+ Class['Site_config::X509::Key'],
+ Class['Site_config::X509::Cert'],
+ Class['Site_config::X509::Ca_bundle'] ];
}
cron { 'add_gateway_ips.sh':
diff --git a/puppet/modules/site_openvpn/manifests/server_config.pp b/puppet/modules/site_openvpn/manifests/server_config.pp
index 97cf2842..466f6d00 100644
--- a/puppet/modules/site_openvpn/manifests/server_config.pp
+++ b/puppet/modules/site_openvpn/manifests/server_config.pp
@@ -85,6 +85,18 @@ define site_openvpn::server_config(
key => 'tcp-nodelay',
server => $openvpn_configname;
}
+ } elsif $proto == 'udp' {
+ if $config['fragment'] != 1500 {
+ openvpn::option {
+ "fragment ${openvpn_configname}":
+ key => 'fragment',
+ value => $config['fragment'],
+ server => $openvpn_configname;
+ "mssfix ${openvpn_configname}":
+ key => 'mssfix',
+ server => $openvpn_configname;
+ }
+ }
}
openvpn::option {
diff --git a/puppet/modules/site_shorewall/manifests/dnat_rule.pp b/puppet/modules/site_shorewall/manifests/dnat_rule.pp
index aa298408..f9fbe950 100644
--- a/puppet/modules/site_shorewall/manifests/dnat_rule.pp
+++ b/puppet/modules/site_shorewall/manifests/dnat_rule.pp
@@ -4,41 +4,45 @@ define site_shorewall::dnat_rule {
if $port != 1194 {
if $site_openvpn::openvpn_allow_unlimited {
shorewall::rule {
- "dnat_tcp_port_$port":
+ "dnat_tcp_port_${port}":
action => 'DNAT',
source => 'net',
destination => "\$FW:${site_openvpn::unlimited_gateway_address}:1194",
proto => 'tcp',
destinationport => $port,
+ originaldest => $site_openvpn::unlimited_gateway_address,
order => 100;
}
shorewall::rule {
- "dnat_udp_port_$port":
+ "dnat_udp_port_${port}":
action => 'DNAT',
source => 'net',
destination => "\$FW:${site_openvpn::unlimited_gateway_address}:1194",
proto => 'udp',
destinationport => $port,
+ originaldest => $site_openvpn::unlimited_gateway_address,
order => 100;
}
}
if $site_openvpn::openvpn_allow_limited {
shorewall::rule {
- "dnat_free_tcp_port_$port":
+ "dnat_free_tcp_port_${port}":
action => 'DNAT',
source => 'net',
destination => "\$FW:${site_openvpn::limited_gateway_address}:1194",
proto => 'tcp',
destinationport => $port,
+ originaldest => $site_openvpn::unlimited_gateway_address,
order => 100;
}
shorewall::rule {
- "dnat_free_udp_port_$port":
+ "dnat_free_udp_port_${port}":
action => 'DNAT',
source => 'net',
destination => "\$FW:${site_openvpn::limited_gateway_address}:1194",
proto => 'udp',
destinationport => $port,
+ originaldest => $site_openvpn::unlimited_gateway_address,
order => 100;
}
}
diff --git a/puppet/modules/site_sshd/manifests/init.pp b/puppet/modules/site_sshd/manifests/init.pp
index 9a05b6ed..1da2f1d5 100644
--- a/puppet/modules/site_sshd/manifests/init.pp
+++ b/puppet/modules/site_sshd/manifests/init.pp
@@ -53,7 +53,7 @@ class site_sshd {
##
class { '::sshd':
manage_nagios => false,
- ports => $ssh['port'],
+ ports => [ $ssh['port'] ],
use_pam => 'yes',
hardened_ssl => 'yes',
print_motd => 'no',
diff --git a/puppet/modules/site_static/templates/apache.conf.erb b/puppet/modules/site_static/templates/apache.conf.erb
index 07ac481d..9b516a10 100644
--- a/puppet/modules/site_static/templates/apache.conf.erb
+++ b/puppet/modules/site_static/templates/apache.conf.erb
@@ -46,10 +46,10 @@
#RewriteLogLevel 3
SSLEngine on
- SSLProtocol all -SSLv2
+ SSLProtocol all -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCompression off
- SSLCipherSuite "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK"
+ SSLCipherSuite "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK"
<%- if @tls_only -%>
Header add Strict-Transport-Security: "max-age=15768000;includeSubdomains"
diff --git a/puppet/modules/site_stunnel/manifests/client.pp b/puppet/modules/site_stunnel/manifests/client.pp
index 12d664b4..3b10ecb8 100644
--- a/puppet/modules/site_stunnel/manifests/client.pp
+++ b/puppet/modules/site_stunnel/manifests/client.pp
@@ -35,10 +35,7 @@ define site_stunnel::client (
pid => "/var/run/stunnel4/${pid}.pid",
rndfile => $rndfile,
debuglevel => $debuglevel,
- subscribe => [
- Class['Site_config::X509::Key'],
- Class['Site_config::X509::Cert'],
- Class['Site_config::X509::Ca'] ];
+ sslversion => 'TLSv1';
}
site_shorewall::stunnel::client { $name:
diff --git a/puppet/modules/site_stunnel/manifests/init.pp b/puppet/modules/site_stunnel/manifests/init.pp
index b292f1cd..2e0cf5b8 100644
--- a/puppet/modules/site_stunnel/manifests/init.pp
+++ b/puppet/modules/site_stunnel/manifests/init.pp
@@ -28,5 +28,7 @@ class site_stunnel {
$clients = $stunnel['clients']
$client_sections = keys($clients)
site_stunnel::clients { $client_sections: }
+
+ include site_stunnel::override_service
}
diff --git a/puppet/modules/site_stunnel/manifests/override_service.pp b/puppet/modules/site_stunnel/manifests/override_service.pp
new file mode 100644
index 00000000..96187048
--- /dev/null
+++ b/puppet/modules/site_stunnel/manifests/override_service.pp
@@ -0,0 +1,13 @@
+class site_stunnel::override_service inherits stunnel::debian {
+
+ include site_config::x509::cert
+ include site_config::x509::key
+ include site_config::x509::ca
+
+ Service[stunnel] {
+ subscribe => [
+ Class['Site_config::X509::Key'],
+ Class['Site_config::X509::Cert'],
+ Class['Site_config::X509::Ca'] ]
+ }
+}
diff --git a/puppet/modules/site_stunnel/manifests/servers.pp b/puppet/modules/site_stunnel/manifests/servers.pp
index b1da5c59..b6fac319 100644
--- a/puppet/modules/site_stunnel/manifests/servers.pp
+++ b/puppet/modules/site_stunnel/manifests/servers.pp
@@ -35,10 +35,7 @@ define site_stunnel::servers (
pid => "/var/run/stunnel4/${pid}.pid",
rndfile => '/var/lib/stunnel4/.rnd',
debuglevel => $debuglevel,
- require => [
- Class['Site_config::X509::Key'],
- Class['Site_config::X509::Cert'],
- Class['Site_config::X509::Ca'] ];
+ sslversion => 'TLSv1';
}
# allow incoming connections on $accept_port
diff --git a/puppet/modules/site_tor/manifests/init.pp b/puppet/modules/site_tor/manifests/init.pp
index e62cb12d..d14e813d 100644
--- a/puppet/modules/site_tor/manifests/init.pp
+++ b/puppet/modules/site_tor/manifests/init.pp
@@ -11,23 +11,31 @@ class site_tor {
$address = hiera('ip_address')
+ $openvpn = hiera('openvpn', undef)
+ if $openvpn {
+ $openvpn_ports = $openvpn['ports']
+ }
+ else {
+ $openvpn_ports = []
+ }
+
class { 'tor::daemon': }
tor::daemon::relay { $nickname:
- port => 9001,
- address => $address,
- contact_info => obfuscate_email($contact_emails),
- bandwidth_rate => $bandwidth_rate,
- my_family => $family
+ port => 9001,
+ address => $address,
+ contact_info => obfuscate_email($contact_emails),
+ bandwidth_rate => $bandwidth_rate,
+ my_family => $family
}
if ( $tor_type == 'exit'){
- tor::daemon::directory { $::hostname: port => 80 }
+ # Only enable the daemon directory if the node isn't also a webapp node
+ # or running openvpn on port 80
+ if ! member($::services, 'webapp') and ! member($openvpn_ports, '80') {
+ tor::daemon::directory { $::hostname: port => 80 }
+ }
}
else {
- tor::daemon::directory { $::hostname:
- port => 80,
- port_front_page => '';
- }
include site_tor::disable_exit
}
diff --git a/puppet/modules/site_webapp/manifests/hidden_service.pp b/puppet/modules/site_webapp/manifests/hidden_service.pp
new file mode 100644
index 00000000..ac0e8a37
--- /dev/null
+++ b/puppet/modules/site_webapp/manifests/hidden_service.pp
@@ -0,0 +1,43 @@
+class site_webapp::hidden_service {
+ $tor = hiera('tor')
+ $hidden_service = $tor['hidden_service']
+ $tor_domain = "${hidden_service['address']}.onion"
+
+ include site_apache::common
+ include site_apache::module::headers
+ include site_apache::module::alias
+ include site_apache::module::expires
+ include site_apache::module::removeip
+
+ include tor::daemon
+ tor::daemon::hidden_service { 'webapp': ports => '80 127.0.0.1:80' }
+
+ file {
+ '/var/lib/tor/webapp/':
+ ensure => directory,
+ owner => 'debian-tor',
+ group => 'debian-tor',
+ mode => '2700';
+
+ '/var/lib/tor/webapp/private_key':
+ ensure => present,
+ source => '/srv/leap/files/nodes/web/tor.key',
+ owner => 'debian-tor',
+ group => 'debian-tor',
+ mode => '0600';
+
+ '/var/lib/tor/webapp/hostname':
+ ensure => present,
+ content => $tor_domain,
+ owner => 'debian-tor',
+ group => 'debian-tor',
+ mode => '0600';
+ }
+
+ apache::vhost::file {
+ 'hidden_service':
+ content => template('site_apache/vhosts.d/hidden_service.conf.erb')
+ }
+
+ include site_shorewall::tor
+} \ No newline at end of file
diff --git a/puppet/modules/site_webapp/manifests/init.pp b/puppet/modules/site_webapp/manifests/init.pp
index 17b010f3..752993c1 100644
--- a/puppet/modules/site_webapp/manifests/init.pp
+++ b/puppet/modules/site_webapp/manifests/init.pp
@@ -10,6 +10,7 @@ class site_webapp {
$webapp = hiera('webapp')
$api_version = $webapp['api_version']
$secret_token = $webapp['secret_token']
+ $tor = hiera('tor', false)
Class['site_config::default'] -> Class['site_webapp']
@@ -157,6 +158,13 @@ class site_webapp {
notify => Service['apache'];
}
+ if $tor {
+ $hidden_service = $tor['hidden_service']
+ if $hidden_service['active'] {
+ include site_webapp::hidden_service
+ }
+ }
+
include site_shorewall::webapp
include site_check_mk::agent::webapp
}
diff --git a/puppet/modules/site_webapp/templates/config.yml.erb b/puppet/modules/site_webapp/templates/config.yml.erb
index 9205438b..0c75f3ca 100644
--- a/puppet/modules/site_webapp/templates/config.yml.erb
+++ b/puppet/modules/site_webapp/templates/config.yml.erb
@@ -19,6 +19,7 @@ production:
default_service_level: "<%= @webapp['default_service_level'] %>"
service_levels: <%= @webapp['service_levels'].to_json %>
allow_registration: <%= @webapp['allow_registration'].inspect %>
+ handle_blacklist: <%= @webapp['forbidden_usernames'].inspect %>
<%- if @webapp['engines'] && @webapp['engines'].any? -%>
engines:
<%- @webapp['engines'].each do |engine| -%>
diff --git a/puppet/modules/sshd b/puppet/modules/sshd
-Subproject 5c23b33200fc6229ada7f4e13672b5da0d4bdd8
+Subproject 750a497758d94c2f5a6cad23cecc3dbde2d2f92
diff --git a/tests/README.md b/tests/README.md
index debbf700..814c25b1 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -1,12 +1,25 @@
-This directory contains to kinds of tests:
+Tests
+---------------------------------
-White Box Tests
-================================
+tests/white-box/
-These tests are run on the server as superuser. They are for troubleshooting any problems with the internal setup of the server.
+ These tests are run on the server as superuser. They are for
+ troubleshooting any problems with the internal setup of the server.
-Black Box Tests
-================================
+tests/black-box/
+
+ These test are run the user's local machine. They are for troubleshooting
+ any external problems with the service exposed by the server.
+
+Additional Files
+---------------------------------
+
+tests/helpers/
+
+ Utility functions made available to all tests.
+
+tests/order.rb
+
+ Configuration file to specify which nodes should be tested in which order.
-These test are run the user's local machine. They are for troubleshooting any external problems with the service exposed by the server.
diff --git a/tests/helpers/couchdb_helper.rb b/tests/helpers/couchdb_helper.rb
new file mode 100644
index 00000000..d4d3c0e0
--- /dev/null
+++ b/tests/helpers/couchdb_helper.rb
@@ -0,0 +1,103 @@
+class LeapTest
+
+ #
+ # generates a couchdb url for when couchdb is running
+ # remotely and is available via stunnel.
+ #
+ # example properties:
+ #
+ # stunnel:
+ # clients:
+ # couch_client:
+ # couch1_5984:
+ # accept_port: 4000
+ # connect: couch1.bitmask.i
+ # connect_port: 15984
+ #
+ def couchdb_urls_via_stunnel(path="", options=nil)
+ if options && options[:username] && options[:password]
+ userpart = "%{username}:%{password}@" % options
+ else
+ userpart = ""
+ end
+ assert_property('stunnel.clients.couch_client').values.collect do |stunnel_conf|
+ assert port = stunnel_conf['accept_port'], 'Field `accept_port` must be present in `stunnel` property.'
+ URLString.new("http://#{userpart}localhost:#{port}#{path}").tap {|url|
+ remote_ip_address = TCPSocket.gethostbyname(stunnel_conf['connect']).last
+ url.memo = "(via stunnel to %s:%s, aka %s)" % [stunnel_conf['connect'], stunnel_conf['connect_port'], remote_ip_address]
+ }
+ end
+ end
+
+ #
+ # generates a couchdb url for accessing couchdb via haproxy
+ #
+ # example properties:
+ #
+ # haproxy:
+ # couch:
+ # listen_port: 4096
+ # servers:
+ # panda:
+ # backup: false
+ # host: localhost
+ # port: 4000
+ # weight: 100
+ # writable: true
+ #
+ def couchdb_url_via_haproxy(path="", options=nil)
+ if options && options[:username] && options[:password]
+ userpart = "%{username}:%{password}@" % options
+ else
+ userpart = ""
+ end
+ port = assert_property('haproxy.couch.listen_port')
+ return URLString.new("http://#{userpart}localhost:#{port}#{path}").tap { |url|
+ url.memo = '(via haproxy)'
+ }
+ end
+
+ #
+ # generates a couchdb url for when couchdb is running locally.
+ #
+ # example properties:
+ #
+ # couch:
+ # port: 5984
+ #
+ def couchdb_url_via_localhost(path="", options=nil)
+ port = (options && options[:port]) || assert_property('couch.port')
+ if options && options[:username]
+ password = property("couch.users.%{username}.password" % options)
+ userpart = "%s:%s@" % [options[:username], password]
+ else
+ userpart = ""
+ end
+ return URLString.new("http://#{userpart}localhost:#{port}#{path}").tap { |url|
+ url.memo = '(via direct localhost connection)'
+ }
+ end
+
+ #
+ # returns a single url for accessing couchdb
+ #
+ def couchdb_url(path="", options=nil)
+ if property('couch.port')
+ couchdb_url_via_localhost(path, options)
+ elsif property('stunnel.clients.couch_client')
+ couchdb_urls_via_stunnel(path, options).first
+ end
+ end
+
+ #
+ # returns an array of urls for accessing couchdb
+ #
+ def couchdb_urls(path="", options=nil)
+ if property('couch.port')
+ [couchdb_url_via_localhost(path, options)]
+ elsif property('stunnel.clients.couch_client')
+ couchdb_urls_via_stunnel(path, options)
+ end
+ end
+
+end \ No newline at end of file
diff --git a/tests/helpers/files_helper.rb b/tests/helpers/files_helper.rb
new file mode 100644
index 00000000..d6795889
--- /dev/null
+++ b/tests/helpers/files_helper.rb
@@ -0,0 +1,54 @@
+class LeapTest
+
+ #
+ # Matches the regexp in the file, and returns the first matched string (or fails if no match).
+ #
+ def file_match(filename, regexp)
+ if match = File.read(filename).match(regexp)
+ match.captures.first
+ else
+ fail "Regexp #{regexp.inspect} not found in file #{filename.inspect}."
+ end
+ end
+
+ #
+ # Matches the regexp in the file, and returns array of matched strings (or fails if no match).
+ #
+ def file_matches(filename, regexp)
+ if match = File.read(filename).match(regexp)
+ match.captures
+ else
+ fail "Regexp #{regexp.inspect} not found in file #{filename.inspect}."
+ end
+ end
+
+ #
+ # checks to make sure the given property path exists in $node (e.g. hiera.yaml)
+ # and returns the value
+ #
+ def assert_property(property)
+ latest = $node
+ property.split('.').each do |segment|
+ latest = latest[segment]
+ fail "Required node property `#{property}` is missing." if latest.nil?
+ end
+ return latest
+ end
+
+ #
+ # a handy function to get the value of a long property path
+ # without needing to test the existance individually of each part
+ # in the tree.
+ #
+ # e.g. property("stunnel.clients.couch_client")
+ #
+ def property(property)
+ latest = $node
+ property.split('.').each do |segment|
+ latest = latest[segment]
+ return nil if latest.nil?
+ end
+ return latest
+ end
+
+end \ No newline at end of file
diff --git a/tests/helpers/http_helper.rb b/tests/helpers/http_helper.rb
new file mode 100644
index 00000000..c941ef63
--- /dev/null
+++ b/tests/helpers/http_helper.rb
@@ -0,0 +1,145 @@
+require 'net/http'
+
+class LeapTest
+
+ #
+ # In order to easily provide detailed error messages, it is useful
+ # to append a memo to a url string that details what this url is for
+ # (e.g. stunnel, haproxy, etc).
+ #
+ # So, the url happens to be a UrlString, the memo field is used
+ # if there is an error in assert_get.
+ #
+ class URLString < String
+ attr_accessor :memo
+ end
+
+ #
+ # aliases for http_send()
+ #
+ def get(url, params=nil, options=nil, &block)
+ http_send("GET", url, params, options, &block)
+ end
+ def delete(url, params=nil, options=nil, &block)
+ http_send("DELETE", url, params, options, &block)
+ end
+ def post(url, params=nil, options=nil, &block)
+ http_send("POST", url, params, options, &block)
+ end
+ def put(url, params=nil, options=nil, &block)
+ http_send("PUT", url, params, options, &block)
+ end
+
+ #
+ # send a GET, DELETE, POST, or PUT
+ # yields |body, response, error|
+ #
+ def http_send(method, url, params=nil, options=nil)
+ options ||= {}
+ response = nil
+
+ # build uri
+ uri = URI(url)
+ if params && (method == 'GET' || method == 'DELETE')
+ uri.query = URI.encode_www_form(params)
+ end
+
+ # build http
+ http = Net::HTTP.new uri.host, uri.port
+ if uri.scheme == 'https'
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ http.use_ssl = true
+ end
+
+ # build request
+ request = build_request(method, uri, params, options)
+
+ # make http request
+ http.start do |agent|
+ response = agent.request(request)
+ yield response.body, response, nil
+ end
+ rescue => exc
+ yield nil, response, exc
+ end
+
+ #
+ # Aliases for assert_http_send()
+ #
+ def assert_get(url, params=nil, options=nil, &block)
+ assert_http_send("GET", url, params, options, &block)
+ end
+ def assert_delete(url, params=nil, options=nil, &block)
+ assert_http_send("DELETE", url, params, options, &block)
+ end
+ def assert_post(url, params=nil, options=nil, &block)
+ assert_http_send("POST", url, params, options, &block)
+ end
+ def assert_put(url, params=nil, options=nil, &block)
+ assert_http_send("PUT", url, params, options, &block)
+ end
+
+ #
+ # calls http_send, yielding results if successful or failing with
+ # descriptive infor otherwise.
+ #
+ def assert_http_send(method, url, params=nil, options=nil, &block)
+ options ||= {}
+ error_msg = options[:error_msg] || (url.respond_to?(:memo) ? url.memo : nil)
+ http_send(method, url, params, options) do |body, response, error|
+ if body && response && response.code.to_i >= 200 && response.code.to_i < 300
+ if block
+ yield(body) if block.arity == 1
+ yield(response, body) if block.arity == 2
+ end
+ elsif response
+ fail ["Expected a 200 status code from #{url}, but got #{response.code} instead.", error_msg, body].compact.join("\n")
+ else
+ fail ["Expected a response from #{url}, but got \"#{error}\" instead.", error_msg, body].compact.join("\n"), error
+ end
+ end
+ end
+
+ #
+ # only a warning for now, should be a failure in the future
+ #
+ def assert_auth_fail(url, params)
+ uri = URI(url)
+ get(url, params) do |body, response, error|
+ unless response.code.to_s == "401"
+ warn "Expected a '401 Unauthorized' response, but got #{response.code} instead (GET #{uri.request_uri} with username '#{uri.user}')."
+ return false
+ end
+ end
+ true
+ end
+
+ private
+
+ def build_request(method, uri, params, options)
+ request = case method
+ when "GET" then Net::HTTP::Get.new(uri.request_uri)
+ when "DELETE" then Net::HTTP::Delete.new(uri.request_uri)
+ when "POST" then Net::HTTP::Post.new(uri.request_uri)
+ when "PUT" then Net::HTTP::Put.new(uri.request_uri)
+ end
+ if uri.user
+ request.basic_auth uri.user, uri.password
+ end
+ if params && (method == 'POST' || method == 'PUT')
+ if options[:format] == :json || options[:format] == 'json'
+ request["Content-Type"] = "application/json"
+ request.body = params.to_json
+ else
+ request.set_form_data(params) if params
+ end
+ end
+ if options[:headers]
+ options[:headers].each do |key, value|
+ request[key] = value
+ end
+ end
+ request
+ end
+
+end \ No newline at end of file
diff --git a/tests/helpers/network_helper.rb b/tests/helpers/network_helper.rb
new file mode 100644
index 00000000..ff92d382
--- /dev/null
+++ b/tests/helpers/network_helper.rb
@@ -0,0 +1,79 @@
+class LeapTest
+
+ #
+ # tcp connection helper with timeout
+ #
+ def try_tcp_connect(host, port, timeout = 5)
+ addr = Socket.getaddrinfo(host, nil)
+ sockaddr = Socket.pack_sockaddr_in(port, addr[0][3])
+
+ Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0).tap do |socket|
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
+ begin
+ socket.connect_nonblock(sockaddr)
+ rescue IO::WaitReadable
+ if IO.select([socket], nil, nil, timeout) == nil
+ raise "Connection timeout"
+ else
+ socket.connect_nonblock(sockaddr)
+ end
+ rescue IO::WaitWritable
+ if IO.select(nil, [socket], nil, timeout) == nil
+ raise "Connection timeout"
+ else
+ socket.connect_nonblock(sockaddr)
+ end
+ end
+ return socket
+ end
+ end
+
+ def try_tcp_write(socket, timeout = 5)
+ begin
+ socket.write_nonblock("\0")
+ rescue IO::WaitReadable
+ if IO.select([socket], nil, nil, timeout) == nil
+ raise "Write timeout"
+ else
+ retry
+ end
+ rescue IO::WaitWritable
+ if IO.select(nil, [socket], nil, timeout) == nil
+ raise "Write timeout"
+ else
+ retry
+ end
+ end
+ end
+
+ def try_tcp_read(socket, timeout = 5)
+ begin
+ socket.read_nonblock(1)
+ rescue IO::WaitReadable
+ if IO.select([socket], nil, nil, timeout) == nil
+ raise "Read timeout"
+ else
+ retry
+ end
+ rescue IO::WaitWritable
+ if IO.select(nil, [socket], nil, timeout) == nil
+ raise "Read timeout"
+ else
+ retry
+ end
+ end
+ end
+
+ def assert_tcp_socket(host, port, msg=nil)
+ begin
+ socket = try_tcp_connect(host, port, 1)
+ #try_tcp_write(socket,1)
+ #try_tcp_read(socket,1)
+ rescue StandardError => exc
+ fail ["Failed to open socket #{host}:#{port}", exc].join("\n")
+ ensure
+ socket.close if socket
+ end
+ end
+
+end \ No newline at end of file
diff --git a/tests/helpers/os_helper.rb b/tests/helpers/os_helper.rb
new file mode 100644
index 00000000..529e899f
--- /dev/null
+++ b/tests/helpers/os_helper.rb
@@ -0,0 +1,34 @@
+class LeapTest
+
+ #
+ # works like pgrep command line
+ # return an array of hashes like so [{:pid => "1234", :process => "ls"}]
+ #
+ def pgrep(match)
+ output = `pgrep --full --list-name '#{match}'`
+ output.each_line.map{|line|
+ pid = line.split(' ')[0]
+ process = line.gsub(/(#{pid} |\n)/, '')
+ if process =~ /pgrep --full --list-name/
+ nil
+ else
+ {:pid => pid, :process => process}
+ end
+ }.compact
+ end
+
+ def assert_running(process)
+ assert pgrep(process).any?, "No running process for #{process}"
+ end
+
+ #
+ # runs the specified command, failing on a non-zero exit status.
+ #
+ def assert_run(command)
+ output = `#{command}`
+ if $?.exitstatus != 0
+ fail "Error running `#{command}`:\n#{output}"
+ end
+ end
+
+end \ No newline at end of file
diff --git a/tests/helpers/srp_helper.rb b/tests/helpers/srp_helper.rb
new file mode 100644
index 00000000..9f4d7f5b
--- /dev/null
+++ b/tests/helpers/srp_helper.rb
@@ -0,0 +1,171 @@
+#
+# Here are some very stripped down helper methods for SRP, useful only for
+# testing the client side.
+#
+
+require 'digest'
+require 'openssl'
+require 'securerandom'
+
+module SRP
+
+ ##
+ ## UTIL
+ ##
+
+ module Util
+ PRIME_N = <<-EOS.split.join.hex
+115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3
+ EOS
+ BIG_PRIME_N = <<-EOS.split.join.hex # 1024 bits modulus (N)
+eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c25657
+6d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089da
+d15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e5
+7ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb
+06e3
+ EOS
+ GENERATOR = 2 # g
+
+ def hn_xor_hg
+ byte_xor_hex(sha256_int(BIG_PRIME_N), sha256_int(GENERATOR))
+ end
+
+ # a^n (mod m)
+ def modpow(a, n, m = BIG_PRIME_N)
+ r = 1
+ while true
+ r = r * a % m if n[0] == 1
+ n >>= 1
+ return r if n == 0
+ a = a * a % m
+ end
+ end
+
+ # Hashes the (long) int args
+ def sha256_int(*args)
+ sha256_hex(*args.map{|a| "%02x" % a})
+ end
+
+ # Hashes the hex args
+ def sha256_hex(*args)
+ h = args.map{|a| a.length.odd? ? "0#{a}" : a }.join('')
+ sha256_str([h].pack('H*'))
+ end
+
+ def sha256_str(s)
+ Digest::SHA2.hexdigest(s)
+ end
+
+ def bigrand(bytes)
+ OpenSSL::Random.random_bytes(bytes).unpack("H*")[0]
+ end
+
+ def multiplier
+ @muliplier ||= calculate_multiplier
+ end
+
+ protected
+
+ def calculate_multiplier
+ sha256_int(BIG_PRIME_N, GENERATOR).hex
+ end
+
+ def byte_xor_hex(a, b)
+ a = [a].pack('H*')
+ b = [b].pack('H*')
+ a.bytes.each_with_index.map do |a_byte, i|
+ (a_byte ^ (b[i].ord || 0)).chr
+ end.join
+ end
+ end
+
+ ##
+ ## SESSION
+ ##
+
+ class Session
+ include SRP::Util
+ attr_accessor :user
+ attr_accessor :bb
+
+ def initialize(user, aa=nil)
+ @user = user
+ @a = bigrand(32).hex
+ end
+
+ def m
+ @m ||= sha256_hex(n_xor_g_long, login_hash, @user.salt.to_s(16), aa, bb, k)
+ end
+
+ def aa
+ @aa ||= modpow(GENERATOR, @a).to_s(16) # A = g^a (mod N)
+ end
+
+ protected
+
+ # client: K = H( (B - kg^x) ^ (a + ux) )
+ def client_secret
+ base = bb.hex
+ base -= modpow(GENERATOR, @user.private_key) * multiplier
+ base = base % BIG_PRIME_N
+ modpow(base, @user.private_key * u.hex + @a)
+ end
+
+ def k
+ @k ||= sha256_int(client_secret)
+ end
+
+ def n_xor_g_long
+ @n_xor_g_long ||= hn_xor_hg.bytes.map{|b| "%02x" % b.ord}.join
+ end
+
+ def login_hash
+ @login_hash ||= sha256_str(@user.username)
+ end
+
+ def u
+ @u ||= sha256_hex(aa, bb)
+ end
+ end
+
+ ##
+ ## Dummy USER
+ ##
+
+ class User
+ include SRP::Util
+
+ attr_accessor :username
+ attr_accessor :password
+ attr_accessor :salt
+ attr_accessor :verifier
+
+ def initialize
+ @username = "test_user_" + SecureRandom.urlsafe_base64(10).downcase.gsub(/[_-]/, '')
+ @password = "password_" + SecureRandom.urlsafe_base64(10)
+ @salt = bigrand(4).hex
+ @verifier = modpow(GENERATOR, private_key)
+ end
+
+ def private_key
+ @private_key ||= calculate_private_key
+ end
+
+ def to_params
+ {
+ 'user[login]' => @username,
+ 'user[password_verifier]' => @verifier.to_s(16),
+ 'user[password_salt]' => @salt.to_s(16)
+ }
+ end
+
+ private
+
+ def calculate_private_key
+ shex = '%x' % [@salt]
+ inner = sha256_str([@username, @password].join(':'))
+ sha256_hex(shex, inner).hex
+ end
+ end
+
+end
diff --git a/tests/order.rb b/tests/order.rb
index ffa6ae4e..4468686f 100644
--- a/tests/order.rb
+++ b/tests/order.rb
@@ -3,6 +3,10 @@ class LeapCli::Config::Node
# returns a list of node names that should be tested before this node.
# make sure to not return ourselves (please no dependency loops!).
#
+ # NOTE: this method determines the order that nodes are tested in. To specify
+ # the order of tests on a particular node, each test can call class method
+ # LeapTest.depends_on().
+ #
def test_dependencies
dependents = LeapCli::Config::ObjectList.new
unless services.include?('couchdb')
diff --git a/tests/white-box/couchdb.rb b/tests/white-box/couchdb.rb
index a5adb2bf..2788f4f7 100644
--- a/tests/white-box/couchdb.rb
+++ b/tests/white-box/couchdb.rb
@@ -1,4 +1,4 @@
-raise SkipTest unless $node["services"].include?("couchdb")
+raise SkipTest unless service?(:couchdb)
require 'json'
@@ -52,7 +52,7 @@ class CouchDB < LeapTest
#
def test_03_Are_configured_nodes_online?
return unless multimaster?
- url = couchdb_url("/_membership", :user => 'admin')
+ url = couchdb_url("/_membership", :username => 'admin')
assert_get(url) do |body|
response = JSON.parse(body)
nodes_configured_but_not_available = response['cluster_nodes'] - response['all_nodes']
@@ -71,7 +71,7 @@ class CouchDB < LeapTest
def test_04_Do_ACL_users_exist?
acl_users = ['_design/_auth', 'leap_mx', 'nickserver', 'soledad', 'tapicero', 'webapp', 'replication']
- url = couchdb_backend_url("/_users/_all_docs", :user => 'admin')
+ url = couchdb_backend_url("/_users/_all_docs", :username => 'admin')
assert_get(url) do |body|
response = JSON.parse(body)
assert_equal acl_users.count, response['total_rows']
@@ -84,7 +84,7 @@ class CouchDB < LeapTest
def test_05_Do_required_databases_exist?
dbs_that_should_exist = ["customers","identities","keycache","sessions","shared","tickets","tokens","users"]
dbs_that_should_exist.each do |db_name|
- url = couchdb_url("/"+db_name, :user => 'admin')
+ url = couchdb_url("/"+db_name, :username => 'admin')
assert_get(url) do |body|
assert response = JSON.parse(body)
assert_equal db_name, response['db_name']
@@ -102,50 +102,54 @@ class CouchDB < LeapTest
#def test_06_Is_ACL_enforced?
# ok = assert_auth_fail(
- # couchdb_url('/users/_all_docs', :user => 'leap_mx'),
+ # couchdb_url('/users/_all_docs', :username => 'leap_mx'),
# {:limit => 1}
# )
# ok = assert_auth_fail(
- # couchdb_url('/users/_all_docs', :user => 'leap_mx'),
+ # couchdb_url('/users/_all_docs', :username => 'leap_mx'),
# {:limit => 1}
# ) && ok
# pass if ok
#end
- def test_07_What?
+ def test_07_Can_records_be_created?
+ token = Token.new
+ url = couchdb_url("/tokens", :username => 'admin')
+ assert_post(url, token, :format => :json) do |body|
+ assert response = JSON.parse(body), "POST response should be JSON"
+ assert response["ok"], "POST response should be OK"
+ assert_delete(File.join(url, response["id"]), :rev => response["rev"]) do |body|
+ assert response = JSON.parse(body), "DELETE response should be JSON"
+ assert response["ok"], "DELETE response should be OK"
+ end
+ end
pass
end
private
- def couchdb_url(path="", options=nil)
- options||={}
- @port ||= begin
- assert_property 'couch.port'
- $node['couch']['port']
- end
- url = 'http://'
- if options[:user]
- assert_property 'couch.users.' + options[:user]
- password = $node['couch']['users'][options[:user]]['password']
- url += "%s:%s@" % [options[:user], password]
- end
- url += "localhost:#{options[:port] || @port}#{path}"
- url
+ def multimaster?
+ mode == "multimaster"
end
+ def mode
+ assert_property('couch.mode')
+ end
+
+ # TODO: admin port is hardcoded for now but should be configurable.
def couchdb_backend_url(path="", options={})
- # TODO: admin port is hardcoded for now but should be configurable.
options = {port: multimaster? && "5986"}.merge options
couchdb_url(path, options)
end
- def multimaster?
- mode == "multimaster"
- end
-
- def mode
- assert_property('couch.mode')
+ require 'securerandom'
+ require 'digest/sha2'
+ class Token < Hash
+ def initialize
+ self['token'] = SecureRandom.urlsafe_base64(32).gsub(/^_*/, '')
+ self['_id'] = Digest::SHA512.hexdigest(self['token'])
+ self['last_seen_at'] = Time.now
+ end
end
end
diff --git a/tests/white-box/mx.rb b/tests/white-box/mx.rb
new file mode 100644
index 00000000..794a9a41
--- /dev/null
+++ b/tests/white-box/mx.rb
@@ -0,0 +1,50 @@
+raise SkipTest unless service?(:mx)
+
+require 'json'
+
+class Mx < LeapTest
+ depends_on "Network"
+
+ def setup
+ end
+
+ def test_01_Can_contact_couchdb?
+ dbs = ["identities"]
+ dbs.each do |db_name|
+ couchdb_urls("/"+db_name, url_options).each do |url|
+ assert_get(url) do |body|
+ assert response = JSON.parse(body)
+ assert_equal db_name, response['db_name']
+ end
+ end
+ end
+ pass
+ end
+
+ def test_02_Can_contact_couchdb_via_haproxy?
+ if property('haproxy.couch')
+ url = couchdb_url_via_haproxy("", url_options)
+ assert_get(url) do |body|
+ assert_match /"couchdb":"Welcome"/, body, "Request to #{url} should return couchdb welcome message."
+ end
+ pass
+ end
+ end
+
+ def test_03_Are_MX_daemons_running?
+ assert_running 'leap_mx'
+ assert_running '/usr/lib/postfix/master'
+ assert_running '/usr/sbin/unbound'
+ pass
+ end
+
+ private
+
+ def url_options
+ {
+ :username => property('couchdb_leap_mx_user.username'),
+ :password => property('couchdb_leap_mx_user.password')
+ }
+ end
+
+end
diff --git a/tests/white-box/openvpn.rb b/tests/white-box/openvpn.rb
index 5eb2bdb5..23a40426 100644
--- a/tests/white-box/openvpn.rb
+++ b/tests/white-box/openvpn.rb
@@ -1,6 +1,6 @@
-raise SkipTest unless $node["services"].include?("openvpn")
+raise SkipTest unless service?(:openvpn)
-class Openvpn < LeapTest
+class OpenVPN < LeapTest
depends_on "Network"
def setup
diff --git a/tests/white-box/soledad.rb b/tests/white-box/soledad.rb
new file mode 100644
index 00000000..5a13e4a6
--- /dev/null
+++ b/tests/white-box/soledad.rb
@@ -0,0 +1,17 @@
+raise SkipTest unless service?(:soledad)
+
+require 'json'
+
+class Soledad < LeapTest
+ depends_on "Network"
+ depends_on "CouchDB" if service?(:couchdb)
+
+ def setup
+ end
+
+ def test_00_Is_Soledad_running?
+ assert_running 'soledad'
+ pass
+ end
+
+end
diff --git a/tests/white-box/webapp.rb b/tests/white-box/webapp.rb
index 7df57fd7..2aa87403 100644
--- a/tests/white-box/webapp.rb
+++ b/tests/white-box/webapp.rb
@@ -1,58 +1,29 @@
-raise SkipTest unless $node["services"].include?("webapp")
+raise SkipTest unless service?(:webapp)
-require 'socket'
+require 'json'
class Webapp < LeapTest
depends_on "Network"
- HAPROXY_CONFIG = '/etc/haproxy/haproxy.cfg'
-
def setup
end
- #
- # example properties:
- #
- # stunnel:
- # clients:
- # couch_client:
- # couch1_5984:
- # accept_port: 4000
- # connect: couch1.bitmask.i
- # connect_port: 15984
- #
def test_01_Can_contact_couchdb?
- assert_property('stunnel.clients.couch_client')
- $node['stunnel']['clients']['couch_client'].values.each do |stunnel_conf|
- assert port = stunnel_conf['accept_port'], 'Field `accept_port` must be present in `stunnel` property.'
- local_stunnel_url = "http://localhost:#{port}"
- remote_ip_address = TCPSocket.gethostbyname(stunnel_conf['connect']).last
- msg = "(stunnel to %s:%s, aka %s)" % [stunnel_conf['connect'], stunnel_conf['connect_port'], remote_ip_address]
- assert_get(local_stunnel_url, nil, error_msg: msg) do |body|
- assert_match /"couchdb":"Welcome"/, body, "Request to #{local_stunnel_url} should return couchdb welcome message."
- end
+ url = couchdb_url("", url_options)
+ assert_get(url) do |body|
+ assert_match /"couchdb":"Welcome"/, body, "Request to #{url} should return couchdb welcome message."
end
pass
end
- #
- # example properties:
- #
- # haproxy:
- # servers:
- # couch1:
- # backup: false
- # host: localhost
- # port: 4000
- # weight: 10
- #
- def test_02_Is_haproxy_working?
- port = file_match(HAPROXY_CONFIG, /^ bind localhost:(\d+)$/)
- url = "http://localhost:#{port}"
- assert_get(url) do |body|
- assert_match /"couchdb":"Welcome"/, body, "Request to #{url} should return couchdb welcome message."
+ def test_02_Can_contact_couchdb_via_haproxy?
+ if property('haproxy.couch')
+ url = couchdb_url_via_haproxy("", url_options)
+ assert_get(url) do |body|
+ assert_match /"couchdb":"Welcome"/, body, "Request to #{url} should return couchdb welcome message."
+ end
+ pass
end
- pass
end
def test_03_Are_daemons_running?
@@ -70,4 +41,94 @@ class Webapp < LeapTest
pass
end
+ def test_05_Can_create_user?
+ @@user = nil
+ user = SRP::User.new
+ url = api_url("/1/users.json")
+ assert_post(url, user.to_params) do |body|
+ assert response = JSON.parse(body), 'response should be JSON'
+ assert response['ok'], 'creating a user should be successful'
+ end
+ @@user = user
+ pass
+ end
+
+ def test_06_Can_authenticate?
+ @@user_id = nil
+ @@session_token = nil
+ if @@user.nil?
+ skip "Depends on user creation"
+ else
+ url = api_url("/1/sessions.json")
+ session = SRP::Session.new(@@user)
+ params = {'login' => @@user.username, 'A' => session.aa}
+ assert_post(url, params) do |response, body|
+ cookie = response['Set-Cookie'].split(';').first
+ assert(response = JSON.parse(body), 'response should be JSON')
+ assert(bb = response["B"])
+ session.bb = bb
+ url = api_url("/1/sessions/login.json")
+ params = {'client_auth' => session.m, 'A' => session.aa}
+ options = {:headers => {'Cookie' => cookie}}
+ assert_put(url, params, options) do |body|
+ assert(response = JSON.parse(body), 'response should be JSON')
+ assert(response['M2'], 'response should include M2')
+ assert(@@session_token = response['token'], 'response should include token')
+ assert(@@user_id = response['id'], 'response should include user id')
+ end
+ end
+ pass
+ end
+ end
+
+ def test_07_Can_delete_user?
+ if @@user_id.nil? || @@session_token.nil?
+ skip "Depends on authentication"
+ else
+ url = api_url("/1/users/#{@@user_id}.json")
+ options = {:headers => {
+ "Authorization" => "Token token=\"#{@@session_token}\""
+ }}
+ delete(url, {}, options) do |body, response, error|
+ if response.code.to_i != 200
+ skip "It appears the web api is too old to support deleting users"
+ else
+ assert(response = JSON.parse(body), 'response should be JSON')
+ assert(response["success"], 'delete should be a success')
+ pass
+ end
+ end
+ end
+ end
+
+ private
+
+ def url_options
+ {
+ :username => property('couchdb_webapp_user.username'),
+ :password => property('couchdb_webapp_user.password')
+ }
+ end
+
+ def api_url(path)
+ "https://%{domain}:%{port}#{path}" % {
+ :domain => property('api.domain'),
+ :port => property('api.port')
+ }
+ end
+
+ #
+ # I tried, but couldn't get this working:
+ # #
+ # # get an CSRF authenticity token
+ # #
+ # url = api_url("/")
+ # csrf_token = nil
+ # assert_get(url) do |body|
+ # lines = body.split("\n").grep(/csrf-token/)
+ # assert lines.any?, 'failed to find csrf-token'
+ # csrf_token = lines.first.split('"')[1]
+ # assert csrf_token, 'failed to find csrf-token'
+ # end
+
end