summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/README.md27
-rw-r--r--tests/example-provider/README.md8
-rw-r--r--tests/example-provider/Vagrantfile58
-rw-r--r--tests/example-provider/hiera.yaml6
-rwxr-xr-xtests/example-provider/vagrant/add-pixelated.sh32
-rwxr-xr-xtests/example-provider/vagrant/configure-leap.sh92
-rwxr-xr-xtests/example-provider/vagrant/install-platform.pp15
-rw-r--r--tests/example-provider/vagrant/vagrant.config23
-rw-r--r--tests/platform-ci/Gemfile17
-rw-r--r--tests/platform-ci/README.md15
-rw-r--r--tests/platform-ci/Rakefile121
-rwxr-xr-xtests/platform-ci/ci-build.sh90
-rw-r--r--tests/platform-ci/hiera.yaml16
-rw-r--r--tests/platform-ci/provider/Leapfile1
-rw-r--r--tests/platform-ci/provider/cloud.json.template15
-rw-r--r--tests/platform-ci/provider/common.json12
-rw-r--r--tests/platform-ci/provider/files/ca/ca.crt32
-rw-r--r--tests/platform-ci/provider/files/ca/ca.key51
-rw-r--r--tests/platform-ci/provider/files/ca/client_ca.crt33
-rw-r--r--tests/platform-ci/provider/files/ca/client_ca.key51
-rw-r--r--tests/platform-ci/provider/files/ca/dh.pem19
-rw-r--r--tests/platform-ci/provider/files/cert/commercial_ca.crt32
-rw-r--r--tests/platform-ci/provider/files/cert/example.org.crt31
-rw-r--r--tests/platform-ci/provider/files/cert/example.org.csr27
-rw-r--r--tests/platform-ci/provider/files/cert/example.org.key51
-rw-r--r--tests/platform-ci/provider/files/mx/dkim.key27
-rw-r--r--tests/platform-ci/provider/files/mx/dkim.pub9
-rw-r--r--tests/platform-ci/provider/files/ssh/monitor_ssh51
-rw-r--r--tests/platform-ci/provider/files/ssh/monitor_ssh.pub1
-rw-r--r--tests/platform-ci/provider/nodes/catalogtest.json39
-rw-r--r--tests/platform-ci/provider/provider.json18
-rw-r--r--tests/platform-ci/provider/tags/catalogtest.json1
-rw-r--r--tests/platform-ci/provider/users/gitlab-runner/gitlab-runner_ssh.pub1
-rwxr-xr-xtests/platform-ci/setup.sh4
-rw-r--r--tests/server-tests/README.md44
-rw-r--r--tests/server-tests/helpers/bonafide_helper.rb (renamed from tests/helpers/bonafide_helper.rb)0
-rw-r--r--tests/server-tests/helpers/client_side_db.py (renamed from tests/helpers/client_side_db.py)0
-rw-r--r--tests/server-tests/helpers/couchdb_helper.rb (renamed from tests/helpers/couchdb_helper.rb)1
-rw-r--r--tests/server-tests/helpers/files_helper.rb (renamed from tests/helpers/files_helper.rb)0
-rw-r--r--tests/server-tests/helpers/http_helper.rb (renamed from tests/helpers/http_helper.rb)0
-rw-r--r--tests/server-tests/helpers/network_helper.rb (renamed from tests/helpers/network_helper.rb)0
-rw-r--r--tests/server-tests/helpers/os_helper.rb (renamed from tests/helpers/os_helper.rb)2
-rw-r--r--tests/server-tests/helpers/smtp_helper.rb (renamed from tests/helpers/smtp_helper.rb)0
-rwxr-xr-xtests/server-tests/helpers/soledad_sync.py (renamed from tests/helpers/soledad_sync.py)0
-rw-r--r--tests/server-tests/helpers/srp_helper.rb (renamed from tests/helpers/srp_helper.rb)0
-rw-r--r--tests/server-tests/order.rb (renamed from tests/order.rb)0
-rw-r--r--tests/server-tests/white-box/couchdb.rb (renamed from tests/white-box/couchdb.rb)17
-rw-r--r--tests/server-tests/white-box/dummy.rb (renamed from tests/white-box/dummy.rb)0
-rw-r--r--tests/server-tests/white-box/mx.rb (renamed from tests/white-box/mx.rb)173
-rw-r--r--tests/server-tests/white-box/network.rb (renamed from tests/white-box/network.rb)4
-rw-r--r--tests/server-tests/white-box/openvpn.rb (renamed from tests/white-box/openvpn.rb)0
-rw-r--r--tests/server-tests/white-box/soledad.rb (renamed from tests/white-box/soledad.rb)0
-rw-r--r--tests/server-tests/white-box/webapp.rb (renamed from tests/white-box/webapp.rb)46
53 files changed, 1202 insertions, 111 deletions
diff --git a/tests/README.md b/tests/README.md
index 814c25b1..ea6bcaa9 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -1,25 +1,24 @@
-Tests
----------------------------------
+What is here?
-tests/white-box/
+**server-tests/**
- These tests are run on the server as superuser. They are for
- troubleshooting any problems with the internal setup of the server.
+These are the tests run on a provider's servers using the command:
-tests/black-box/
+ workstation$ leap test
- These test are run the user's local machine. They are for troubleshooting
- any external problems with the service exposed by the server.
+Or the command:
-Additional Files
----------------------------------
+ server# run_tests
-tests/helpers/
+These tests are to confirm that a provider's infrasture is working and to troubleshoot any possible problems.
- Utility functions made available to all tests.
+**example-provider/**
-tests/order.rb
+Allows you to generate a pre-configured provider using Vagrant virtual
+machines.
- Configuration file to specify which nodes should be tested in which order.
+**platform-ci/**
+Continous integration tests run for the LEAP Platform. These tests are for the
+platform code itself.
diff --git a/tests/example-provider/README.md b/tests/example-provider/README.md
new file mode 100644
index 00000000..80cb3ae9
--- /dev/null
+++ b/tests/example-provider/README.md
@@ -0,0 +1,8 @@
+Here lies a script to generate a pre-configured provider using Vagrant virtual
+machines. This virtual provider includes only a single node.
+
+All you have to do is this:
+
+ cd leap_platform/tests/example-provider
+ vagrant up
+
diff --git a/tests/example-provider/Vagrantfile b/tests/example-provider/Vagrantfile
new file mode 100644
index 00000000..1e410f5e
--- /dev/null
+++ b/tests/example-provider/Vagrantfile
@@ -0,0 +1,58 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+Vagrant.configure("2") do |config|
+
+ # shared config for all boxes
+
+ # make the leap_platform directory available as /srv/leap_platform
+ # inside the virtual machine.
+ config.vm.synced_folder "../..", "/srv/leap_platform"
+
+ # Please verify the sha512 sum of the downloaded box before importing it into vagrant !
+ # see https://leap.se/en/docs/platform/details/development#Verify.vagrantbox.download
+ # for details
+ config.vm.box = "LEAP/jessie"
+
+ config.vm.provider "virtualbox" do |v|
+ v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
+ v.name = "jessie"
+ v.memory = 1536
+ end
+
+ config.vm.provider "libvirt" do |v|
+ v.memory = 1536
+ end
+
+ # Fix annoying 'stdin: is not a tty' warning
+ # see http://foo-o-rama.com/vagrant--stdin-is-not-a-tty--fix.html
+ config.vm.provision "shell" do |s|
+ s.privileged = false
+ s.inline = "sudo sed -i '/tty/!s/mesg n/tty -s \\&\\& mesg n/' /root/.profile"
+ end
+
+ config.vm.provision "puppet" do |puppet|
+ puppet.manifests_path = "./vagrant"
+ puppet.module_path = "../../puppet/modules"
+ puppet.manifest_file = "install-platform.pp"
+ puppet.options = "--verbose"
+ puppet.hiera_config_path = "./hiera.yaml"
+ end
+ config.vm.provision "shell", path: "vagrant/configure-leap.sh"
+
+ config.ssh.username = "vagrant"
+
+ # forward leap_web ports
+ config.vm.network "forwarded_port", guest: 443, host:4443
+ # forward pixelated ports
+ config.vm.network "forwarded_port", guest: 8080, host:8080
+ config.vm.network "forwarded_port", guest: 4430, host:4430
+
+ config.vm.define :"leap_platform", primary: true do |leap_vagrant|
+ end
+
+ config.vm.define :"pixelated", autostart: false do |pixelated_vagrant|
+ pixelated_vagrant.vm.provision "shell", path: "vagrant/add-pixelated.sh"
+ end
+
+end
diff --git a/tests/example-provider/hiera.yaml b/tests/example-provider/hiera.yaml
new file mode 100644
index 00000000..3ff857b8
--- /dev/null
+++ b/tests/example-provider/hiera.yaml
@@ -0,0 +1,6 @@
+---
+:backends: yaml
+:yaml:
+ :datadir: /var/lib/hiera
+:hierarchy: common
+:logger: console
diff --git a/tests/example-provider/vagrant/add-pixelated.sh b/tests/example-provider/vagrant/add-pixelated.sh
new file mode 100755
index 00000000..f9908947
--- /dev/null
+++ b/tests/example-provider/vagrant/add-pixelated.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+#
+# adds pixelated-server to the node
+
+. /vagrant/vagrant/vagrant.config
+
+cd "$PROVIDERDIR"
+
+if ! git submodule status files/puppet/modules/pixelated > /dev/null 2>&1; then
+ git submodule add https://github.com/pixelated/puppet-pixelated.git files/puppet/modules/pixelated
+fi
+
+echo '{}' > services/pixelated.json
+[ -d files/puppet/modules/custom/manifests ] || mkdir -p files/puppet/modules/custom/manifests
+echo 'class custom { include ::pixelated}' > files/puppet/modules/custom/manifests/init.pp
+
+$LEAP $OPTS -v 2 deploy
+
+echo '==============================================='
+echo 'testing the platform'
+echo '==============================================='
+
+$LEAP $OPTS -v 2 test --continue
+
+
+echo -e '\n===========================================================================================================\n\n'
+echo -e 'You are now ready to use your vagrant Pixelated provider.\n'
+
+echo -e 'The LEAP webapp is available at https://localhost:4443. Use it to register an account before using the Pixelated Useragent.\n'
+echo -e 'The Pixelated Useragent is available at https://localhost:8080\n'
+
+echo -e 'Please add an exception for both sites in your browser dialog to allow the self-signed certificate.\n'
diff --git a/tests/example-provider/vagrant/configure-leap.sh b/tests/example-provider/vagrant/configure-leap.sh
new file mode 100755
index 00000000..fd34d7ea
--- /dev/null
+++ b/tests/example-provider/vagrant/configure-leap.sh
@@ -0,0 +1,92 @@
+#!/bin/bash
+
+
+. /vagrant/vagrant/vagrant.config
+
+echo '==============================================='
+echo 'configuring leap'
+echo '==============================================='
+
+# purge $PROVIDERDIR so this script can be run multiple times
+[ -e $PROVIDERDIR ] && rm -rf $PROVIDERDIR
+
+mkdir -p $PROVIDERDIR
+chown ${USER}:${USER} ${PROVIDERDIR}
+cd $PROVIDERDIR
+
+$LEAP $OPTS new --contacts "$contacts" --domain "$provider_domain" --name "$provider_name" --platform="$PLATFORMDIR" .
+echo -e '\n@log = "./deploy.log"' >> Leapfile
+
+if [ ! -e /home/${USER}/.ssh/id_rsa ]; then
+ $SUDO ssh-keygen -f /home/${USER}/.ssh/id_rsa -P ''
+ [ -d /root/.ssh ] || mkdir /root/.ssh
+ cat /home/${USER}/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys
+fi
+
+$SUDO mkdir -p ${PROVIDERDIR}/files/nodes/${NODE}
+sh -c "cat /etc/ssh/ssh_host_rsa_key.pub | cut -d' ' -f1,2 >> $PROVIDERDIR/files/nodes/$NODE/${NODE}_ssh.pub"
+chown ${USER}:${USER} ${PROVIDERDIR}/files/nodes/${NODE}/${NODE}_ssh.pub
+
+$LEAP $OPTS add-user --self
+$LEAP $OPTS cert ca
+$LEAP $OPTS cert csr
+$LEAP $OPTS node add $NODE ip_address:"$(facter ipaddress)" couch.mode:plain services:"$services" tags:production
+echo '{ "webapp": { "admins": ["testadmin"] } }' > services/webapp.json
+
+$LEAP $OPTS compile
+
+$GIT init
+$GIT add .
+$GIT commit -m'configured provider'
+
+$LEAP $OPTS node init $NODE
+if [ $? -eq 1 ]; then
+ echo 'node init failed'
+ exit 1
+fi
+
+# couchrest gem does currently not install on jessie
+# https://leap.se/code/issues/7754
+# workaround is to install rake as gem
+gem install rake
+
+$LEAP $OPTS -v 2 deploy
+
+$GIT add .
+$GIT commit -m'initialized and deployed provider'
+
+# Vagrant: leap_mx fails to start on jessie
+# https://leap.se/code/issues/7755
+# Workaround: we stop and start leap-mx after deploy and
+# before testing
+
+service leap-mx stop
+service leap-mx start
+
+
+
+echo '==============================================='
+echo 'testing the platform'
+echo '==============================================='
+
+$LEAP $OPTS -v 2 test --continue
+
+echo '==============================================='
+echo 'setting node to demo-mode'
+echo '==============================================='
+postconf -e default_transport='error: in demo mode'
+
+# add users: testadmin and testuser with passwords "hallo123"
+curl -s -k https://localhost/1/users.json -d "user%5Blogin%5D=testuser&user%5Bpassword_salt%5D=7d4880237a038e0e&user%5Bpassword_verifier%5D=b98dc393afcd16e5a40fb57ce9cddfa6a978b84be326196627c111d426cada898cdaf3a6427e98b27daf4b0ed61d278bc856515aeceb2312e50c8f816659fcaa4460d839a1e2d7ffb867d32ac869962061368141c7571a53443d58dc84ca1fca34776894414c1090a93e296db6cef12c2cc3f7a991b05d49728ed358fd868286"
+curl -s -k https://localhost/1/users.json -d "user%5Blogin%5D=testadmin&user%5Bpassword_salt%5D=ece1c457014d8282&user%5Bpassword_verifier%5D=9654d93ab409edf4ff1543d07e08f321107c3fd00de05c646c637866a94f28b3eb263ea9129dacebb7291b3374cc6f0bf88eb3d231eb3a76eed330a0e8fd2a5c477ed2693694efc1cc23ae83c2ae351a21139701983dd595b6c3225a1bebd2a4e6122f83df87606f1a41152d9890e5a11ac3749b3bfcf4407fc83ef60b4ced68"
+
+echo -e '\n===========================================================================================================\n\n'
+echo -e 'You are now ready to use your local LEAP provider.\n'
+echo 'If you want to use the *Bitmask client* with your provider, please update your /etc/hosts with following dns overrides:'
+
+$LEAP list --print ip_address,domain.full,dns.aliases | sed 's/^.* //' | sed 's/, null//g' | tr -d '\]\[",'
+
+echo 'Please see https://leap.se/en/docs/platform/tutorials/vagrant#use-the-bitmask-client-to-do-an-initial-soledad-sync for more details how to use and test your LEAP provider.'
+echo -e "\nIf you don't want to use the Bitmask client, please ignore the above instructions.\n"
+echo -e 'The LEAP webapp is now available at https://localhost:4443\n'
+echo -e 'Please add an exception in your browser dialog to allow the self-signed certificate.\n'
diff --git a/tests/example-provider/vagrant/install-platform.pp b/tests/example-provider/vagrant/install-platform.pp
new file mode 100755
index 00000000..223853c1
--- /dev/null
+++ b/tests/example-provider/vagrant/install-platform.pp
@@ -0,0 +1,15 @@
+class {'apt': }
+Exec['update_apt'] -> Package <||>
+
+# install leap_cli from source, so it will work with the develop
+# branch of leap_platform
+class { '::leap::cli::install':
+ source => true,
+}
+
+file { [ '/srv/leap', '/srv/leap/configuration', '/var/log/leap' ]:
+ ensure => directory
+}
+
+# install prerequisites for configuring the provider
+include ::git
diff --git a/tests/example-provider/vagrant/vagrant.config b/tests/example-provider/vagrant/vagrant.config
new file mode 100644
index 00000000..60d2a52c
--- /dev/null
+++ b/tests/example-provider/vagrant/vagrant.config
@@ -0,0 +1,23 @@
+# provider config values used by vagrant provision scripts
+provider_domain='example.org'
+provider_name='Leap Example Provider'
+contacts="no-reply@$provider_domain"
+
+# serivces that get configured
+# note that the "openvpn" service does currently *not* work
+# in a vagrant setup,
+# see https://leap.se/en/docs/platform/troubleshooting/known-issues#Special.Environments
+# to speed up things, don't deploy monitor service by default
+# services='webapp,mx,couchdb,soledad,monitor'
+services='webapp,mx,couchdb,soledad'
+
+# default vars used by vagrant provision scripts
+OPTS=''
+USER='vagrant'
+NODE='node1'
+SUDO="sudo -u ${USER}"
+PROVIDERDIR="/home/${USER}/leap/configuration"
+PLATFORMDIR="/srv/leap_platform"
+LEAP="$SUDO /usr/local/bin/leap"
+GIT="$SUDO git"
+
diff --git a/tests/platform-ci/Gemfile b/tests/platform-ci/Gemfile
new file mode 100644
index 00000000..36f556e5
--- /dev/null
+++ b/tests/platform-ci/Gemfile
@@ -0,0 +1,17 @@
+source "https://rubygems.org"
+
+group :test do
+ gem "rake"
+ gem "rspec"
+ gem "puppet", ENV['PUPPET_VERSION'] || ENV['GEM_PUPPET_VERSION'] || ENV['PUPPET_GEM_VERSION'] || '~> 3.8'
+ gem "facter", ENV['FACTER_VERSION'] || ENV['GEM_FACTER_VERSION'] || ENV['FACTER_GEM_VERSION'] || '~> 2.2.0'
+ gem "rspec-puppet"
+ gem "puppetlabs_spec_helper"
+ gem "metadata-json-lint"
+ gem "rspec-puppet-facts"
+ gem "mocha"
+ # Use puppet-catalog-test from git because last released gem 0.4.2 gives a deprecation
+ # warning: "[DEPRECATION] `last_comment` is deprecated. Please use `last_description` instead."
+ gem "puppet-catalog-test", :git => 'https://github.com/invadersmustdie/puppet-catalog-test.git'
+ gem "leap_cli", :git => 'https://leap.se/git/leap_cli.git', :branch => 'develop'
+end
diff --git a/tests/platform-ci/README.md b/tests/platform-ci/README.md
new file mode 100644
index 00000000..60c17e41
--- /dev/null
+++ b/tests/platform-ci/README.md
@@ -0,0 +1,15 @@
+Continuous integration tests for the leap_platform code.
+
+Usage:
+
+ ./setup.sh
+ bin/rake test:syntax
+ bin/rake test:catalog
+
+For a list of all tasks:
+
+ bin/rake -T
+
+To create a virtual provider, run tests on it, then tear it down:
+
+ ./ci-build.sh
diff --git a/tests/platform-ci/Rakefile b/tests/platform-ci/Rakefile
new file mode 100644
index 00000000..5443be36
--- /dev/null
+++ b/tests/platform-ci/Rakefile
@@ -0,0 +1,121 @@
+require 'puppetlabs_spec_helper/rake_tasks'
+require 'puppet-lint/tasks/puppet-lint'
+require 'puppet-syntax/tasks/puppet-syntax'
+require 'puppet-catalog-test'
+
+CI_DIR = File.dirname(__FILE__)
+PLATFORM_DIR = File.expand_path('../..', CI_DIR)
+PROVIDER_DIR = File.join(CI_DIR, 'provider')
+
+#
+# return list of modules, either "external" (submodules or subrepos), "custom"
+# (no submodules nor subrepos) or all modules so we can check each array
+# seperately
+#
+def modules_pattern (type)
+ external = Array.new
+ internal = Array.new
+ all = Array.new
+
+ Dir.chdir(PLATFORM_DIR) do
+ Dir['puppet/modules/*'].sort.each do |m|
+
+ # submodule or subrepo ?
+ system("grep -q #{m} .gitmodules 2>/dev/null || test -f #{m}/.gitrepo")
+ if $?.exitstatus == 0
+ external << m + '/**/*.pp'
+ else
+ internal << m + '/**/*.pp'
+ end
+ all << m + '/**/*.pp'
+ end
+
+ case type
+ when 'external'
+ external
+ when 'internal'
+ internal
+ when 'all'
+ all
+ end
+ end
+end
+
+exclude_paths = ["**/vendor/**/*", "spec/fixtures/**/*", "pkg/**/*" ]
+
+#
+# redefine lint task so we don't lint submoudules for now
+#
+Rake::Task[:lint].clear
+PuppetLint::RakeTask.new :lint do |config|
+ # only check for custom manifests, not submodules for now
+ config.pattern = modules_pattern('internal')
+ config.ignore_paths = exclude_paths
+ config.disable_checks = ['documentation', '140chars', 'arrow_alignment']
+ config.fail_on_warnings = false
+end
+
+# rake syntax::* tasks
+PuppetSyntax.exclude_paths = exclude_paths
+PuppetSyntax.future_parser = true
+
+desc "Validate erb templates"
+task :templates do
+ Dir.chdir(PLATFORM_DIR) do
+ Dir['**/templates/**/*.erb'].each do |template|
+ sh "erb -P -x -T '-' #{template} | ruby -c" unless template =~ /.*vendor.*/
+ end
+ end
+end
+
+namespace :platform do
+ desc "Compile hiera config for test_provider"
+ task :provider_compile do
+ Dir.chdir(PROVIDER_DIR) do
+ sh "bundle exec leap compile"
+ end
+ end
+end
+
+PuppetCatalogTest::RakeTask.new('catalog') do |t|
+ Rake::Task["platform:provider_compile"].invoke
+ t.module_paths = [File.join(PLATFORM_DIR, "puppet", "modules")]
+ t.manifest_path = File.join(PLATFORM_DIR, "puppet","manifests", "site.pp")
+ t.facts = {
+ "operatingsystem" => "Debian",
+ "osfamily" => "Debian",
+ "operatingsystemmajrelease" => "8",
+ "debian_release" => "stable",
+ "debian_codename" => "jessie",
+ "lsbdistcodename" => "jessie",
+ "concat_basedir" => "/var/lib/puppet/concat",
+ "interfaces" => "eth0"
+ }
+
+ # crucial option for hiera integration
+ t.config_dir = CI_DIR # expects hiera.yaml to be included in directory
+
+ # t.parser = "future"
+ #t.verbose = true
+end
+
+
+namespace :test do
+ # :syntax:templates fails on squirrel, see https://jenkins.leap.se/view/Platform%20Builds/job/platform_citest/115/console
+ # but we have our own synax test
+ desc "Run all puppet syntax checks required for CI (syntax , validate, templates, spec, lint)"
+ task :syntax => [:"syntax:hiera", :"syntax:manifests", :validate, :templates, :spec, :lint]
+
+ desc "Tries to compile the catalog"
+ task :catalog => [:catalog]
+
+ #task :all => [:syntax, :catalog]
+end
+
+# unfortunatly, we cannot have one taks to rule them all
+# because :catalog would conflict with :syntax or :validate:
+# rake aborted!
+# Puppet::DevError: Attempting to initialize global default settings more than once!
+# /home/varac/dev/projects/leap/git/leap_platform/vendor/bundle/ruby/2.3.0/gems/puppet-3.8.7/lib/puppet/settings.rb:261:in `initialize_global_settings'
+#desc "Run all platform tests"
+#task :test => 'test:all'
diff --git a/tests/platform-ci/ci-build.sh b/tests/platform-ci/ci-build.sh
new file mode 100755
index 00000000..85557b3f
--- /dev/null
+++ b/tests/platform-ci/ci-build.sh
@@ -0,0 +1,90 @@
+#!/bin/bash
+#
+# This script will run create a virtual provider
+# and run tests on it.
+#
+# This script is triggered by .gitlab-ci.yml
+#
+# It depends on:
+# * leap_platform: in ../..
+# * test provider: in provider/
+# * leap-platform-test: installed in path
+# * AWS credentials as environment variables:
+# * `AWS_ACCESS_KEY`
+# * `AWS_SECRET_KEY`
+# * ssh private key used to login to remove vm
+# * `SSH_PRIVATE_KEY`
+#
+# Todo:
+# - Running locally works fine, now use it in gitlab CI ( which ssh-key ? create cloud.json from env vars )
+# - Speed up vm boot if possible ( right now 3-4mins )
+
+# exit if any commands returns non-zero status
+set -e
+
+# leap_platform/tests/platform-ci
+# shellcheck disable=SC2086
+ROOTDIR=$(readlink -f "$(dirname $0)")
+
+# leap_platform/tests/platform-ci/provider
+PROVIDERDIR="${ROOTDIR}/provider"
+
+# leap_platform
+PLATFORMDIR=$(readlink -f "${ROOTDIR}/../..")
+
+LEAP_CMD="/usr/local/bin/bundle exec leap -v2 --yes"
+
+# create node(s) with unique id so we can run tests in parallel
+NAME="citest${CI_BUILD_ID}"
+# when using gitlab-runner locally, CI_BUILD_ID is always 1 which
+# will conflict with running/terminating AWS instances in subsequent runs
+# therefore we pick a random number in this case
+[ "$CI_BUILD_ID" -eq "1" ] && NAME+="000${RANDOM}"
+
+TAG='single'
+SERVICES='couchdb,soledad,mx,webapp,tor,monitor'
+SEEDS='sources.platform.apt.basic:http://deb.leap.se/experimental-0.9 sources.webapp.revision:master sources.nickserver.revision:master'
+
+
+#
+# Main
+#
+
+
+/bin/echo "CI directory: ${ROOTDIR}"
+/bin/echo "Provider directory: ${PROVIDERDIR}"
+/bin/echo "Platform directory: ${PLATFORMDIR}"
+cd "$PROVIDERDIR"
+
+# Ensure we don't output secret stuff to console even when running in verbose mode with -x
+set +x
+
+# Create cloud.json needed for `leap vm` commands using AWS credentials
+which jq || ( apt-get update -y && apt-get install jq -y )
+/usr/bin/jq ".platform_ci.auth |= .+ {\"aws_access_key_id\":\"$AWS_ACCESS_KEY\", \"aws_secret_access_key\":\"$AWS_SECRET_KEY\"}" < cloud.json.template > cloud.json
+
+# Configure ssh keypair
+[ -d ~/.ssh ] || /bin/mkdir ~/.ssh
+/bin/echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
+/bin/chmod 600 ~/.ssh/id_rsa
+/bin/cp users/gitlab-runner/gitlab-runner_ssh.pub ~/.ssh/id_rsa.pub
+
+[ -d "./tags" ] || mkdir "./tags"
+/bin/echo "{\"environment\": \"$TAG\"}" | /usr/bin/json_pp > "${PROVIDERDIR}/tags/${TAG}.json"
+
+$LEAP_CMD vm status "$TAG"
+# shellcheck disable=SC2086
+$LEAP_CMD vm add "$NAME" services:"$SERVICES" tags:"$TAG" $SEEDS
+$LEAP_CMD compile "$TAG"
+$LEAP_CMD vm status "$TAG"
+
+$LEAP_CMD node init "$TAG"
+$LEAP_CMD info "${TAG}"
+
+# Deploy and test
+$LEAP_CMD deploy "$TAG"
+$LEAP_CMD test "$TAG"
+
+# if everything succeeds, destroy the vm
+$LEAP_CMD vm rm "${TAG}"
+[ -f "nodes/${NAME}.json" ] && /bin/rm "nodes/${NAME}.json"
diff --git a/tests/platform-ci/hiera.yaml b/tests/platform-ci/hiera.yaml
new file mode 100644
index 00000000..a23d8b92
--- /dev/null
+++ b/tests/platform-ci/hiera.yaml
@@ -0,0 +1,16 @@
+---
+:backends:
+ - yaml
+ - puppet
+
+:logger: console
+
+:yaml:
+ :datadir: provider/hiera
+
+:hierarchy:
+ - catalogtest
+
+:puppet:
+ :datasource: data
+
diff --git a/tests/platform-ci/provider/Leapfile b/tests/platform-ci/provider/Leapfile
new file mode 100644
index 00000000..4852aed7
--- /dev/null
+++ b/tests/platform-ci/provider/Leapfile
@@ -0,0 +1 @@
+@platform_directory_path = File.expand_path("../../../..", __FILE__)
diff --git a/tests/platform-ci/provider/cloud.json.template b/tests/platform-ci/provider/cloud.json.template
new file mode 100644
index 00000000..28152e82
--- /dev/null
+++ b/tests/platform-ci/provider/cloud.json.template
@@ -0,0 +1,15 @@
+{
+ "platform_ci": {
+ "api": "aws",
+ "vendor": "aws",
+ "auth": {
+ "region": "us-west-2",
+ "aws_access_key_id": "",
+ "aws_secret_access_key": ""
+ },
+ "default_image": "ami-2a34e94a",
+ "default_options": {
+ "InstanceType": "t2.small"
+ }
+ }
+}
diff --git a/tests/platform-ci/provider/common.json b/tests/platform-ci/provider/common.json
new file mode 100644
index 00000000..a13f8f75
--- /dev/null
+++ b/tests/platform-ci/provider/common.json
@@ -0,0 +1,12 @@
+{
+ "sources": {
+ "platform": {
+ "apt": {
+ "basic": "http://deb.leap.se/experimental-0.9"
+ }
+ },
+ "nickserver": {
+ "revision": "develop"
+ }
+ }
+}
diff --git a/tests/platform-ci/provider/files/ca/ca.crt b/tests/platform-ci/provider/files/ca/ca.crt
new file mode 100644
index 00000000..01df56a7
--- /dev/null
+++ b/tests/platform-ci/provider/files/ca/ca.crt
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFbzCCA1egAwIBAgIBATANBgkqhkiG9w0BAQ0FADBKMRAwDgYDVQQKDAdFeGFt
+cGxlMRwwGgYDVQQLDBNodHRwczovL2V4YW1wbGUub3JnMRgwFgYDVQQDDA9FeGFt
+cGxlIFJvb3QgQ0EwHhcNMTYwNjExMDAwMDAwWhcNMjYwNjExMDAwMDAwWjBKMRAw
+DgYDVQQKDAdFeGFtcGxlMRwwGgYDVQQLDBNodHRwczovL2V4YW1wbGUub3JnMRgw
+FgYDVQQDDA9FeGFtcGxlIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQCyW2rTcWimY288/Ddu7OPvJxShS1RInQqfq8hYy6hEK2QYn656dRDf
+pJXgSYWMvWzSXWJiQkyA8L2+DDilFtccqToqnKE7IwYHlxaeh8OSyZcHl4YCpWJi
+1rc7pysN/l/0pjsp1aKKyHEObnkGMev07uGmI8aOE4Yvd2K5LjBjlov5mNnbYEHW
+j+hWctV6OcphnxVboqtTy+0Ewv5D56snLjUtedyB7Er4ryRjIrWOyd+ZSyi03zov
+oY1xPXS5wSxCc6y6wOKt7/noIg9xxWi7XgSd3OVtPYRU3Io62lMBSzNG6fmos3Mb
+E4ui5ma7IFCJlMEFHirVSBiHn2jwsDtSsrTl0JHJS5ud8Eve5vV0r/n07QsDMhj5
+ol+YDq4+VvOekCAH75GFYkMIzpgcVzC2a1Rq7JTkcnINAF/m47yBLPomItklHv0z
++I23Q+jjTaM2A+40T2K+YRLjFyZwrlCAScjMwPFspnGxfa02miYENhPV1TdTP/ap
+QE7TSl6oFiNrTh7INHGvKgrZYRW598dgAWNKG4zWY8Vj/bIXR1lC8Lp6enQtsIsU
+WiF+zl+xq6bRHg7W419qQowiD349gPXIJGlXEzLqjgLpmpFywrpzxBv6sfZgYT9d
+OPATT+GSiOFYJh9K4/JIxOFBJhzDD6PribjhzydPMTojSJ2Xu7nsOwIDAQABo2Aw
+XjAdBgNVHQ4EFgQUlhC2wfrVFzGrtuzcA0mkO+yn9bgwDgYDVR0PAQH/BAQDAgIE
+MAwGA1UdEwQFMAMBAf8wHwYDVR0jBBgwFoAUlhC2wfrVFzGrtuzcA0mkO+yn9bgw
+DQYJKoZIhvcNAQENBQADggIBAAKdSviiZY8tINlDSVrib0CyDbXymO5uTPRqsf/u
+MC7/DYXlNFy0GHpX4Ls6GcJN5DdZAG0TaoWo5RkNerxqv78sGJsmPqWt55cpBPVe
+NLpFmxcOmLClSDLBhSaq5ggbxULScee7MS1gPHqz1BHXmi7ZJIip4VeVA2e1E52F
+J7E4Y36AJOdZYLgz50YOX/NZwSYBTMy7RI1MiqG/eJf1BjkwtSyO7FTjPXsdKi8x
+HhtRr5udm7Nprq1eJUUDD0+z4kAeTe/LJeuhxc4QKzpVZkE1peW6Wlklp0cdLJud
+7gUsY1GFnNhZDDQ3SW2ZJ/p2OdH35rX96cj+6VClqSQMbH4rL63tICLmAsEzPKwJ
+57bGVUM822n4mh0vn79dam40vMw7wkTKqIKVyLhk30N5/73XczpoLhvVdKDtA1Aj
+C6LseWq4CZsaRSCgk2VsEEYyl7M+BIREuhYOllsILneOTiCOCnU4EdnBQZIHdz3S
+xhduafYXLa7RHkFMfOjtmhogXXpGyaQuS8IsivIowOxKoIZo47IhYRRAghrVN2HK
+ZXrgftIHNfHsFLfe6iiQBgaRn/1w7xOIPVDBqlZKKAMQE7cvum2o6dJo03Sc4dIe
+rvIU1WGNRLM3/AsbZ/7gqwD3INiNUPeuVaiRqvLvXnKfHlR/4s2wZrnKqUgYF1Go
+arXF
+-----END CERTIFICATE-----
diff --git a/tests/platform-ci/provider/files/ca/ca.key b/tests/platform-ci/provider/files/ca/ca.key
new file mode 100644
index 00000000..c022b19a
--- /dev/null
+++ b/tests/platform-ci/provider/files/ca/ca.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/tests/platform-ci/provider/files/ca/client_ca.crt b/tests/platform-ci/provider/files/ca/client_ca.crt
new file mode 100644
index 00000000..c1214476
--- /dev/null
+++ b/tests/platform-ci/provider/files/ca/client_ca.crt
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFpzCCA4+gAwIBAgIBATANBgkqhkiG9w0BAQ0FADBmMRAwDgYDVQQKDAdFeGFt
+cGxlMRwwGgYDVQQLDBNodHRwczovL2V4YW1wbGUub3JnMTQwMgYDVQQDDCtFeGFt
+cGxlIFJvb3QgQ0EgKGNsaWVudCBjZXJ0aWZpY2F0ZXMgb25seSEpMB4XDTE2MDYx
+MTAwMDAwMFoXDTI2MDYxMTAwMDAwMFowZjEQMA4GA1UECgwHRXhhbXBsZTEcMBoG
+A1UECwwTaHR0cHM6Ly9leGFtcGxlLm9yZzE0MDIGA1UEAwwrRXhhbXBsZSBSb290
+IENBIChjbGllbnQgY2VydGlmaWNhdGVzIG9ubHkhKTCCAiIwDQYJKoZIhvcNAQEB
+BQADggIPADCCAgoCggIBAL+WKlA0V+1aMjDKCwk3HaVJz7tk+knutrr3RtjwUshp
+wPty3+t1WrTEtfLLUv6MNOFStTPv5/JKAtDVEcm5xVJ9DNAw8XBnouUnm77WrMa5
+t3Oa8iA6kL1GsdfCoAyKNSX7ArDlfumA/fakjIvPoRYmjplzsodlHISu5FqpHc+G
+NdX89K6yzcjgMhRhCvHLrL9d+pe+efBDLab5I8pA0CGpaLfzPQiUNc2E1jn+ApSJ
+Bkq+gVBscKcomluDa6rtP2UeGGvG1DkHtbpx1WA/a/T9Tt7ACFd1uIQ2Ob57MAHx
+WgP6jD+Kj+/r9sA0iXGN/JnWXxpsVfYjbEFhRhL80Z3Rpj3Hf6xgUJbx0LUE34xA
+CTAK/n9G5q+7oog6oSNx80AU6ihWoucARtrQpwrV8rMvEO+QAtT2DjQMShYupk+n
+vHV1BTsigsjfywB2eGODKC5u6Ev91Zc8JEFmVvR+/tEP/XNzUTejGVi1fuABLILq
+Id0rL37j/NZ9OyExGSJDIRXSH45gHMkjNlQlqXYJ4JiZZbs/8UHEv4TnwFceBBhM
+lk8NwQE13B8F+/mcpaLaQ3X9AJzYBIh0CWkAaSKXmpIMSrOFlljihIIsA/p2OmOc
+g1sumCK3IU8AXoUbzDM1EqL5/wE9jD+ns8Bsy4JR1FFZy1FOmQfacIJdbd46jkvD
+AgMBAAGjYDBeMB0GA1UdDgQWBBSrXJyoXQRw+uwU274hxHyKeX6kgDAOBgNVHQ8B
+Af8EBAMCAgQwDAYDVR0TBAUwAwEB/zAfBgNVHSMEGDAWgBSrXJyoXQRw+uwU274h
+xHyKeX6kgDANBgkqhkiG9w0BAQ0FAAOCAgEATF/s9DHNj3h8O4IN0eUC6YiXnpGv
+z3z4KPD5RYy9+O3uf+f6SxFOZZU5NU9GHE9VRenmerHSsux9FxEAGsCjpiCFQGXq
+PKPBINyuR6TIDo+E/bl97Te0wL7aATiy5HFfQd41IoYPjuDpgb1Fc25w6iv9VeFG
+WrZ1JLJp4wguZ6RKSSLhsBF3m+wGe6Mg89b1sdkCvFr6EVqlZZbOSPUpUjVYp46p
+v3WP+Grtx9rBlJxqPpA7RPIyqnyiE4ovZcznz+9glgB3n1ufO+dSCVjkAEPxvmLu
+Qj7Jc+rpNOE5xZCFBaqtCBaBm2Uht3OyHypK9UYLZ7QOAfrGnBdgLERkAzPG6Zok
+yXuo0YTjHpdy5BPUD8VOahsj/2tzkMXkYmRCW9/dRwhfvi3QQHyQpsRZizmWXgTV
+JWa6UYfF1B/rDt3sn+AjDCxhHeBe02YTw0MWG3frv3Gn2/JUESSQjK4Xhjg/DPxb
+pLfhSLuq7WWqtkJsI0sZVj+GAdkbTgGjMLvj6+ckXpqE9V8eDgvE7KqYlSS2i6Sm
+e3SofOC2h10D3pWtX1KSPUp20ClRE/MUS/YW9szKZhqA/ZNMX2eViF05hgqywYwg
+GvapgFpn0mbBj9sOrBuAZX/r+U3MBv/Pj8ErdX/m20Bg/eIPBcHftS465Y9fjGu+
+apsldYNSrCZ30p4=
+-----END CERTIFICATE-----
diff --git a/tests/platform-ci/provider/files/ca/client_ca.key b/tests/platform-ci/provider/files/ca/client_ca.key
new file mode 100644
index 00000000..160cad43
--- /dev/null
+++ b/tests/platform-ci/provider/files/ca/client_ca.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/tests/platform-ci/provider/files/ca/dh.pem b/tests/platform-ci/provider/files/ca/dh.pem
new file mode 100644
index 00000000..3c86bf39
--- /dev/null
+++ b/tests/platform-ci/provider/files/ca/dh.pem
@@ -0,0 +1,19 @@
+-----BEGIN DH PARAMETERS-----
+MIIDDQKCAYEAhh7GNJktPFPgzCHPrWKCSmbhZtO1ypcVJCEZ0VkvpgUpUxAZnRl4
+TPZaQVbYx1gGpvJ6pV341zoeKlFjxK5h8iG5vWYplMk9FzxbI4O7oT2APZcVfR2U
+4lrmQMK7EFDrfRw+CYCuwv0/NxEoMFINRnWtyksLPw3ZtFDdnUAz4Dnu15yAFBW9
+vmOqM72Npx3BnkREOZtB5Fj5FkH9DOVSibuD6zMlUCcVXaX/bON4yrhDGnSctj0y
+mwCpkLK5GkpV24i7pW7LAY+MKXOtDObZHenwdJCBdcAMbNYO5BXuFFxlgJlxRT3T
+j6IH25j9/dRzaO73rh222Qp/EA3YGvhuEAMps/o30flbjZdsiAzn8ajg8NO1qf4+
+aDJDN4xTRVFkTOgTuORqamiLmV7Q3yU4wDFo9jf8H/fD8uIXESy1NOTuKbXjoITL
+D3RVivSlTXHWJ3rSOm13uFY0OVG+gx36Oz5O/hzbtGrebrTadKALS0SkH1cjh6pf
+sCpgVW7BorY3AoIBgAZWGr/JxYe8PtTwPm40EH2r1FUvDlhEEaxF4Ky0VRxh4sdq
+/Vdcvn5ww3KgItkwSSAM0TchtosXtjILXkuRSwJglu15OHOuJHZKsaaD7NeT+AoS
+HOfaiEUJRht9+/lNLbLwxpq8FSCOabWSeqj40rq1P7wQWUh2gyAh9GWc+KO9Lg1w
+Feo0re6IgmPaVhpWS/a2/IguHQwbMdly6EgWD8CqGIK9T4agWqYr4FIUzaEO3SOi
+fe+MPV6U5P1STcs9+UQG9LjzqHHDjMHIm4I3KNXKyM2myl8ncTrmD6uRRiRh6bhn
+wZHMXwk+JsJgbwz8d4T/xDoGNWvonGvnQWgPTaVry1N1TLjgWd+k8UCio0DmxgUJ
+qOz1x7LIGqLGiSOF1xUxA4M50we/JVw8731PLFxZNiSRvKHW/Dh3YsZ2jSoPT+1T
+1l+azCglr1Xz560GEjswedZgsAb1tBm7AFtpJIfujMLRZhhoUZl2rDX1A2h69/HN
+kn86NydyUXjVGYttQgICAQA=
+-----END DH PARAMETERS-----
diff --git a/tests/platform-ci/provider/files/cert/commercial_ca.crt b/tests/platform-ci/provider/files/cert/commercial_ca.crt
new file mode 100644
index 00000000..01df56a7
--- /dev/null
+++ b/tests/platform-ci/provider/files/cert/commercial_ca.crt
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFbzCCA1egAwIBAgIBATANBgkqhkiG9w0BAQ0FADBKMRAwDgYDVQQKDAdFeGFt
+cGxlMRwwGgYDVQQLDBNodHRwczovL2V4YW1wbGUub3JnMRgwFgYDVQQDDA9FeGFt
+cGxlIFJvb3QgQ0EwHhcNMTYwNjExMDAwMDAwWhcNMjYwNjExMDAwMDAwWjBKMRAw
+DgYDVQQKDAdFeGFtcGxlMRwwGgYDVQQLDBNodHRwczovL2V4YW1wbGUub3JnMRgw
+FgYDVQQDDA9FeGFtcGxlIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQCyW2rTcWimY288/Ddu7OPvJxShS1RInQqfq8hYy6hEK2QYn656dRDf
+pJXgSYWMvWzSXWJiQkyA8L2+DDilFtccqToqnKE7IwYHlxaeh8OSyZcHl4YCpWJi
+1rc7pysN/l/0pjsp1aKKyHEObnkGMev07uGmI8aOE4Yvd2K5LjBjlov5mNnbYEHW
+j+hWctV6OcphnxVboqtTy+0Ewv5D56snLjUtedyB7Er4ryRjIrWOyd+ZSyi03zov
+oY1xPXS5wSxCc6y6wOKt7/noIg9xxWi7XgSd3OVtPYRU3Io62lMBSzNG6fmos3Mb
+E4ui5ma7IFCJlMEFHirVSBiHn2jwsDtSsrTl0JHJS5ud8Eve5vV0r/n07QsDMhj5
+ol+YDq4+VvOekCAH75GFYkMIzpgcVzC2a1Rq7JTkcnINAF/m47yBLPomItklHv0z
++I23Q+jjTaM2A+40T2K+YRLjFyZwrlCAScjMwPFspnGxfa02miYENhPV1TdTP/ap
+QE7TSl6oFiNrTh7INHGvKgrZYRW598dgAWNKG4zWY8Vj/bIXR1lC8Lp6enQtsIsU
+WiF+zl+xq6bRHg7W419qQowiD349gPXIJGlXEzLqjgLpmpFywrpzxBv6sfZgYT9d
+OPATT+GSiOFYJh9K4/JIxOFBJhzDD6PribjhzydPMTojSJ2Xu7nsOwIDAQABo2Aw
+XjAdBgNVHQ4EFgQUlhC2wfrVFzGrtuzcA0mkO+yn9bgwDgYDVR0PAQH/BAQDAgIE
+MAwGA1UdEwQFMAMBAf8wHwYDVR0jBBgwFoAUlhC2wfrVFzGrtuzcA0mkO+yn9bgw
+DQYJKoZIhvcNAQENBQADggIBAAKdSviiZY8tINlDSVrib0CyDbXymO5uTPRqsf/u
+MC7/DYXlNFy0GHpX4Ls6GcJN5DdZAG0TaoWo5RkNerxqv78sGJsmPqWt55cpBPVe
+NLpFmxcOmLClSDLBhSaq5ggbxULScee7MS1gPHqz1BHXmi7ZJIip4VeVA2e1E52F
+J7E4Y36AJOdZYLgz50YOX/NZwSYBTMy7RI1MiqG/eJf1BjkwtSyO7FTjPXsdKi8x
+HhtRr5udm7Nprq1eJUUDD0+z4kAeTe/LJeuhxc4QKzpVZkE1peW6Wlklp0cdLJud
+7gUsY1GFnNhZDDQ3SW2ZJ/p2OdH35rX96cj+6VClqSQMbH4rL63tICLmAsEzPKwJ
+57bGVUM822n4mh0vn79dam40vMw7wkTKqIKVyLhk30N5/73XczpoLhvVdKDtA1Aj
+C6LseWq4CZsaRSCgk2VsEEYyl7M+BIREuhYOllsILneOTiCOCnU4EdnBQZIHdz3S
+xhduafYXLa7RHkFMfOjtmhogXXpGyaQuS8IsivIowOxKoIZo47IhYRRAghrVN2HK
+ZXrgftIHNfHsFLfe6iiQBgaRn/1w7xOIPVDBqlZKKAMQE7cvum2o6dJo03Sc4dIe
+rvIU1WGNRLM3/AsbZ/7gqwD3INiNUPeuVaiRqvLvXnKfHlR/4s2wZrnKqUgYF1Go
+arXF
+-----END CERTIFICATE-----
diff --git a/tests/platform-ci/provider/files/cert/example.org.crt b/tests/platform-ci/provider/files/cert/example.org.crt
new file mode 100644
index 00000000..7de2982d
--- /dev/null
+++ b/tests/platform-ci/provider/files/cert/example.org.crt
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFbDCCA1SgAwIBAgIRAJW2X9xbiBvmbN1kMlRVKtQwDQYJKoZIhvcNAQELBQAw
+SjEQMA4GA1UECgwHRXhhbXBsZTEcMBoGA1UECwwTaHR0cHM6Ly9leGFtcGxlLm9y
+ZzEYMBYGA1UEAwwPRXhhbXBsZSBSb290IENBMB4XDTE2MDYxMTAwMDAwMFoXDTE3
+MDYxMTAwMDAwMFowKDEQMA4GA1UECgwHRXhhbXBsZTEUMBIGA1UEAwwLZXhhbXBs
+ZS5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDFuKIL//hf5cjU
+m18q5fSUyvwtmWREJPaVp+CiWiGJHmxFAiWMGuAFRRChhZ4SYmnEscNda0f6ntPz
+rO+XjhQeA05bIYD9JcFT25Jg4kSX4pQ0+pK2vuHqk4ascZgOOaq4fN8SXD6ZiL3m
+CONDRzbnZVR2LqsdCbEqIuHlo7VK7MO8/9A+rF7wKLVatBtk25uSWMQPt0Q41gw6
+YTV447SltFH3fgUZnNR6p7Oxpsi3qEWlt2vZMIa5xdq4ge2dx1GgC8oSBx1XT/Yd
+qu//GECAH5XsZsAaPXDuor1iTbWELzHyGrQ7V80e67lE2lxoaHxRCOE/NDUU6UXm
+CqXwhdBHarHehOCGSDXvHEwAH5zpV77XOm2bIoZmCjM1fRk5p2S3GmXteCdvCxBP
++2wECnRXuwN2aICrBk7sZ9FieRsYao8GZN/A7ZY24pf7CMEBsgjYktTjAwUb21m6
+vmmzt93dEVJgkd8LASFmoXn+YAIGF0/fD5ZutlsAsBfodoCH9JKBi25nVVTEQW8g
+TzUegTC3PUqnathWv4gZIYDG1ZUDxjk30beNmXV2XudASmP7NG4uSlQwGAEWn+cc
+dzOnRxR0BQpkMMNEV/HmJVuSV5Ak4DkruSXGjLpzi30BjJ8obx85YAusIrhWRUrR
+2oz6gqDUnwq3Nkr3Nk45iOEDC0cZnwIDAQABo28wbTAdBgNVHQ4EFgQUS7rm3WfC
+psxoh4i7q0YbTbMZWuIwCwYDVR0PBAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMB
+MAkGA1UdEwQCMAAwHwYDVR0jBBgwFoAUlhC2wfrVFzGrtuzcA0mkO+yn9bgwDQYJ
+KoZIhvcNAQELBQADggIBAKxeVSMEpUOdBO1zmwd5NtugOlYV3/Gu9GqmUQdlB4FF
+Wt6sKJmYYByNquKT79oJLb9dgUPw8qQiHCB+MAsjB4PpHvMRlpgrcDGsI8+esnfG
+dJny+82aRIFZ2KnNbH8FchcCh4bviaY+DE9kyJNHILk0ujICXabR0G6ArVISTbyB
+C+6BdFyKTT5zj9mtkiTgvZchlKCmOmvh/HeCONu6MGYbqcqp41RA3g1eEjFoROKO
+wmf65VvfOBeb9VydOTICh/bJWRSmAMJqWxbOiV8+Ldufi0vXMcOhEfsyo316xxRq
+1GMb5xVihtCxj/+qBKNoun4k9LTmUvComuPakbtEPT2QbxiTvqCbXsWHPoRwCKEj
+RcFPsxWAnUslzqSl1b0oLaE1zNjBmB/Zd82i2MC4PncLC2hLHtAU1imRZKP6rnHx
+cb1NyFLS0FmIPqZUz9qcY2Tj3GbjqYqRi/sXNKrR2axAUx+jGI/Ie7Zsqa4VZA0A
+ZsiF0BGN3RTCYHuoJbXfEVFQ3o97JGNC3t07u9XhVuC0fjCiQu5PBbMRHSSvtBdN
++LSrhR5j4aiCmppgQSeTtoKSIS3EiOzDtawdewxhffK+co0pGnO3nox+iINvSIQ5
+IevAREmZ2ytjFDU/kVFFlINesFsLRouO37DUf2Kjxaa0RgkCBHpOnTAAD7bXiSaJ
+-----END CERTIFICATE-----
diff --git a/tests/platform-ci/provider/files/cert/example.org.csr b/tests/platform-ci/provider/files/cert/example.org.csr
new file mode 100644
index 00000000..95e8b65d
--- /dev/null
+++ b/tests/platform-ci/provider/files/cert/example.org.csr
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIEqzCCApMCAQAwKDEQMA4GA1UECgwHRXhhbXBsZTEUMBIGA1UEAwwLZXhhbXBs
+ZS5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDFuKIL//hf5cjU
+m18q5fSUyvwtmWREJPaVp+CiWiGJHmxFAiWMGuAFRRChhZ4SYmnEscNda0f6ntPz
+rO+XjhQeA05bIYD9JcFT25Jg4kSX4pQ0+pK2vuHqk4ascZgOOaq4fN8SXD6ZiL3m
+CONDRzbnZVR2LqsdCbEqIuHlo7VK7MO8/9A+rF7wKLVatBtk25uSWMQPt0Q41gw6
+YTV447SltFH3fgUZnNR6p7Oxpsi3qEWlt2vZMIa5xdq4ge2dx1GgC8oSBx1XT/Yd
+qu//GECAH5XsZsAaPXDuor1iTbWELzHyGrQ7V80e67lE2lxoaHxRCOE/NDUU6UXm
+CqXwhdBHarHehOCGSDXvHEwAH5zpV77XOm2bIoZmCjM1fRk5p2S3GmXteCdvCxBP
++2wECnRXuwN2aICrBk7sZ9FieRsYao8GZN/A7ZY24pf7CMEBsgjYktTjAwUb21m6
+vmmzt93dEVJgkd8LASFmoXn+YAIGF0/fD5ZutlsAsBfodoCH9JKBi25nVVTEQW8g
+TzUegTC3PUqnathWv4gZIYDG1ZUDxjk30beNmXV2XudASmP7NG4uSlQwGAEWn+cc
+dzOnRxR0BQpkMMNEV/HmJVuSV5Ak4DkruSXGjLpzi30BjJ8obx85YAusIrhWRUrR
+2oz6gqDUnwq3Nkr3Nk45iOEDC0cZnwIDAQABoD4wPAYJKoZIhvcNAQkOMS8wLTAJ
+BgNVHRMEAjAAMAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkq
+hkiG9w0BAQsFAAOCAgEAG0IpXLHZpgXtBZHEnGBghrucWnAuhRf0sXauboBVWnwA
+5noESIIX/hNq9DdaBba684u1Qga+lZcFsO1Zh/K1Guu74FTNxV2jCLKcX1T+Ymx4
+uRJ1jcdCc+YB/f+ce+pAhFJei/6sKP//MtYIBHlbe8aGQx1yVPJ5oSb4yS9Hloe4
+DuM0bp6ZXhXFv4YxxxDbaTMs9D46AKnqXV0rLe8WwHH1Mbdxl0bi7roZ3/1NPYsg
+diUMWQlnrR1d1xxUG7x+PJRpPcN3GmZQ0WyZoNrIQA7OLEg6nM8T4sQX5OZFdQrQ
+KQJyX8+Cc8j/UtPrPIPgch6iYX32e+1wTAP82npw1KMELxRsxjX6ERl65apkADFa
+w6LrCFtUQApWY/vZPz88udzSxVytJL4ZrHJxuZEG1WFE3kPY2Ak5LYw/IVxCDFsL
+GVfhb92zkn5iUkULXbwjcTytK3IqXZHl05PW+etGtqbkdh99m8eH1HxolKEgtehm
+l7FMD/JrC0GJWhI4Dl0CpvhAsV61pa8f1KmfGFTt+zpS4epSIItWTuSd4tzaXwNq
+3K1zJaKHs16VWBFuhH5kle4QGRIuDRPHchBQQg0wgy/sfHuzqbcVNotGZ7qzvnRL
+x5eXmWm1HaVKl1NpxbntMY4o9u0WgyzmU0VVsv+oWJj6J88T97rqTNg1Q1Uj8ic=
+-----END CERTIFICATE REQUEST-----
diff --git a/tests/platform-ci/provider/files/cert/example.org.key b/tests/platform-ci/provider/files/cert/example.org.key
new file mode 100644
index 00000000..7ca1c512
--- /dev/null
+++ b/tests/platform-ci/provider/files/cert/example.org.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEAxbiiC//4X+XI1JtfKuX0lMr8LZlkRCT2lafgolohiR5sRQIl
+jBrgBUUQoYWeEmJpxLHDXWtH+p7T86zvl44UHgNOWyGA/SXBU9uSYOJEl+KUNPqS
+tr7h6pOGrHGYDjmquHzfElw+mYi95gjjQ0c252VUdi6rHQmxKiLh5aO1SuzDvP/Q
+Pqxe8Ci1WrQbZNubkljED7dEONYMOmE1eOO0pbRR934FGZzUeqezsabIt6hFpbdr
+2TCGucXauIHtncdRoAvKEgcdV0/2Harv/xhAgB+V7GbAGj1w7qK9Yk21hC8x8hq0
+O1fNHuu5RNpcaGh8UQjhPzQ1FOlF5gql8IXQR2qx3oTghkg17xxMAB+c6Ve+1zpt
+myKGZgozNX0ZOadktxpl7XgnbwsQT/tsBAp0V7sDdmiAqwZO7GfRYnkbGGqPBmTf
+wO2WNuKX+wjBAbII2JLU4wMFG9tZur5ps7fd3RFSYJHfCwEhZqF5/mACBhdP3w+W
+brZbALAX6HaAh/SSgYtuZ1VUxEFvIE81HoEwtz1Kp2rYVr+IGSGAxtWVA8Y5N9G3
+jZl1dl7nQEpj+zRuLkpUMBgBFp/nHHczp0cUdAUKZDDDRFfx5iVbkleQJOA5K7kl
+xoy6c4t9AYyfKG8fOWALrCK4VkVK0dqM+oKg1J8KtzZK9zZOOYjhAwtHGZ8CAwEA
+AQKCAgAht6KquTP55o2g8/3+qshSt2rZu9bFaChEzSQZi5U8dNuxyPPuOIcLXwO/
+B7I1IGM5D7dpLupPatZqL4uMJMZ5d8bc85GzmcSmMEN+EhfwbssnXbO3RkXwYsgM
+kDKF+n+KhoDj+KcUN6VqnQlkZ7iNLVKB9ONpSEXWEazEJG6+IDIhAN7aUTq/abHD
+jgM959VX15tXssEHkDj1m64qt2oO9/kiY3MrMvtpD0Atg2unJiL6Z5UUrJnNBFiQ
+Llf/GAZrbJdBC8WNJi2qUYQr1E7rindeoQcRcnjXuRjisq3JpOK3jqY9mHN6Wmh1
+vWcUxvysNP90b8q9jipFWHuD0M37kq+BLn5Bub0ypiIkId0CUnAB9MBYcBJlYhai
+ZwI1fe0uGFD7XlJbHexTgnLreDAo3FR9CIUDo2HUWqmUNWadAl/rPNRe6+QDDvmP
+5v4HiFmSuCjZJOu9x2z/ly1JzM+iCUp+q6BxYYYW/5tDLYAw7sl1uaiLTzZuhrrM
+PlO6DNLAQhMn29jeszPHt7iXHdHAHAuYSeHpfeqnAV1qB+6x7UFVZjbDxXkt/Sn0
++LvCzJUQOwQNlnnzIwVdn8phS3r9TN2rI3dtlvPMWJqgBiheJ9qn2tHjjoPETt9I
+hfvw949Gi65D+AFSzowjNUFwDXzphOwETv5tpKCRROhdBRBdwQKCAQEA+L9RsVqT
+F+7HyGza+F53mgED5SQoS52vRA2OiAbCgiNjY7JH7bqIpuO4RlgqKo+GKoboCP6D
+1CmVGUm/Z8wYspzQs15O/jUO1bZ8KFREt8TquxFtikwyvIXQhUdJhZYUnhfMzV3O
+sH1blWhJnSX21rxJWlrkN0I8Zdkl6mjvFa97Kr9UA/pdZd0qgIw5Vi7MFLPC7j2Q
+YmTPhNsb0oZMJHGvwENUmuCQDhGiRhQV06R963mTMvxY7LWqUVf6dr7xg89Qt5Yo
+AdSHllOxHOMTAa+kZNF1N8UM9S2iJSn6ZeUEOXOJEuosghpE/QIuvo81Txm63G7e
+BjU3H7cFqDetfQKCAQEAy3xy2cQ/+GlSIbwXrzBr483Z0jXnvknlCJMh+NCTXObk
+idOhhnIuZu+JoAovv2AfKNPvYXotmb1xxmws5RSrlZDGiQQzEwvJPeLN2DnUGqzc
+ZPenu64Je6v9L35iRMF8vyx3xf27FC4zmR6nLuZbgfEfQdModqCbTpzh23Cl3mkM
+IZFYPhhfnh/pcwccuqfOn0Adt+1X3jvp3QzCh1jkEjhaRB5qjt58nlmxA2EKYv1w
+OzSTH9owqsCMmdrqzR7iKh59LrfOfggJbhHCyrORZ/S8h5lwqIk3+zLMrwGSvkXL
+tuKLXtkX/Xy98cbHwk5M/bf3hH6I5njlsssFsS8+SwKCAQEAxCzu2raaJ1fUDAd9
+sj+eh8ChN8gKV4hmv38Jl9Hs+QG70ta5z407VJNns2K47pP+te9rdBx2D48z3ZvB
+7rSSDduK5MtN9UIXDwk6Zfv/rgcJMLuP7nAl23SVfWc5Xrd8TypqBNUkuyBCaFS1
+KdDVGYmpOC9SqRn91D0rn/FeDXY15wK52eFMY5fHe1YbqhKCNRmIdKftBQyIdTjw
+elocFunqN/Fh+jt8oPvbRPV2OVITVPCu3JkT8KtdRYXjLF9uzgtkl0U/DCJ3RGGA
+301eogfJ2REwJumrTHnO1QyERHQXns+1nUs+CuV43ykngHYlDts1+b8eLzss3EBV
+n9M5aQKCAQEArqKmmtg/on0ZPNSFaxfecEq5lxwmQHyAsMQ9UqIG5qNOHi9fn9gc
+lMEdVxmG8vKWq16AQiMuQZSBsa4jNZNw0tLGYM8W2lCyLIea6+htbVtPZuPYs0zg
+3J+1ke4gfiukWRnbzTM+PEqOg+n3x1txy2pZzg9f2bdqsqQXflIGOIPlImXv2pLm
+dPmkS9Edyd+8h5XqK3DpiVPYGJsb1Dbove5ZIb8M6oJtZyVIssK0vFIP4O/1GFAU
+lmbcBCsKenH33ff+rXqYIDfbh/h8OaS0tQgoSSPZuPrS7aYiXku2Wc/izplMzWD5
+otZM2dQkmlDC6LjbF33VFh9J2xE8WF1YUwKCAQAeJYro7nBxM0eOmof1ty24UPfg
+jx72sH/FpgKIyvZ4yQoreNUc4TVsy5QMIVd0G966CRgvzaE0vcBHm//7YCXHtIa9
+ihqmYDo7SoaF7nZNjxJIxyQVPY0+Kntkwz0XAX0IbJ0nMx+3x6d5UhbQbxFVKe7X
+5WmOMb0ro9NLaCvh5IUxSHsG/a8hYRqoX3tZbPRvTJMZMTMxWslsscWINNu/80KS
+ggpD9Uu9hdVwT7yavl6JKC3ypRdBzmpKZfiLt5CTFex+XGIgKLHVqbHxXu487YsL
+AlexBvk1/RKMTHIgUl7uMmaJsUSD+ME4SWuU9cW115kwp+JBMXES4ZfWnRHZ
+-----END RSA PRIVATE KEY-----
diff --git a/tests/platform-ci/provider/files/mx/dkim.key b/tests/platform-ci/provider/files/mx/dkim.key
new file mode 100644
index 00000000..0dc069c6
--- /dev/null
+++ b/tests/platform-ci/provider/files/mx/dkim.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAyrO7MRuCSyM2Iz5fXT17q2rpP8U8m4zE98gShMAzzKFOUjk2
+WldOzVDrNVMc6nlIkBifNpk85SCdgc5GRCWMMHifbKTWjduK9pCTtvIOVPq9H0Ak
+mgqXEgoVurn9gIxfUk2zpr+TzE9r7/U+O8ffmtmZMKbWldvvqwfm3rLRBUpvi9EH
+KWjmqEp9I4x0mXzkwRzoyrN0xZB2pLJJVlg/edeI7omPaqRRbf/OWQb1CHK5g3Yo
+l1zlnBEG4X7BsIqqvaKfPT6Zp1AM/QP5qJESjvLyEf5eguWSeU/kyUCtSLZqigxf
+sYqcClN7gWiQ1j5d3vkpv6xEvLZOL9Jhg00FnQIDAQABAoIBACZMxX7m4ryNv6nz
+HBPDDT37am0ZOHVvqLvkutMIegEdLW5Nzx5Mxt/2fSrLNHh9SB+p91Naqu3kNr6T
+GiXALnfuIrllgAC3zc7+zFpR7DFUWy2vcfsFKzxGWYq5n9ONMmmbsuk745JEI3Ho
+lcS35GEe4loV/A++yc84JABKK0JjUpXeafXr21dNGNe0mv0j/0zM8jWzZ6QFneeG
+0MpZMP4rR+STe70n3Cgqoue0IDejt+N/jA6Js6cYV3zQgDX8sQVPR3saTaYMrjHH
+UV7qj+tvzgUxMp0XEzhAxNKsHpGSwyDt0X2RPFj1Jye9OSLHV+p4Mbjij+9p8ZUB
+robaTGECgYEA/SWNspWkyNSGE9YNErxiFL6WUGaXgJgoPNA6R/i5KoC/7jv8gn4X
+TeZ9a5b+JHt5fSy3Ph1Pje8T3ZLDl+7Oahr2/Xh+pmcQ3Tb3gEA30e2dmQgLGfcD
+wIa/wr+FpW/DofdjXtdVMyn9urnTiOYbPaFVY82z4OcQmFF/kMfSKAkCgYEAzPyf
+FToGs1BmEz416js7QsXNdr3sQMONsBRVw3H26qWbYDxaT6piHBwNfw2N2awr1WFc
+6XuQ93xymHHwNfb4vjTOI+vJLPwZo3P8KOZwDwjUpVL8OTDSXt87yJMbcmexLATS
+Asmnoe5h8rVXHc+BJ8UR0HdkJN0SVD/LKlySTfUCgYAVm5D+v1szcUCIjOrMwJu2
+nZYDAt7HsTUuC7AN2KMlh5vaX/Brywt+MMBf4KGMx6VVE+4INURHHzMY5KAhZdbk
+o6yVciWNWprL5xc1MUYSey/Kki8wZi9Bzb6shuCHgIS4XH905vh0x47K03XE569H
+kW/Sdwp1lgOKnNpAp22+0QKBgEZQIQFW9hVr7peLL1M5Hgq5btDcNL3CVkefsgto
+fBng1HseOJw7BYw+0yJRs+aGeEKpMwWjrQY3WdeQvaTFIm2cD1mi907G6sR2dHhT
+Ev0VOlu7K2kypfaE/CzAyRllGBDRVng+U5HoAxENwuQm2Vaa8pFfYqqCalcbysSt
+HEJBAoGAS/liytZxCp9v8RCNyAOo8JPHPw/EdPGxuk5lP7m4iNbB1O9DqvEEmR4l
+RzgXcAPgIAy5+TEwUQwarqbHe8fgmGziMP4xtntN2X+epreD1fWqfTHphO2njaDT
+SKMlO5hUVlQXc7/J6DRbFzWFlEngvqNx+PzM5VlEYc7mK6xRSjo=
+-----END RSA PRIVATE KEY-----
diff --git a/tests/platform-ci/provider/files/mx/dkim.pub b/tests/platform-ci/provider/files/mx/dkim.pub
new file mode 100644
index 00000000..bbd32086
--- /dev/null
+++ b/tests/platform-ci/provider/files/mx/dkim.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyrO7MRuCSyM2Iz5fXT17
+q2rpP8U8m4zE98gShMAzzKFOUjk2WldOzVDrNVMc6nlIkBifNpk85SCdgc5GRCWM
+MHifbKTWjduK9pCTtvIOVPq9H0AkmgqXEgoVurn9gIxfUk2zpr+TzE9r7/U+O8ff
+mtmZMKbWldvvqwfm3rLRBUpvi9EHKWjmqEp9I4x0mXzkwRzoyrN0xZB2pLJJVlg/
+edeI7omPaqRRbf/OWQb1CHK5g3Yol1zlnBEG4X7BsIqqvaKfPT6Zp1AM/QP5qJES
+jvLyEf5eguWSeU/kyUCtSLZqigxfsYqcClN7gWiQ1j5d3vkpv6xEvLZOL9Jhg00F
+nQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/tests/platform-ci/provider/files/ssh/monitor_ssh b/tests/platform-ci/provider/files/ssh/monitor_ssh
new file mode 100644
index 00000000..81ff75e4
--- /dev/null
+++ b/tests/platform-ci/provider/files/ssh/monitor_ssh
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEAxG2QA8pYOcU8ViBfg5QFTS7jboEr7G9UFpUisHyGyY5rJZ/9
+N04UK7GRtCYS+Rd/9nqWmoV4StdVH9rXFLHxPvVH3z/jHGDir2fRpkywaWGVMiU/
+F0QSv67YbooNOdMaTacapWEwmwjO0ApDrHlqdBZwGb/gh1wW7lUpBgzHN+ZzNU8Z
+lVh7icYgqv114NAjfzA+VGOwVpCW1q3pR8c08lSJgfnMUZ2gEmjJPizC6Za1RvIx
+kzEkRVnmtlN4i62J6aSwLKMDXlyfjailFzfZaPdjlA5ijMvGZXo/zUWCaQJS8k1c
+0vkj1arGHZ0J/t5so90qycD9j5Q8s8nMYZ5rbc1C7uPsx2ywqCVwd0St1STcNq4d
+FTZEc5edaKFGNjC0fzp3ZGzIvEyAdJMimXFcC10JP+TBgorQmNrH7iGNfsrr8b0P
+65Xr9P3sopEOSknsONoVBiH/9MoDd0CuVY9A0ZmmExB8qf2uc9ll2/SLcdX2jon9
+mX5zBI2ENcGnezHgGv4jp3PKaBokmOaOrQWND70/bgXooDmTU7+O63SemPoJhdwS
+CiI4QU2Vi5aEEOz966q+hi2xTXd96G9/qoOjdlBBZThwSsF6FDGdGjdUe+qkfGQb
+aybWHXbdHJamAWnqAtfwHDbSkShKwfJK+BqukkHGoe+l0zXrfUP+DZ5rcxsCAwEA
+AQKCAgEAk1rNysok3VHFLacjgAWu5HPkUaW9WaU6o6ZFW7hPNS0N3C/lOXPtVcnj
+0A0v9oVWjYTxLgIqd5qKVVdKOlAy9lPzEttOeJ+F7qgncmXdgXCfB/tBFScQGZQE
+8QfHXDWtacuOBbqfR+6XlyHcGqsK3QNoHSkAOwsueKSSHePAH4NVsgwg2RSDuJtV
+LnDt2TTLLEL4vz35rzbQsUPN2PbsFU6tyT+nsyJYTvck4OubXLieTRarcgxPdWc3
+2FdN+xq4dvoA37t6b3N0jkSRdJWFF2Ve4lbYP18u+jl3W3pllnkT2ImItQwJgeSW
+suh38ybQwSzNSITqsqc10nn0RNcfJvLK5CscX3xL8RYMcMAk9uxIAa5pACKnVwVj
+a92pP0838E+3AP5yaAMNZg5xeC5jlZCOS82SM7xKBQacmeLHh7DmjZPqngzSHyDu
+UyIlRn4dYB7G51QuQ1ZOZui/uODpUDcmh0EvAN2P/FDfhE4yvdJ5jIznNci7qEbG
+GZ7S2RcMbexl7RSo6hvhXp8D4vuAgWMPpVXaIBf/G9BX+YBwpo90ohB+LRmnOdWA
+FJfm7tis4ANx6XT+aWwvFEc6YTxV2ECq9yKcm8Ws0dhifDE2eKbWD9sL41p2Ghaa
+NWGUJ55wgsidl6r9roA6spROMvaM0bRZFLE5OcIhuE9D7wnHD0ECggEBAO8t7v31
+23y8BSY4WuIN5JzNkcrY+d+P/WNM4KjATUw08Yzg29u1Ebh2JJLoSAHEyV6ngNJ3
+SO9uOhrH6mfBW+CC5RT1rE5g3G9bz8ZI1scMDJcXYfIHqhOynP3RbiaXR39fja+l
+u7lW76mVM3qqET5oj4LBxU7eUyXzQqV7UoQyHBKNW0TALL/bRVdTabUyGprVbo70
+Ww75j2JqD2hK/803Ebi+VkkN1NcGiLdaWm9qvgTYiRENsSb4UjZX2EalKZERXcHy
+e7VCKdOVWwbWpDdTG1mg/bI+EdQvHXXuD7yDIKs3z3d2sYeqdMhQ7rJE3Ra/P/0p
+Kim+GTnDlVOwfXMCggEBANI99A6zp1iu2tkTIExEYin189BxtRg5EQRVm1CJl15O
+RrfReUVuhvRSagQlXz7qnNiETG73F7ouGu4QTLYP0Lmjxa6UP7Lu0mQ23al2/OXE
+1agzSLGTZv50sRE8f0Oo7fi/n++QVUfQ1MRM7yMtZnrl9X85+2KKQLYI4Gwb3yUU
+geJMaX8X5s6CwffRYe9BtYb3q2o1ySTbMIcdL2aQbAorBz33DNkp/SyLwEiuaogt
+jb93KCtoMiOyYs6gRMI3MxLGg6dLGbSB8QwBxCCV87UoUAS7IODqAHJwnacYKhEV
+0EcA0oDjT9cQomX6lQQFmxXE/2A96P5e8wVPyTi5SbkCggEALkIA/ecF6yrmCA1Q
+LnYnZ9guQUATm5RamlDtBlYi3QFEUk3O18A+TCG1UyBPhOANXhwhQxNE7OGxpSpT
+AHwaC+Lk8VfOWl5LY9Iq7ht6RobjDHm+PLQUxbh+umw91ILflhfh7D2uf9r7gR3V
+Ff08VoiccNqPEYDYLffNRPoD7INQgJoMM9DDFtwOniQIxr2I/bcXqdhCoDPN8me2
+0SHoNUVYTRWq1HgzWN7vpB56bSAE3iUO5VhzkajnJZF5x7f7wQ3Nx0vhdx3zvvMc
+5sauffC50mzbhBSTGCmAliVTr87gi5zAqEcxcJ6b9X4JnDrLU7Hra0gB2o7kjBJy
+l/wDVwKCAQEAjcICNouCEbTMkUNpKqONQNe6zthsj+mihLaoI7SyYH8NBdJzH5K3
+4jNTknoUb5rHqOIDm2p2EC4YMF7DKpsdVJ6NovoIvUB0kefArAwz10VR/ridkkZe
+UsIhxgpxkRBtbKTgVSqPpf20CKwLLj/lcoZtcpyI2Nd5bIQtthdQ7XKXZRu6olxe
+Xu4hlVQT4bv/hwKmDNY5SuWUIfZWyKQmhPCgUHKsshyyvX95ZkhcQnfctLXGWwZF
+kHYuUz4TPpTzlfxONtXXfjODcWIbeRFCouqMkbQPJjgBlyhB1LHhY2W+6rEuPoOG
+iO+JYJOGOJEDEbmjq6Py3tjsqa8zcVDV2QKCAQB2O6qmJCgn6os9ladkcnpTO5oD
+I+poz8PdwPcoB+KxW/Jj759mmBCeFh0HtZlct9JMexWD8cB2+x0412y9cZC2XduK
+tX0tci1WhZTR9XEo2BjzNJBRvRxSDOz1Fk0y2D9fhsVrPkS6qZ5/+kt/O6cgyFxb
+4m0+2V4qnJcF075PF4G/Raq8sKKuPOg8EHTnVRZgyL7vmrprRlPqpq8CYJUwPX53
+ddK3exo96qLvYCf7qKtQvDedLbllrqgOE2xrhuPPAmaXjto2dHb/7NCVBoccL5mN
+SPFLi0V6EvPUlYZZ/e0XQafMT20/moMWnuIH1igkXPkw/hwpBLGVVEsLv5hl
+-----END RSA PRIVATE KEY-----
diff --git a/tests/platform-ci/provider/files/ssh/monitor_ssh.pub b/tests/platform-ci/provider/files/ssh/monitor_ssh.pub
new file mode 100644
index 00000000..8be32927
--- /dev/null
+++ b/tests/platform-ci/provider/files/ssh/monitor_ssh.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDEbZADylg5xTxWIF+DlAVNLuNugSvsb1QWlSKwfIbJjmsln/03ThQrsZG0JhL5F3/2epaahXhK11Uf2tcUsfE+9UffP+McYOKvZ9GmTLBpYZUyJT8XRBK/rthuig050xpNpxqlYTCbCM7QCkOseWp0FnAZv+CHXBbuVSkGDMc35nM1TxmVWHuJxiCq/XXg0CN/MD5UY7BWkJbWrelHxzTyVImB+cxRnaASaMk+LMLplrVG8jGTMSRFWea2U3iLrYnppLAsowNeXJ+NqKUXN9lo92OUDmKMy8Zlej/NRYJpAlLyTVzS+SPVqsYdnQn+3myj3SrJwP2PlDyzycxhnmttzULu4+zHbLCoJXB3RK3VJNw2rh0VNkRzl51ooUY2MLR/OndkbMi8TIB0kyKZcVwLXQk/5MGCitCY2sfuIY1+yuvxvQ/rlev0/eyikQ5KSew42hUGIf/0ygN3QK5Vj0DRmaYTEHyp/a5z2WXb9Itx1faOif2ZfnMEjYQ1wad7MeAa/iOnc8poGiSY5o6tBY0PvT9uBeigOZNTv47rdJ6Y+gmF3BIKIjhBTZWLloQQ7P3rqr6GLbFNd33ob3+qg6N2UEFlOHBKwXoUMZ0aN1R76qR8ZBtrJtYddt0clqYBaeoC1/AcNtKRKErB8kr4Gq6SQcah76XTNet9Q/4NnmtzGw== monitor
diff --git a/tests/platform-ci/provider/nodes/catalogtest.json b/tests/platform-ci/provider/nodes/catalogtest.json
new file mode 100644
index 00000000..05703666
--- /dev/null
+++ b/tests/platform-ci/provider/nodes/catalogtest.json
@@ -0,0 +1,39 @@
+{
+ "ip_address": "1.1.1.1",
+ "openvpn": {
+ "gateway_address": "1.1.1.2"
+ },
+ "services": [
+ "couchdb",
+ "mx",
+ "soledad",
+ "webapp",
+ "monitor",
+ "openvpn",
+ "tor",
+ "obfsproxy",
+ "static"
+ ],
+ "tags": ["catalogtest","development"],
+ "static": {
+ "domains":{
+ "example.org": {
+ "tls_only": true,
+ "locations": {
+ "front": {
+ "path": "/",
+ "format": "amber",
+ "source": {
+ "type": "git",
+ "repo": "https://leap.se/git/bitmask_help",
+ "revision": "origin/master"
+ }
+ }
+ },
+ "cert": "= file('cert/example.org.crt')",
+ "key": "= file('cert/example.org.key')",
+ "ca_cert": "= file('cert/commercial_ca.crt')"
+ }
+ }
+ }
+}
diff --git a/tests/platform-ci/provider/provider.json b/tests/platform-ci/provider/provider.json
new file mode 100644
index 00000000..218ff529
--- /dev/null
+++ b/tests/platform-ci/provider/provider.json
@@ -0,0 +1,18 @@
+//
+// General service provider configuration.
+//
+{
+ "domain": "example.org",
+ "name": {
+ "en": "Example"
+ },
+ "description": {
+ "en": "You really should change this text"
+ },
+ "contacts": {
+ "default": "root@example.org"
+ },
+ "languages": ["en"],
+ "default_language": "en",
+ "enrollment_policy": "open"
+}
diff --git a/tests/platform-ci/provider/tags/catalogtest.json b/tests/platform-ci/provider/tags/catalogtest.json
new file mode 100644
index 00000000..0967ef42
--- /dev/null
+++ b/tests/platform-ci/provider/tags/catalogtest.json
@@ -0,0 +1 @@
+{}
diff --git a/tests/platform-ci/provider/users/gitlab-runner/gitlab-runner_ssh.pub b/tests/platform-ci/provider/users/gitlab-runner/gitlab-runner_ssh.pub
new file mode 100644
index 00000000..3e72b70f
--- /dev/null
+++ b/tests/platform-ci/provider/users/gitlab-runner/gitlab-runner_ssh.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEtniDgIYEm4WtGgiQsZKBpY8x3tbzDBIoMLbZT496juCu4c3f+F5KkMPLmYRPcAupF8tVf+j7Fns7z69PuTjdGfe/cA9CTw/4sNAu3iLpunGR0d2Wtctez5mwz13bKRu9fck3H9p2F9Z47vMKtRTJJ6iIgaUVWU/eFd/MSMJeUVd2ns4Wr7SkHCBB3PV+QL1xl4+AZsUtnGVQ5cE4MZZFia/g6SlrKQYFtLRVIIpDuuaDSvULg1BFMhSCBDNygts8dKTJsCEQYeGVvHZaDwtKTnMqEIwBP4TkIoP+YWnZTPrGywFEJOlZ8b+4HdgdUAFLcFCycWMM9nVcWX7P2lIN gitlab-runner_ssh
diff --git a/tests/platform-ci/setup.sh b/tests/platform-ci/setup.sh
new file mode 100755
index 00000000..39ef3130
--- /dev/null
+++ b/tests/platform-ci/setup.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+which bundle || /usr/bin/apt install bundle
+/usr/local/bin/bundle install --binstubs --path=/var/cache/gitlab-runner/ --with=test --jobs "$(nproc)"
diff --git a/tests/server-tests/README.md b/tests/server-tests/README.md
new file mode 100644
index 00000000..29db2e06
--- /dev/null
+++ b/tests/server-tests/README.md
@@ -0,0 +1,44 @@
+Tests for Server
+---------------------------------
+
+The tests in this directory are run against the servers of a live running
+provider.
+
+Usage
+---------------------------------
+
+To run the tests from a local workstation:
+
+ workstation$ cd <my provider directory>
+ workstation$ leap test
+
+To run the tests from the server itself:
+
+ workstation$ leap ssh servername
+ servername# run_tests
+
+Notes
+---------------------------------
+
+server-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.
+
+server-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
+---------------------------------
+
+server-tests/helpers/
+
+ Utility functions made available to all tests.
+
+server-tests/order.rb
+
+ Configuration file to specify which nodes should be tested in which order.
+
+
diff --git a/tests/helpers/bonafide_helper.rb b/tests/server-tests/helpers/bonafide_helper.rb
index 5b886228..5b886228 100644
--- a/tests/helpers/bonafide_helper.rb
+++ b/tests/server-tests/helpers/bonafide_helper.rb
diff --git a/tests/helpers/client_side_db.py b/tests/server-tests/helpers/client_side_db.py
index 2f8c220f..2f8c220f 100644
--- a/tests/helpers/client_side_db.py
+++ b/tests/server-tests/helpers/client_side_db.py
diff --git a/tests/helpers/couchdb_helper.rb b/tests/server-tests/helpers/couchdb_helper.rb
index b9085c1e..efb2c2bf 100644
--- a/tests/helpers/couchdb_helper.rb
+++ b/tests/server-tests/helpers/couchdb_helper.rb
@@ -124,6 +124,7 @@ class LeapTest
# returns true if the per-user db created by soledad-server exists.
#
def user_db_exists?(user_id, options=nil)
+ options = {:username => 'admin'}.merge(options || {})
db_name = "user-#{user_id}"
url = couchdb_url("/#{db_name}", options)
get(url) do |body, response, error|
diff --git a/tests/helpers/files_helper.rb b/tests/server-tests/helpers/files_helper.rb
index d6795889..d6795889 100644
--- a/tests/helpers/files_helper.rb
+++ b/tests/server-tests/helpers/files_helper.rb
diff --git a/tests/helpers/http_helper.rb b/tests/server-tests/helpers/http_helper.rb
index 0d0bb7d5..0d0bb7d5 100644
--- a/tests/helpers/http_helper.rb
+++ b/tests/server-tests/helpers/http_helper.rb
diff --git a/tests/helpers/network_helper.rb b/tests/server-tests/helpers/network_helper.rb
index 713d57aa..713d57aa 100644
--- a/tests/helpers/network_helper.rb
+++ b/tests/server-tests/helpers/network_helper.rb
diff --git a/tests/helpers/os_helper.rb b/tests/server-tests/helpers/os_helper.rb
index da9ac843..9923d5b1 100644
--- a/tests/helpers/os_helper.rb
+++ b/tests/server-tests/helpers/os_helper.rb
@@ -32,7 +32,7 @@ class LeapTest
# runs the specified command, failing on a non-zero exit status.
#
def assert_run(command)
- output = `#{command}`
+ output = `#{command} 2>&1`
if $?.exitstatus != 0
fail "Error running `#{command}`:\n#{output}"
end
diff --git a/tests/helpers/smtp_helper.rb b/tests/server-tests/helpers/smtp_helper.rb
index ea7fb9fa..ea7fb9fa 100644
--- a/tests/helpers/smtp_helper.rb
+++ b/tests/server-tests/helpers/smtp_helper.rb
diff --git a/tests/helpers/soledad_sync.py b/tests/server-tests/helpers/soledad_sync.py
index f4fc81ae..f4fc81ae 100755
--- a/tests/helpers/soledad_sync.py
+++ b/tests/server-tests/helpers/soledad_sync.py
diff --git a/tests/helpers/srp_helper.rb b/tests/server-tests/helpers/srp_helper.rb
index b30fa768..b30fa768 100644
--- a/tests/helpers/srp_helper.rb
+++ b/tests/server-tests/helpers/srp_helper.rb
diff --git a/tests/order.rb b/tests/server-tests/order.rb
index 14aad9be..14aad9be 100644
--- a/tests/order.rb
+++ b/tests/server-tests/order.rb
diff --git a/tests/white-box/couchdb.rb b/tests/server-tests/white-box/couchdb.rb
index 85dc6840..44a2769b 100644
--- a/tests/white-box/couchdb.rb
+++ b/tests/server-tests/white-box/couchdb.rb
@@ -27,23 +27,6 @@ class CouchDB < LeapTest
end
#
- # compare the configured nodes to the nodes that are actually listed in bigcouch
- #
- def test_02_Is_cluster_membership_ok?
- return unless multimaster?
- url = couchdb_backend_url("/nodes/_all_docs")
- neighbors = assert_property('couch.bigcouch.neighbors')
- neighbors << assert_property('domain.full')
- neighbors.sort!
- assert_get(url) do |body|
- response = JSON.parse(body)
- nodes_in_db = response['rows'].collect{|row| row['id'].sub(/^bigcouch@/, '')}.sort
- assert_equal neighbors, nodes_in_db, "The couchdb replication node list is wrong (/nodes/_all_docs)"
- end
- pass
- end
-
- #
# all configured nodes are in 'cluster_nodes'
# all nodes online and communicating are in 'all_nodes'
#
diff --git a/tests/white-box/dummy.rb b/tests/server-tests/white-box/dummy.rb
index a3e8ad68..a3e8ad68 100644
--- a/tests/white-box/dummy.rb
+++ b/tests/server-tests/white-box/dummy.rb
diff --git a/tests/white-box/mx.rb b/tests/server-tests/white-box/mx.rb
index 6c0982ce..0eeaacd0 100644
--- a/tests/white-box/mx.rb
+++ b/tests/server-tests/white-box/mx.rb
@@ -1,5 +1,6 @@
raise SkipTest unless service?(:mx)
+require 'date'
require 'json'
require 'net/smtp'
@@ -38,38 +39,15 @@ class Mx < LeapTest
# using the by_address view for that same document again.
#
def test_03_Can_query_identities_db?
- assert_get(couchdb_url("/identities", couch_url_options)) do |body|
+ ident = pick_random_identity
+ address = ident['address']
+ url_base = %(/identities/_design/Identity/_view/by_address)
+ params = %(?include_docs=true&reduce=false&startkey="#{address}"&endkey="#{address}")
+ assert_get(couchdb_url(url_base+params, couch_url_options)) do |body|
assert response = JSON.parse(body)
- doc_count = response['doc_count'].to_i
- if doc_count <= 1
- # the design document counts as one document.
- skip "There are no identity documents yet."
- else
- # try five times to get a valid doc
- for i in 1..5
- offset = rand(doc_count) # pick a random document
- count_url = couchdb_url("/identities/_all_docs?include_docs=true&limit=1&skip=#{offset}", couch_url_options)
- assert_get(count_url) do |body|
- assert response = JSON.parse(body)
- record = response['rows'].first
- if record['id'] =~ /_design/
- next
- else
- address = record['doc']['address']
- assert address, "Identity document #{record['id']} is missing an address field. #{record['doc'].inspect}"
- url_base = %(/identities/_design/Identity/_view/by_address)
- params = %(?include_docs=true&reduce=false&startkey="#{address}"&endkey="#{address}")
- assert_get(couchdb_url(url_base+params, couch_url_options)) do |body|
- assert response = JSON.parse(body)
- assert record = response['rows'].first
- assert_equal address, record['doc']['address']
- pass
- end
- break
- end
- end
- end
- end
+ assert record = response['rows'].first
+ assert_equal address, record['doc']['address']
+ pass
end
end
@@ -85,9 +63,59 @@ class Mx < LeapTest
if Dir.glob("/var/lib/clamav/main.{c[vl]d,inc}").size > 0 and Dir.glob("/var/lib/clamav/daily.{c[vl]d,inc}").size > 0
assert_running '^/usr/sbin/clamd'
assert_running '^/usr/sbin/clamav-milter'
+ pass
else
- skip "Downloading the clamav signature files (/var/lib/clamav/{daily,main}.{c[vl]d,inc}) is still in progress, so clamd is not running.\nDon't worry, mail delivery will work without clamav. The download should finish soon."
+ skip "Downloading the clamav signature files (/var/lib/clamav/{daily,main}.{c[vl]d,inc}) is still in progress, so clamd is not running."
end
+ end
+
+ #
+ # TODO: test to make sure postmap returned the right result
+ #
+ def test_05_Can_postfix_query_leapmx?
+ ident = pick_random_identity(10, :with_public_key => true)
+ address = ident["address"]
+
+ #
+ # virtual alias map:
+ #
+ # user@domain => 41c29a80a44f4775513c64ac9cab91b9@deliver.local
+ #
+ assert_run("postmap -v -q \"#{address}\" tcp:localhost:4242")
+
+ #
+ # recipient access map:
+ #
+ # user@domain => [OK|REJECT|TEMP_FAIL]
+ #
+ # This map is queried by the mail server before delivery to the mail spool
+ # directory, and should check if the address is able to receive messages.
+ # Examples of reasons for denying delivery would be that the user is out of
+ # quota, is user, or have no pgp public key in the server.
+ #
+ # NOTE: in the future, when we support quota, we need to make sure that
+ # we don't randomly pick a user for this test that happens to be over quota.
+ #
+ assert_run("postmap -v -q \"#{address}\" tcp:localhost:2244")
+
+ #
+ # certificate validity map:
+ #
+ # fa:2a:70:1f:d8:16:4e:1a:3b:15:c1:67:00:f0 => [200|500]
+ #
+ # Determines whether a particular SMTP client cert is authorized
+ # to relay mail, based on the fingerprint.
+ #
+ if ident["cert_fingerprints"]
+ not_expired = ident["cert_fingerprints"].select {|key, value|
+ Time.now.utc < DateTime.strptime("2016-01-03", "%F").to_time.utc
+ }
+ if not_expired.any?
+ fingerprint = not_expired.first
+ assert_run("postmap -v -q #{fingerprint} tcp:localhost:2424")
+ end
+ end
+
pass
end
@@ -98,20 +126,24 @@ class Mx < LeapTest
# quickly that something is wrong.
#
def test_05_Can_deliver_email?
- addr = [TEST_EMAIL_USER, property('domain.full_suffix')].join('@')
- bad_addr = [TEST_BAD_USER, property('domain.full_suffix')].join('@')
+ if pgrep('^/usr/sbin/clamd').empty? || pgrep('^/usr/sbin/clamav-milter').empty?
+ skip "Mail delivery is being deferred because clamav daemon is not running"
+ else
+ addr = [TEST_EMAIL_USER, property('domain.full_suffix')].join('@')
+ bad_addr = [TEST_BAD_USER, property('domain.full_suffix')].join('@')
- assert !identity_exists?(bad_addr), "the address #{bad_addr} must not exist."
- if !identity_exists?(addr)
- user = assert_create_user(TEST_EMAIL_USER, :monitor)
- upload_public_key(user.id, TEST_EMAIL_PUBLIC_KEY)
- end
- assert identity_exists?(addr), "The identity #{addr} should have been created, but it doesn't exist yet."
- assert_send_email(addr)
- assert_raises(Net::SMTPError) do
- send_email(bad_addr)
+ assert !identity_exists?(bad_addr), "the address #{bad_addr} must not exist."
+ if !identity_exists?(addr)
+ user = assert_create_user(TEST_EMAIL_USER, :monitor)
+ upload_public_key(user.id, TEST_EMAIL_PUBLIC_KEY)
+ end
+ assert identity_exists?(addr), "The identity #{addr} should have been created, but it doesn't exist yet."
+ assert_send_email(addr)
+ assert_raises(Net::SMTPError) do
+ send_email(bad_addr)
+ end
+ pass
end
- pass
end
private
@@ -123,6 +155,59 @@ class Mx < LeapTest
}
end
+ #
+ # returns a random identity record that also has valid address
+ # and destination fields.
+ #
+ # options:
+ #
+ # * :with_public_key -- searches only for identities with public keys
+ #
+ # note to self: for debugging, here is the curl you want:
+ # curl --netrc "127.0.0.1:5984/identities/_design/Identity/_view/by_address?startkey=\"xxxx@leap.se\"&endkey=\"xxxx@leap.se\"&reduce=false&include_docs=true"
+ #
+ def pick_random_identity(tries=5, options={})
+ assert_get(couchdb_url("/identities", couch_url_options)) do |body|
+ assert response = JSON.parse(body)
+ doc_count = response['doc_count'].to_i
+ if doc_count <= 1
+ # the design document counts as one document.
+ skip "There are no identity documents yet."
+ else
+ # try repeatedly to get a valid doc
+ for i in 1..tries
+ offset = rand(doc_count) # pick a random document
+ url = couchdb_url("/identities/_all_docs?include_docs=true&limit=1&skip=#{offset}", couch_url_options)
+ assert_get(url) do |body|
+ assert response = JSON.parse(body)
+ record = response['rows'].first
+ if record['id'] =~ /_design/
+ next
+ elsif record['doc'] && record['doc']['address']
+ next if record['doc']['destination'].nil? || record['doc']['destination'].empty?
+ next if options[:with_public_key] && !record_has_key?(record)
+ return record['doc']
+ else
+ fail "Identity document #{record['id']} is missing an address field. #{record['doc'].inspect}"
+ end
+ end
+ end
+ if options[:with_public_key]
+ skip "Could not find an Identity document with a public key for testing."
+ else
+ fail "Failed to find a valid Identity document (with address and destination)."
+ end
+ end
+ end
+ end
+
+ def record_has_key?(record)
+ !record['doc']['keys'].nil? &&
+ !record['doc']['keys'].empty? &&
+ !record['doc']['keys']['pgp'].nil? &&
+ !record['doc']['keys']['pgp'].empty?
+ end
+
TEST_EMAIL_PUBLIC_KEY=<<HERE
-----BEGIN PGP PUBLIC KEY BLOCK-----
mI0EVvzIKQEEAN4f8FOGntJGTTD+fFUQS6y/ihn6tYLtyGZZbCOd0t/9kHt/raoR
diff --git a/tests/white-box/network.rb b/tests/server-tests/white-box/network.rb
index 436fc8a8..a08cdfbe 100644
--- a/tests/white-box/network.rb
+++ b/tests/server-tests/white-box/network.rb
@@ -65,7 +65,7 @@ class Network < LeapTest
end
def test_03_Is_shorewall_running?
- ignore unless File.exists?('/sbin/shorewall')
+ ignore unless File.exist?('/sbin/shorewall')
assert_run('/sbin/shorewall status')
pass
end
@@ -75,7 +75,7 @@ class Network < LeapTest
def test_04_Are_server_certificates_valid?
cert_paths = ["/etc/x509/certs/leap_commercial.crt", "/etc/x509/certs/leap.crt"]
cert_paths.each do |cert_path|
- if File.exists?(cert_path)
+ if File.exist?(cert_path)
cert = OpenSSL::X509::Certificate.new(File.read(cert_path))
if Time.now > cert.not_after
fail "The certificate #{cert_path} expired on #{cert.not_after}"
diff --git a/tests/white-box/openvpn.rb b/tests/server-tests/white-box/openvpn.rb
index 170d4503..170d4503 100644
--- a/tests/white-box/openvpn.rb
+++ b/tests/server-tests/white-box/openvpn.rb
diff --git a/tests/white-box/soledad.rb b/tests/server-tests/white-box/soledad.rb
index d41bee58..d41bee58 100644
--- a/tests/white-box/soledad.rb
+++ b/tests/server-tests/white-box/soledad.rb
diff --git a/tests/white-box/webapp.rb b/tests/server-tests/white-box/webapp.rb
index 68f3dcd2..da1ec8c5 100644
--- a/tests/white-box/webapp.rb
+++ b/tests/server-tests/white-box/webapp.rb
@@ -28,7 +28,7 @@ class Webapp < LeapTest
def test_03_Are_daemons_running?
assert_running '^/usr/sbin/apache2'
- assert_running '^/usr/bin/ruby /usr/bin/nickserver'
+ assert_running '^ruby /usr/bin/nickserver'
pass
end
@@ -61,7 +61,7 @@ class Webapp < LeapTest
soledad_url = "https://#{soledad_server}/user-#{user.id}"
soledad_cert = "/usr/local/share/ca-certificates/leap_ca.crt"
assert_run "#{command} #{user.id} #{user.session_token} #{soledad_url} #{soledad_cert} #{user.password}"
- assert_user_db_exists(user)
+ assert_user_db_privileges(user)
pass
end
end
@@ -96,39 +96,19 @@ class Webapp < LeapTest
end
#
- # returns true if the per-user db created by soledad-server exists.
- # we try three times, and give up after that.
+ # checks if user db exists and is properly protected
#
- def assert_user_db_exists(user)
- db_name = "user-#{user.id}"
- repeatedly_try("/#{db_name}") do |body, response, error|
- assert false, "Could not find user db `#{db_name}` for test user `#{user.username}`\nuuid=#{user.id}\nHTTP #{response.code} #{error} #{body}"
+ def assert_user_db_privileges(user)
+ db_name = "/user-#{user.id}"
+ get(couchdb_url(db_name)) do |body, response, error|
+ code = response.code.to_i
+ assert code != 404, "Could not find user db `#{db_name}` for test user `#{user.username}`\nuuid=#{user.id}\nHTTP #{response.code} #{error} #{body}"
+ # After moving to couchdb, webapp user is not allowed to Read user dbs,
+ # but the return code for non-existent databases is 404. See #7674
+ # 401 should come as we aren't supposed to have read privileges on it.
+ assert code != 200, "Incorrect security settings (design doc) on user db `#{db_name}` for test user `#{user.username}`\nuuid=#{user.id}\nHTTP #{response.code} #{error} #{body}"
+ assert code == 401, "Unknown error on user db on user db `#{db_name}` for test user `#{user.username}`\nuuid=#{user.id}\nHTTP #{response.code} #{error} #{body}"
end
- repeatedly_try("/#{db_name}/_design/docs") do |body, response, error|
- assert false, "Could not find design docs for user db `#{db_name}` for test user `#{user.username}`\nuuid=#{user.id}\nHTTP #{response.code} #{error} #{body}"
- end
- end
-
- #
- # tries the URL repeatedly, giving up and yield the last response if
- # no try returned a 200 http status code.
- #
- def repeatedly_try(url, &block)
- last_body, last_response, last_error = nil
- 3.times do
- sleep 0.2
- get(couchdb_url(url)) do |body, response, error|
- last_body, last_response, last_error = body, response, error
- # After moving to couchdb, webapp user is not allowed to Read user dbs,
- # but the return code for non-existent databases is 404. See #7674
- if response.code.to_i == 401
- return
- end
- end
- sleep 1
- end
- yield last_body, last_response, last_error
- return
end
end