2017-04-25 18:47:41 +00:00
import pwd
2017-12-30 13:57:48 +00:00
from oscrypto import asymmetric
from csrbuilder import CSRBuilder , pem_armor_csr
from subprocess import check_output
from importlib import reload
2015-09-09 05:31:48 +00:00
from click . testing import CliRunner
2016-09-18 15:30:31 +00:00
from datetime import datetime , timedelta
2017-05-03 07:04:52 +00:00
from time import sleep
2017-12-30 13:57:48 +00:00
import json
2017-04-25 10:52:10 +00:00
import pytest
2017-05-03 07:04:52 +00:00
import shutil
2017-08-16 21:08:20 +00:00
import sys
2017-05-03 07:04:52 +00:00
import os
2017-04-25 13:04:11 +00:00
2017-05-03 21:54:08 +00:00
UA_FEDORA_FIREFOX = " Mozilla/5.0 (X11; Fedora; Linux x86_64) " \
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 "
2017-05-03 14:42:37 +00:00
smtp = None
inbox = [ ]
class DummySMTP ( object ) :
def __init__ ( self , address ) :
self . address = address
def login ( self , username , password ) :
self . username = username
self . password = password
def sendmail ( self , from_address , to_address , fullmessage ) :
global inbox
inbox . append ( fullmessage )
return [ ]
def quit ( self ) :
self . has_quit = True
# this is the actual monkey patch (simply replacing one class with another)
import smtplib
smtplib . SMTP = DummySMTP
2015-09-09 05:31:48 +00:00
runner = CliRunner ( )
2017-04-25 10:52:10 +00:00
@pytest.fixture ( scope = ' module ' )
def client ( ) :
from certidude . api import certidude_app
2017-05-01 16:20:50 +00:00
from falcon import testing
app = certidude_app ( )
return testing . TestClient ( app )
2017-04-25 10:52:10 +00:00
2017-04-25 13:40:33 +00:00
def generate_csr ( cn = None ) :
2017-12-30 13:57:48 +00:00
public_key , private_key = asymmetric . generate_pair ( ' rsa ' , bit_size = 2048 )
builder = CSRBuilder ( { ' common_name ' : cn } , public_key )
request = builder . build ( private_key )
return pem_armor_csr ( request )
2017-04-25 10:52:10 +00:00
2017-05-03 21:03:51 +00:00
def clean_client ( ) :
assert os . getuid ( ) == 0 and os . getgid ( ) == 0
2017-12-30 13:57:48 +00:00
files = [
" /etc/certidude/client.conf " ,
" /etc/certidude/services.conf " ,
2018-04-27 07:48:15 +00:00
" /etc/certidude/authority/ca.example.lan/ca_cert.pem " ,
" /etc/certidude/authority/ca.example.lan/client_key.pem " ,
" /etc/certidude/authority/ca.example.lan/server_key.pem " ,
" /etc/certidude/authority/ca.example.lan/client_req.pem " ,
" /etc/certidude/authority/ca.example.lan/server_req.pem " ,
" /etc/certidude/authority/ca.example.lan/client_cert.pem " ,
" /etc/certidude/authority/ca.example.lan/server_cert.pem " ,
2017-12-30 13:57:48 +00:00
]
for path in files :
if os . path . exists ( path ) :
os . unlink ( path )
2017-05-03 21:03:51 +00:00
# Remove client storage area
if os . path . exists ( " /tmp/ca.example.lan " ) :
for filename in os . listdir ( " /tmp/ca.example.lan " ) :
if filename . endswith ( " .pem " ) :
os . unlink ( os . path . join ( " /tmp/ca.example.lan " , filename ) )
2017-05-06 21:07:41 +00:00
# Reset IPsec stuff
with open ( " /etc/ipsec.conf " , " w " ) as fh : # TODO: make compatible with Fedora
pass
with open ( " /etc/ipsec.secrets " , " w " ) as fh : # TODO: make compatible with Fedora
pass
2017-05-03 21:03:51 +00:00
2017-05-06 21:35:02 +00:00
def clean_server ( ) :
2018-04-27 07:48:15 +00:00
os . umask ( 0o22 )
2017-05-01 16:20:50 +00:00
if os . path . exists ( " /run/certidude/server.pid " ) :
with open ( " /run/certidude/server.pid " ) as fh :
try :
os . kill ( int ( fh . read ( ) ) , 15 )
except OSError :
pass
2017-05-07 19:11:24 +00:00
2017-05-01 16:20:50 +00:00
if os . path . exists ( " /var/lib/certidude/ca.example.lan " ) :
shutil . rmtree ( " /var/lib/certidude/ca.example.lan " )
2017-05-01 23:06:45 +00:00
if os . path . exists ( " /run/certidude " ) :
shutil . rmtree ( " /run/certidude " )
2018-04-27 07:48:15 +00:00
files = [
" /etc/krb5.keytab " ,
" /etc/samba/smb.conf " ,
" /etc/certidude/server.conf " ,
" /etc/certidude/builder.conf " ,
" /etc/certidude/profile.conf " ,
" /var/log/certidude.log " ,
" /etc/cron.hourly/certidude " ,
" /etc/systemd/system/certidude.service " ,
" /etc/nginx/sites-available/ca.conf " ,
" /etc/nginx/sites-enabled/ca.conf " ,
" /etc/nginx/sites-available/certidude.conf " ,
" /etc/nginx/sites-enabled/certidude.conf " ,
" /etc/nginx/conf.d/tls.conf " ,
" /etc/certidude/server.keytab " ,
]
for filename in files :
if os . path . exists ( filename ) :
os . unlink ( filename )
2017-05-03 21:03:51 +00:00
2017-05-01 16:20:50 +00:00
# Remove OpenVPN stuff
2017-05-01 16:39:12 +00:00
if os . path . exists ( " /etc/openvpn " ) :
for filename in os . listdir ( " /etc/openvpn " ) :
if filename . endswith ( " .conf " ) :
os . unlink ( os . path . join ( " /etc/openvpn " , filename ) )
if os . path . exists ( " /etc/openvpn/keys " ) :
shutil . rmtree ( " /etc/openvpn/keys " )
2017-05-01 16:20:50 +00:00
2017-05-07 19:11:24 +00:00
# Stop samba
if os . path . exists ( " /run/samba/samba.pid " ) :
with open ( " /run/samba/samba.pid " ) as fh :
try :
os . kill ( int ( fh . read ( ) ) , 15 )
except OSError :
pass
2017-05-09 09:48:24 +00:00
os . system ( " rm -Rfv /var/lib/samba/* " )
2017-05-07 19:11:24 +00:00
# Restore initial resolv.conf
shutil . copyfile ( " /etc/resolv.conf.orig " , " /etc/resolv.conf " )
2017-05-06 21:35:02 +00:00
def test_cli_setup_authority ( ) :
assert os . getuid ( ) == 0 , " Run tests as root in a clean VM or container "
2017-12-30 13:57:48 +00:00
assert check_output ( [ " /bin/hostname " , " -f " ] ) == b " ca.example.lan \n " , " As a safety precaution, unittests only run in a machine whose hostanme -f is ca.example.lan "
2017-05-06 21:35:02 +00:00
2018-04-27 07:48:15 +00:00
os . system ( " DEBIAN_FRONTEND=noninteractive apt-get install -qq -y git build-essential python-dev libkrb5-dev " )
2017-08-16 21:08:20 +00:00
2017-05-08 16:25:59 +00:00
assert not os . environ . get ( " KRB5CCNAME " ) , " Environment contaminated "
assert not os . environ . get ( " KRB5_KTNAME " ) , " Environment contaminated "
2017-07-06 09:29:02 +00:00
# Mock Fedora
2017-07-07 21:07:25 +00:00
for util in " /usr/bin/chcon " , " /usr/bin/dnf " , " /usr/bin/update-ca-trust " , " /usr/sbin/dmidecode " :
2017-07-06 09:29:02 +00:00
with open ( util , " w " ) as fh :
fh . write ( " #!/bin/bash \n " )
fh . write ( " exit 0 \n " )
2017-12-30 13:57:48 +00:00
os . chmod ( util , 0o755 )
2017-07-06 09:29:02 +00:00
if not os . path . exists ( " /etc/pki/ca-trust/source/anchors/ " ) :
os . makedirs ( " /etc/pki/ca-trust/source/anchors/ " )
# Back up original DNS server
2017-05-07 19:11:24 +00:00
if not os . path . exists ( " /etc/resolv.conf.orig " ) :
shutil . copyfile ( " /etc/resolv.conf " , " /etc/resolv.conf.orig " )
2017-05-06 21:35:02 +00:00
clean_server ( )
2017-05-03 21:03:51 +00:00
clean_client ( )
2017-06-04 14:19:29 +00:00
with open ( " /etc/hosts " , " w " ) as fh :
fh . write ( " 127.0.0.1 localhost \n " )
from certidude import const
assert const . FQDN == " ca "
assert const . HOSTNAME == " ca "
assert not const . DOMAIN
2017-05-08 16:25:59 +00:00
# TODO: set hostname to 'ca'
with open ( " /etc/hosts " , " w " ) as fh :
fh . write ( " 127.0.0.1 localhost \n " )
fh . write ( " 127.0.1.1 ca.example.lan ca \n " )
fh . write ( " 127.0.0.1 vpn.example.lan vpn \n " )
fh . write ( " 127.0.0.1 www.example.lan www \n " )
with open ( " /etc/passwd " ) as fh : # TODO: Better
buf = fh . read ( )
if " adminbot " not in buf :
os . system ( " useradd adminbot -G sudo -p ' $1$PBkf5waA$n9EV6WJ7PS6lyGWkgeTPf1 ' " )
if " userbot " not in buf :
os . system ( " useradd userbot -G users -p ' $1$PBkf5waA$n9EV6WJ7PS6lyGWkgeTPf1 ' -c ' User Bot,,, ' " )
2017-05-07 19:11:24 +00:00
2017-06-04 14:19:29 +00:00
reload ( const )
2017-05-01 17:06:39 +00:00
from certidude . cli import entry_point as cli
2017-06-04 14:19:29 +00:00
assert const . FQDN == " ca.example.lan "
assert const . HOSTNAME == " ca "
assert const . DOMAIN == " example.lan "
2017-05-01 16:20:50 +00:00
2017-12-30 13:57:48 +00:00
# Bootstrap authority
bootstrap_pid = os . fork ( ) # TODO: this shouldn't be necessary
if not bootstrap_pid :
assert os . getuid ( ) == 0 and os . getgid ( ) == 0
result = runner . invoke ( cli , [ " setup " , " authority " ] )
assert not result . exception , result . output
return
else :
os . waitpid ( bootstrap_pid , 0 )
assert os . getuid ( ) == 0 and os . getgid ( ) == 0 , " Environment contaminated "
2017-05-04 06:40:47 +00:00
2017-05-03 07:04:52 +00:00
2017-08-16 20:25:16 +00:00
# Make sure nginx is running
2017-05-06 21:35:02 +00:00
assert os . system ( " nginx -t " ) == 0 , " invalid nginx configuration "
2017-08-16 20:25:16 +00:00
os . system ( " service nginx restart " )
2017-05-06 21:35:02 +00:00
assert os . path . exists ( " /run/nginx.pid " ) , " nginx wasn ' t started up properly "
2015-09-09 05:31:48 +00:00
2017-12-30 13:57:48 +00:00
# Make sure we generated legit CA certificate
2017-05-07 19:11:24 +00:00
from certidude import config , authority , auth , user
2017-08-16 20:25:16 +00:00
assert authority . certificate . serial_number > = 0x100000000000000000000000000000000000000
assert authority . certificate . serial_number < = 0xfffffffffffffffffffffffffffffffffffffff
assert authority . certificate [ " tbs_certificate " ] [ " validity " ] [ " not_before " ] . native . replace ( tzinfo = None ) < datetime . utcnow ( )
assert authority . certificate [ " tbs_certificate " ] [ " validity " ] [ " not_after " ] . native . replace ( tzinfo = None ) > datetime . utcnow ( ) + timedelta ( days = 7000 )
2017-05-04 09:14:47 +00:00
assert authority . server_flags ( " lauri@fedora-123 " ) == False
assert authority . server_flags ( " fedora-123 " ) == False
assert authority . server_flags ( " vpn.example.lan " ) == True
assert authority . server_flags ( " lauri@a.b.c " ) == False
# Generate garbage
with open ( " /var/lib/certidude/ca.example.lan/bla " , " w " ) as fh :
pass
with open ( " /var/lib/certidude/ca.example.lan/requests/bla " , " w " ) as fh :
pass
with open ( " /var/lib/certidude/ca.example.lan/signed/bla " , " w " ) as fh :
pass
with open ( " /var/lib/certidude/ca.example.lan/revoked/bla " , " w " ) as fh :
pass
2017-03-13 15:20:41 +00:00
2017-04-25 21:14:02 +00:00
# Start server before any signing operations are performed
2017-05-02 06:11:28 +00:00
config . CERTIFICATE_RENEWAL_ALLOWED = True
2017-05-03 07:04:52 +00:00
server_pid = os . fork ( )
if not server_pid :
# Fork to prevent umask, setuid, setgid side effects
2017-07-11 18:57:19 +00:00
result = runner . invoke ( cli , [ ' serve ' ] )
2017-05-03 07:04:52 +00:00
assert not result . exception , result . output
return
sleep ( 1 ) # Wait for serve to start up
2017-04-25 21:14:02 +00:00
2017-05-08 20:33:20 +00:00
# TODO: check that port 8080 is listening, otherwise app probably crashed
2017-05-01 20:15:45 +00:00
import requests
2017-05-01 21:52:27 +00:00
# Test CA certificate fetch
2017-12-30 13:57:48 +00:00
buf = open ( " /var/lib/certidude/ca.example.lan/ca_cert.pem " ) . read ( )
2017-05-01 21:52:27 +00:00
r = requests . get ( " http://ca.example.lan/api/certificate " )
assert r . status_code == 200
assert r . headers . get ( ' content-type ' ) == " application/x-x509-ca-cert "
assert r . text == buf
2017-05-04 07:38:49 +00:00
r = client ( ) . simulate_get ( " /api/certificate " )
assert r . status_code == 200
assert r . headers . get ( ' content-type ' ) == " application/x-x509-ca-cert "
assert r . text == buf
2017-05-01 21:52:27 +00:00
2017-04-25 19:52:49 +00:00
# Password is bot, users created by Travis
2017-04-25 18:47:41 +00:00
usertoken = " Basic dXNlcmJvdDpib3Q= "
admintoken = " Basic YWRtaW5ib3Q6Ym90 "
2017-05-07 22:14:58 +00:00
2017-04-25 18:47:41 +00:00
result = runner . invoke ( cli , [ ' users ' ] )
2017-05-01 18:37:34 +00:00
assert not result . exception , result . output
2017-05-07 22:14:58 +00:00
assert " user;userbot;User;Bot;userbot@example.lan " in result . output
assert " admin;adminbot;;;adminbot@example.lan " in result . output
# TODO: assert nothing else is in the list
2017-04-25 18:47:41 +00:00
2017-04-25 10:06:59 +00:00
# Check that we can retrieve empty CRL
2017-05-01 21:52:27 +00:00
assert authority . export_crl ( ) , " Failed to export CRL "
2017-05-03 07:04:52 +00:00
r = requests . get ( " http://ca.example.lan/api/revoked/ " )
2017-05-01 20:49:25 +00:00
assert r . status_code == 200 , r . text
2017-04-25 10:06:59 +00:00
2017-04-25 13:04:11 +00:00
# Test command line interface
2017-03-26 21:16:01 +00:00
result = runner . invoke ( cli , [ ' list ' , ' -srv ' ] )
2017-05-01 18:37:34 +00:00
assert not result . exception , result . output
2017-03-26 21:16:01 +00:00
2017-05-01 21:37:56 +00:00
# Test static
2017-05-01 21:41:34 +00:00
r = requests . get ( " http://ca.example.lan/index.html " )
2017-05-03 07:04:52 +00:00
assert r . status_code == 200 , r . text # if this breaks certidude serve has no read access to static folder
r = requests . get ( " http://ca.example.lan/nonexistant.html " )
assert r . status_code == 404 , r . text
2017-05-04 07:38:49 +00:00
r = requests . get ( " http://ca.example.lan/../nonexistant.html " )
2017-05-06 21:07:41 +00:00
assert r . status_code == 400 , r . text
2017-05-04 07:38:49 +00:00
2017-05-04 09:14:47 +00:00
r = client ( ) . simulate_get ( " / " )
assert r . status_code == 200 , r . text
2017-05-04 07:38:49 +00:00
r = client ( ) . simulate_get ( " /index.html " )
assert r . status_code == 200 , r . text
r = client ( ) . simulate_get ( " /nonexistant.html " )
assert r . status_code == 404 , r . text
r = client ( ) . simulate_get ( " /../nonexistant.html " )
2017-05-06 21:07:41 +00:00
assert r . status_code == 400 , r . text
2017-05-01 21:37:56 +00:00
2017-04-25 13:04:11 +00:00
# Test request submission
2017-12-30 13:57:48 +00:00
buf = generate_csr ( cn = " test " )
2017-04-25 13:04:11 +00:00
r = client ( ) . simulate_post ( " /api/request/ " , body = buf )
assert r . status_code == 415 # wrong content type
r = client ( ) . simulate_post ( " /api/request/ " ,
body = buf ,
headers = { " content-type " : " application/pkcs10 " } )
assert r . status_code == 202 # success
2017-05-03 14:42:37 +00:00
assert " Stored request " in inbox . pop ( ) , inbox
2017-05-06 21:07:41 +00:00
assert os . path . exists ( " /var/lib/certidude/ca.example.lan/requests/test.pem " )
2017-04-25 13:04:11 +00:00
2017-05-03 21:54:08 +00:00
# Test request deletion
r = client ( ) . simulate_delete ( " /api/request/test/ " )
assert r . status_code == 401 , r . text
r = client ( ) . simulate_delete ( " /api/request/test/ " ,
headers = { " Authorization " : usertoken } )
assert r . status_code == 403 , r . text
r = client ( ) . simulate_delete ( " /api/request/test/ " ,
headers = { " User-Agent " : UA_FEDORA_FIREFOX , " Authorization " : admintoken } )
assert r . status_code == 403 , r . text # CSRF prevented
2017-05-06 21:07:41 +00:00
assert os . path . exists ( " /var/lib/certidude/ca.example.lan/requests/test.pem " )
2017-05-03 21:54:08 +00:00
r = client ( ) . simulate_delete ( " /api/request/test/ " ,
headers = { " Authorization " : admintoken } )
assert r . status_code == 200 , r . text
2017-05-04 10:02:14 +00:00
r = client ( ) . simulate_delete ( " /api/request/nonexistant/ " ,
headers = { " Authorization " : admintoken } )
assert r . status_code == 404 , r . text
2017-05-03 21:54:08 +00:00
# Test request submission corner cases
r = client ( ) . simulate_post ( " /api/request/ " ,
body = buf ,
headers = { " content-type " : " application/pkcs10 " } )
assert r . status_code == 202 # success
assert " Stored request " in inbox . pop ( ) , inbox
2017-04-25 13:04:11 +00:00
r = client ( ) . simulate_post ( " /api/request/ " ,
body = buf ,
headers = { " content-type " : " application/pkcs10 " } )
assert r . status_code == 202 # already exists, same keypair so it's ok
2017-05-03 14:42:37 +00:00
assert not inbox
2017-04-25 13:04:11 +00:00
r = client ( ) . simulate_post ( " /api/request/ " ,
2017-05-03 07:04:52 +00:00
query_string = " wait=true " ,
2017-04-25 13:04:11 +00:00
body = buf ,
headers = { " content-type " : " application/pkcs10 " } )
assert r . status_code == 303 # redirect to long poll
2017-05-03 14:42:37 +00:00
assert not inbox
2017-04-25 13:04:11 +00:00
r = client ( ) . simulate_post ( " /api/request/ " ,
2017-12-30 13:57:48 +00:00
body = generate_csr ( cn = " test " ) ,
2017-04-25 13:04:11 +00:00
headers = { " content-type " : " application/pkcs10 " } )
assert r . status_code == 409 # duplicate cn, different keypair
2017-05-03 14:42:37 +00:00
assert not inbox
2017-04-25 13:04:11 +00:00
2017-04-25 13:15:39 +00:00
r = client ( ) . simulate_get ( " /api/request/test/ " , headers = { " Accept " : " application/json " } )
2017-04-25 13:40:33 +00:00
assert r . status_code == 200 # fetch as JSON ok
2017-04-25 13:15:39 +00:00
assert r . headers . get ( ' content-type ' ) == " application/json "
r = client ( ) . simulate_get ( " /api/request/test/ " , headers = { " Accept " : " application/x-pem-file " } )
2017-04-25 13:40:33 +00:00
assert r . status_code == 200 # fetch as PEM ok
2017-04-25 13:15:39 +00:00
assert r . headers . get ( ' content-type ' ) == " application/x-pem-file "
r = client ( ) . simulate_get ( " /api/request/test/ " , headers = { " Accept " : " text/plain " } )
2017-04-25 13:40:33 +00:00
assert r . status_code == 415 # not available as plaintext
2017-04-25 13:15:39 +00:00
r = client ( ) . simulate_get ( " /api/request/nonexistant/ " , headers = { " Accept " : " application/json " } )
2017-04-25 13:40:33 +00:00
assert r . status_code == 404 # nonexistant common names
2017-05-03 21:54:08 +00:00
# TODO: submit messed up CSR-s: no CN, empty CN etc
# Test command line interface
result = runner . invoke ( cli , [ ' list ' , ' -srv ' ] )
assert not result . exception , result . output
# Test sign API call
2017-08-16 20:25:16 +00:00
r = client ( ) . simulate_post ( " /api/request/test/ " )
2017-05-03 21:54:08 +00:00
assert r . status_code == 401 , r . text
2017-08-16 20:25:16 +00:00
r = client ( ) . simulate_post ( " /api/request/test/ " ,
2017-05-03 21:54:08 +00:00
headers = { " Authorization " : usertoken } )
assert r . status_code == 403 , r . text
2017-08-16 20:25:16 +00:00
r = client ( ) . simulate_post ( " /api/request/test/ " ,
2017-05-03 21:54:08 +00:00
headers = { " Authorization " : admintoken } )
assert r . status_code == 201 , r . text
assert " Signed " in inbox . pop ( ) , inbox
# Test autosign
2017-12-30 13:57:48 +00:00
buf = generate_csr ( cn = " test2 " )
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_post ( " /api/request/ " ,
query_string = " autosign=1 " ,
2017-04-25 13:40:33 +00:00
body = buf ,
headers = { " content-type " : " application/pkcs10 " } )
assert r . status_code == 200 # autosign successful
assert r . headers . get ( ' content-type ' ) == " application/x-pem-file "
2017-05-03 14:42:37 +00:00
assert " Signed " in inbox . pop ( ) , inbox
2017-05-03 21:54:08 +00:00
assert not inbox
2017-04-25 13:15:39 +00:00
2017-05-03 21:54:08 +00:00
r = client ( ) . simulate_post ( " /api/request/ " ,
query_string = " autosign=1 " ,
body = buf ,
headers = { " content-type " : " application/pkcs10 " } )
assert r . status_code == 303 # already signed, redirect to signed certificate
assert not inbox
2017-04-25 10:06:59 +00:00
2017-12-30 13:57:48 +00:00
buf = generate_csr ( cn = " test2 " )
2017-05-03 21:54:08 +00:00
r = client ( ) . simulate_post ( " /api/request/ " ,
query_string = " autosign=1 " ,
body = buf ,
headers = { " content-type " : " application/pkcs10 " } )
assert r . status_code == 202 # duplicate CN, request stored
assert " Stored request " in inbox . pop ( ) , inbox
assert not inbox
2017-04-25 20:32:21 +00:00
2017-04-25 10:06:59 +00:00
# Test signed certificate API call
2017-04-25 13:04:11 +00:00
r = client ( ) . simulate_get ( " /api/signed/nonexistant/ " )
2017-05-01 20:49:25 +00:00
assert r . status_code == 404 , r . text
2017-04-25 10:58:21 +00:00
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_get ( " /api/signed/test/ " )
2017-05-01 20:49:25 +00:00
assert r . status_code == 200 , r . text
2017-04-25 10:06:59 +00:00
assert r . headers . get ( ' content-type ' ) == " application/x-pem-file "
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_get ( " /api/signed/test/ " , headers = { " Accept " : " application/json " } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 200 , r . text
2017-04-25 10:06:59 +00:00
assert r . headers . get ( ' content-type ' ) == " application/json "
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_get ( " /api/signed/test/ " , headers = { " Accept " : " text/plain " } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 415 , r . text
2017-04-25 10:58:21 +00:00
2017-04-25 10:06:59 +00:00
# Test revocations API call
2017-04-25 13:04:11 +00:00
r = client ( ) . simulate_get ( " /api/revoked/ " ,
2017-04-25 10:06:59 +00:00
headers = { " Accept " : " application/x-pem-file " } )
2017-08-16 20:25:16 +00:00
assert r . status_code == 200 , r . text
2017-05-01 20:15:45 +00:00
assert r . headers . get ( ' content-type ' ) == " application/x-pem-file "
2017-05-01 20:40:22 +00:00
r = client ( ) . simulate_get ( " /api/revoked/ " )
2017-05-01 20:49:25 +00:00
assert r . status_code == 200 , r . text
2017-05-01 20:40:22 +00:00
assert r . headers . get ( ' content-type ' ) == " application/x-pkcs7-crl "
2017-04-25 13:04:11 +00:00
r = client ( ) . simulate_get ( " /api/revoked/ " ,
headers = { " Accept " : " text/plain " } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 415 , r . text
2017-04-25 13:04:11 +00:00
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_get ( " /api/revoked/ " ,
query_string = " wait=true " ,
2017-04-25 13:04:11 +00:00
headers = { " Accept " : " application/x-pem-file " } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 303 , r . text
2017-04-25 13:04:11 +00:00
2017-04-25 10:06:59 +00:00
# Test attribute fetching API call
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_get ( " /api/signed/test/attr/ " )
2017-07-05 21:22:02 +00:00
assert r . status_code == 401 , r . text
r = client ( ) . simulate_get ( " /api/signed/test/attr/ " , headers = { " Authorization " : usertoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 403 , r . text
2017-07-05 21:22:02 +00:00
r = client ( ) . simulate_get ( " /api/signed/test/attr/ " , headers = { " Authorization " : admintoken } )
assert r . status_code == 200 , r . text
r = client ( ) . simulate_get ( " /api/signed/nonexistant/attr/ " , headers = { " Authorization " : admintoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 404 , r . text
2017-04-25 10:06:59 +00:00
# Tags should not be visible anonymously
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_get ( " /api/signed/test/tag/ " )
2017-05-01 20:49:25 +00:00
assert r . status_code == 401 , r . text
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_get ( " /api/signed/test/tag/ " , headers = { " Authorization " : usertoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 403 , r . text
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_get ( " /api/signed/test/tag/ " , headers = { " Authorization " : admintoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 200 , r . text
2017-04-25 18:47:41 +00:00
2017-04-25 20:32:21 +00:00
# Tags can be added only by admin
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_post ( " /api/signed/test/tag/ " )
2017-05-01 20:49:25 +00:00
assert r . status_code == 401 , r . text
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_post ( " /api/signed/test/tag/ " ,
2017-04-25 20:32:21 +00:00
headers = { " Authorization " : usertoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 403 , r . text
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_post ( " /api/signed/test/tag/ " ,
2017-04-25 20:32:21 +00:00
body = " key=other&value=something " ,
headers = { " content-type " : " application/x-www-form-urlencoded " , " Authorization " : admintoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 200 , r . text
2017-05-04 09:14:47 +00:00
r = client ( ) . simulate_post ( " /api/signed/test/tag/ " ,
body = " key=location&value=Tallinn " ,
headers = { " content-type " : " application/x-www-form-urlencoded " , " Authorization " : admintoken } )
assert r . status_code == 200 , r . text
2017-04-25 18:47:41 +00:00
2017-04-25 20:32:21 +00:00
# Tags can be overwritten only by admin
2017-05-04 07:38:49 +00:00
r = client ( ) . simulate_put ( " /api/signed/test/tag/something/ " )
2017-05-01 20:49:25 +00:00
assert r . status_code == 401 , r . text
2017-05-04 07:38:49 +00:00
r = client ( ) . simulate_put ( " /api/signed/test/tag/something/ " ,
2017-04-25 20:32:21 +00:00
headers = { " Authorization " : usertoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 403 , r . text
2017-05-04 07:38:49 +00:00
r = client ( ) . simulate_put ( " /api/signed/test/tag/something/ " ,
2017-04-25 20:32:21 +00:00
body = " value=else " ,
headers = { " content-type " : " application/x-www-form-urlencoded " , " Authorization " : admintoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 200 , r . text
2017-05-04 09:14:47 +00:00
r = client ( ) . simulate_put ( " /api/signed/test/tag/location=Tallinn/ " ,
body = " value=Tartu " ,
headers = { " content-type " : " application/x-www-form-urlencoded " , " Authorization " : admintoken } )
assert r . status_code == 200 , r . text
2017-05-04 09:35:31 +00:00
r = client ( ) . simulate_get ( " /api/signed/test/tag/ " , headers = { " Authorization " : admintoken } )
assert r . status_code == 200 , r . text
2017-12-30 13:57:48 +00:00
# TODO: assert set(json.loads(r.text)) == set([{"key": "location", "id": "location=Tartu", "value": "Tartu"}, {"key": "other", "id": "else", "value": "else"}]), r.text
2017-07-06 08:15:44 +00:00
# Test scripting
r = client ( ) . simulate_get ( " /api/signed/test/script/ " )
assert r . status_code == 403 , r . text # script not authorized
r = client ( ) . simulate_get ( " /api/signed/nonexistant/script/ " )
assert r . status_code == 404 , r . text # cert not found
# Insert lease
r = client ( ) . simulate_get ( " /api/signed/test/lease/ " , headers = { " Authorization " : admintoken } )
assert r . status_code == 404 , r . text
2017-12-30 13:57:48 +00:00
r = client ( ) . simulate_post ( " /api/lease/ " ,
query_string = " client=test&inner_address=127.0.0.1&outer_address=8.8.8.8 " )
assert r . status_code == 403 , r . text # lease update forbidden without cert
2017-07-06 08:15:44 +00:00
r = client ( ) . simulate_post ( " /api/lease/ " ,
query_string = " client=test&inner_address=127.0.0.1&outer_address=8.8.8.8 " ,
2017-12-30 13:57:48 +00:00
headers = { " X-SSL-CERT " : open ( " /var/lib/certidude/ca.example.lan/signed/ca.example.lan.pem " ) . read ( ) } )
2017-07-06 08:15:44 +00:00
assert r . status_code == 200 , r . text # lease update ok
2017-07-07 21:07:25 +00:00
# Attempt to fetch and execute default.sh script
2017-07-08 08:51:00 +00:00
from xattr import listxattr , getxattr
2017-12-30 13:57:48 +00:00
assert not [ j for j in listxattr ( " /var/lib/certidude/ca.example.lan/signed/test.pem " ) if j . startswith ( b " user.machine. " ) ]
2017-07-07 21:07:25 +00:00
#os.system("curl http://ca.example.lan/api/signed/test/script | bash")
2017-12-30 13:57:48 +00:00
r = client ( ) . simulate_post ( " /api/signed/test/attr " , body = " cpu=i5&mem=512M&dist=Ubunt " ,
2017-07-07 21:07:25 +00:00
headers = { " content-type " : " application/x-www-form-urlencoded " } )
assert r . status_code == 200 , r . text
2017-12-30 13:57:48 +00:00
assert getxattr ( " /var/lib/certidude/ca.example.lan/signed/test.pem " , " user.machine.cpu " ) == b " i5 "
assert getxattr ( " /var/lib/certidude/ca.example.lan/signed/test.pem " , " user.machine.mem " ) == b " 512M "
assert getxattr ( " /var/lib/certidude/ca.example.lan/signed/test.pem " , " user.machine.dist " ) == b " Ubunt "
2017-07-07 21:07:25 +00:00
2017-07-06 08:15:44 +00:00
# Test tagging integration in scripting framework
r = client ( ) . simulate_get ( " /api/signed/test/script/ " )
assert r . status_code == 200 , r . text # script render ok
2018-04-27 07:48:15 +00:00
assert " curl https://ca.example.lan:8443/api/signed/test/attr " in r . text , r . text
2017-07-06 08:15:44 +00:00
assert " Tartu " in r . text , r . text
r = client ( ) . simulate_post ( " /api/signed/test/tag/ " ,
body = " key=script&value=openwrt.sh " ,
headers = { " content-type " : " application/x-www-form-urlencoded " , " Authorization " : admintoken } )
assert r . status_code == 200 , r . text
r = client ( ) . simulate_get ( " /api/signed/test/script/ " )
assert r . status_code == 200 , r . text # script render ok
assert " uci set " in r . text , r . text
# Test lease retrieval
r = client ( ) . simulate_get ( " /api/signed/test/lease/ " )
assert r . status_code == 401 , r . text
r = client ( ) . simulate_get ( " /api/signed/test/lease/ " , headers = { " Authorization " : usertoken } )
assert r . status_code == 403 , r . text
r = client ( ) . simulate_get ( " /api/signed/test/lease/ " , headers = { " Authorization " : admintoken } )
assert r . status_code == 200 , r . text
assert r . headers . get ( ' content-type ' ) == " application/json; charset=UTF-8 "
2017-04-25 20:32:21 +00:00
# Tags can be deleted only by admin
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_delete ( " /api/signed/test/tag/else/ " )
2017-05-01 20:49:25 +00:00
assert r . status_code == 401 , r . text
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_delete ( " /api/signed/test/tag/else/ " ,
2017-04-25 20:32:21 +00:00
headers = { " Authorization " : usertoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 403 , r . text
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_delete ( " /api/signed/test/tag/else/ " ,
2017-04-25 20:32:21 +00:00
headers = { " content-type " : " application/x-www-form-urlencoded " , " Authorization " : admintoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 200 , r . text
2017-05-04 09:14:47 +00:00
r = client ( ) . simulate_delete ( " /api/signed/test/tag/location=Tartu/ " ,
headers = { " content-type " : " application/x-www-form-urlencoded " , " Authorization " : admintoken } )
assert r . status_code == 200 , r . text
2017-07-06 08:15:44 +00:00
r = client ( ) . simulate_delete ( " /api/signed/test/tag/script=openwrt.sh/ " ,
headers = { " content-type " : " application/x-www-form-urlencoded " , " Authorization " : admintoken } )
assert r . status_code == 200 , r . text
2017-05-04 09:35:31 +00:00
r = client ( ) . simulate_get ( " /api/signed/test/tag/ " , headers = { " Authorization " : admintoken } )
assert r . status_code == 200 , r . text
assert r . text == " [] " , r . text
2017-04-25 10:06:59 +00:00
2017-07-06 09:29:02 +00:00
# Test script without tags
r = client ( ) . simulate_get ( " /api/signed/test/script/ " )
assert r . status_code == 200 , r . text # script render ok
assert " # No tags " in r . text , r . text
# Test lease update
r = client ( ) . simulate_post ( " /api/lease/ " ,
query_string = " client=test&inner_address=127.0.0.1&outer_address=8.8.8.8&serial=0 " ,
2017-12-30 13:57:48 +00:00
headers = { " X-SSL-CERT " : open ( " /var/lib/certidude/ca.example.lan/signed/ca.example.lan.pem " ) . read ( ) } )
2017-07-06 09:29:02 +00:00
assert r . status_code == 403 , r . text # invalid serial number supplied
r = client ( ) . simulate_post ( " /api/lease/ " ,
query_string = " client=test&inner_address=1.2.3.4&outer_address=8.8.8.8 " ,
2017-12-30 13:57:48 +00:00
headers = { " X-SSL-CERT " : open ( " /var/lib/certidude/ca.example.lan/signed/ca.example.lan.pem " ) . read ( ) } )
2017-07-06 09:29:02 +00:00
assert r . status_code == 200 , r . text # lease update ok
2017-04-25 20:32:21 +00:00
2017-12-30 13:57:48 +00:00
2017-04-25 20:32:21 +00:00
# Test revocation
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_delete ( " /api/signed/test/ " )
2017-05-01 20:49:25 +00:00
assert r . status_code == 401 , r . text
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_delete ( " /api/signed/test/ " ,
2017-04-25 20:32:21 +00:00
headers = { " Authorization " : usertoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 403 , r . text
2017-05-03 07:04:52 +00:00
r = client ( ) . simulate_delete ( " /api/signed/test/ " ,
2017-04-25 20:32:21 +00:00
headers = { " Authorization " : admintoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 200 , r . text
2017-05-03 14:42:37 +00:00
assert " Revoked " in inbox . pop ( ) , inbox
2018-04-27 07:48:15 +00:00
"""
2017-04-25 10:52:10 +00:00
2017-04-25 21:10:12 +00:00
# Log can be read only by admin
r = client ( ) . simulate_get ( " /api/log/ " )
2017-05-01 20:49:25 +00:00
assert r . status_code == 401 , r . text
2017-04-25 21:10:12 +00:00
r = client ( ) . simulate_get ( " /api/log/ " ,
headers = { " Authorization " : usertoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 403 , r . text
2017-04-25 21:10:12 +00:00
r = client ( ) . simulate_get ( " /api/log/ " ,
headers = { " Authorization " : admintoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 200 , r . text
2017-04-25 21:10:12 +00:00
assert r . headers . get ( ' content-type ' ) == " application/json; charset=UTF-8 "
2018-04-27 07:48:15 +00:00
"""
2017-05-04 07:38:49 +00:00
# Test session API call
2017-05-09 09:48:24 +00:00
r = client ( ) . simulate_get ( " /api/ " )
assert r . status_code == 401
assert " Please authenticate " in r . text
r = client ( ) . simulate_get ( " /api/ " , headers = { " Accept " : " text/plain " , " Authorization " : admintoken } )
assert r . status_code == 415 # invalid media type
2017-05-04 07:38:49 +00:00
r = client ( ) . simulate_get ( " /api/ " , headers = { " Authorization " : usertoken } )
assert r . status_code == 200
2017-05-09 09:48:24 +00:00
assert r . headers . get ( ' content-type ' ) . startswith ( " application/json " )
assert r . json , r . text
assert not r . json . get ( " authority " ) , r . text # No permissions to admin
2017-05-04 07:38:49 +00:00
r = client ( ) . simulate_get ( " /api/ " , headers = { " Authorization " : admintoken } )
assert r . status_code == 200
2017-05-09 09:48:24 +00:00
assert r . headers . get ( ' content-type ' ) . startswith ( " application/json " )
assert " /ev/sub/ " in r . text , r . text
assert r . json , r . text
assert r . json . get ( " authority " ) , r . text
2017-08-16 20:25:16 +00:00
ev_url = r . json . get ( " authority " ) . get ( " events " )
assert ev_url , r . text
if ev_url . startswith ( " / " ) : # Expand URL
ev_url = " http://ca.example.lan " + ev_url
assert ev_url . startswith ( " http://ca.example.lan/ev/sub/ " )
2017-05-04 07:38:49 +00:00
2017-05-09 09:48:24 +00:00
#######################
### Token mechanism ###
#######################
2017-12-30 13:57:48 +00:00
# TODO
2017-05-01 16:20:50 +00:00
2017-05-07 19:11:24 +00:00
2017-05-03 21:03:51 +00:00
#############
### nginx ###
#############
2017-05-07 19:11:24 +00:00
# In this case nginx is set up as web server with TLS certificates
# generated by certidude.
2017-05-03 21:03:51 +00:00
clean_client ( )
2017-05-01 16:20:50 +00:00
2017-05-04 06:40:47 +00:00
result = runner . invoke ( cli , [ " setup " , " nginx " , " -cn " , " www " , " ca.example.lan " ] )
assert result . exception # FQDN required
2017-05-03 21:03:51 +00:00
result = runner . invoke ( cli , [ " setup " , " nginx " , " -cn " , " www.example.lan " , " ca.example.lan " ] )
2017-05-03 14:42:37 +00:00
assert not result . exception , result . output
2017-05-04 06:40:47 +00:00
result = runner . invoke ( cli , [ " setup " , " nginx " , " -cn " , " www.example.lan " , " ca.example.lan " ] )
2017-05-04 09:14:47 +00:00
assert not result . exception , result . output # client conf already exists, remove to regenerate
2017-05-04 06:40:47 +00:00
2017-05-03 21:03:51 +00:00
with open ( " /etc/certidude/client.conf " , " a " ) as fh :
fh . write ( " insecure = true \n " )
2018-04-27 07:48:15 +00:00
fh . write ( " autosign = false \n " )
2017-05-03 21:03:51 +00:00
2018-04-27 07:48:15 +00:00
assert not os . path . exists ( " /etc/certidude/authority/ca.example.lan/server_cert.pem " )
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ " enroll " , " --skip-self " , " --no-wait " ] )
2017-05-03 14:42:37 +00:00
assert not result . exception , result . output
2017-06-04 14:19:29 +00:00
assert not os . path . exists ( " /run/certidude/ca.example.lan.pid " ) , result . output
2018-04-27 07:48:15 +00:00
assert " (autosign not requested) " in result . output , result . output
assert not os . path . exists ( " /etc/certidude/authority/ca.example.lan/server_cert.pem " )
2017-05-03 14:42:37 +00:00
2017-05-03 21:03:51 +00:00
child_pid = os . fork ( )
if not child_pid :
2018-04-10 09:29:05 +00:00
result = runner . invoke ( cli , [ " sign " , " www.example.lan " , " --profile " , " srv " ] )
2017-05-03 21:03:51 +00:00
assert not result . exception , result . output
2017-08-16 20:25:16 +00:00
assert " Publishing request-signed event ' www.example.lan ' on http://localhost/ev/pub/ " in result . output , result . output
2017-05-03 21:03:51 +00:00
return
else :
os . waitpid ( child_pid , 0 )
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ " enroll " , " --skip-self " , " --no-wait " ] )
2017-05-03 14:42:37 +00:00
assert not result . exception , result . output
2017-06-04 14:19:29 +00:00
assert not os . path . exists ( " /run/certidude/ca.example.lan.pid " ) , result . output
2017-05-03 21:03:51 +00:00
assert " Writing certificate to: " in result . output , result . output
2018-04-27 07:48:15 +00:00
assert os . path . exists ( " /etc/certidude/authority/ca.example.lan/server_cert.pem " )
2017-05-03 14:42:37 +00:00
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ " enroll " , " --skip-self " , " --renew " , " --no-wait " ] )
2017-05-03 14:42:37 +00:00
assert not result . exception , result . output
2017-06-04 14:19:29 +00:00
assert not os . path . exists ( " /run/certidude/ca.example.lan.pid " ) , result . output
2018-04-27 07:48:15 +00:00
assert " Renewing using current keypair " in result . output , result . output
2017-05-03 21:03:51 +00:00
# Test nginx setup
assert os . system ( " nginx -t " ) == 0 , " Generated nginx config was invalid "
###############
### OpenVPN ###
###############
2017-05-07 19:11:24 +00:00
# First OpenVPN server is set up
2017-05-03 21:03:51 +00:00
clean_client ( )
2018-04-27 07:48:15 +00:00
assert not os . path . exists ( " /etc/certidude/authority/ca.example.lan/server_cert.pem " )
2017-05-03 14:42:37 +00:00
2017-05-01 16:20:50 +00:00
if not os . path . exists ( " /etc/openvpn/keys " ) :
os . makedirs ( " /etc/openvpn/keys " )
2017-05-04 06:40:47 +00:00
result = runner . invoke ( cli , [ ' setup ' , ' openvpn ' , ' server ' , " -cn " , " vpn " , " ca.example.lan " ] )
assert result . exception , result . output
2017-05-03 21:03:51 +00:00
result = runner . invoke ( cli , [ ' setup ' , ' openvpn ' , ' server ' , " -cn " , " vpn.example.lan " , " ca.example.lan " ] )
assert not result . exception , result . output
2017-05-04 06:40:47 +00:00
result = runner . invoke ( cli , [ ' setup ' , ' openvpn ' , ' server ' , " -cn " , " vpn.example.lan " , " ca.example.lan " ] )
2017-05-04 09:14:47 +00:00
assert not result . exception , result . output # client conf already exists, remove to regenerate
2017-05-04 06:40:47 +00:00
2017-05-01 16:20:50 +00:00
with open ( " /etc/certidude/client.conf " , " a " ) as fh :
fh . write ( " insecure = true \n " )
2018-04-27 07:48:15 +00:00
fh . write ( " autosign = false \n " )
2017-05-01 16:20:50 +00:00
2018-04-27 07:48:15 +00:00
assert not os . path . exists ( " /etc/certidude/authority/ca.example.lan/server_cert.pem " )
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ " enroll " , " --skip-self " , " --no-wait " ] )
2017-05-03 21:03:51 +00:00
assert not result . exception , result . output
2018-04-27 07:48:15 +00:00
assert " (autosign not requested) " in result . output , result . output
2017-06-04 14:19:29 +00:00
assert not os . path . exists ( " /run/certidude/ca.example.lan.pid " ) , result . output
2017-08-16 20:25:16 +00:00
assert not os . path . exists ( " /var/lib/certidude/ca.example.lan/signed/vpn.example.lan.pem " )
2017-05-03 07:04:52 +00:00
child_pid = os . fork ( )
if not child_pid :
2017-08-16 20:25:16 +00:00
assert not os . path . exists ( " /var/lib/certidude/ca.example.lan/signed/vpn.example.lan.pem " )
2018-04-10 09:29:05 +00:00
result = runner . invoke ( cli , [ " sign " , " vpn.example.lan " , " --profile " , " srv " ] )
2017-05-03 07:04:52 +00:00
assert not result . exception , result . output
2017-08-16 20:25:16 +00:00
assert " overwrit " not in result . output , result . output
assert " Publishing request-signed event ' vpn.example.lan ' on http://localhost/ev/pub/ " in result . output , result . output
2017-05-03 07:04:52 +00:00
return
else :
os . waitpid ( child_pid , 0 )
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ " enroll " , " --skip-self " , " --no-wait " ] )
2017-05-01 18:37:34 +00:00
assert not result . exception , result . output
2017-06-04 14:19:29 +00:00
assert not os . path . exists ( " /run/certidude/ca.example.lan.pid " ) , result . output
2017-05-03 21:03:51 +00:00
assert " Writing certificate to: " in result . output , result . output
2018-04-27 07:48:15 +00:00
assert os . path . exists ( " /etc/certidude/authority//ca.example.lan/server_cert.pem " )
2017-05-06 21:07:41 +00:00
assert os . path . exists ( " /etc/openvpn/site-to-client.conf " )
2017-05-03 21:03:51 +00:00
2017-05-07 19:11:24 +00:00
# Secondly OpenVPN client is set up
2017-05-03 21:03:51 +00:00
os . unlink ( " /etc/certidude/client.conf " )
os . unlink ( " /etc/certidude/services.conf " )
result = runner . invoke ( cli , [ ' setup ' , ' openvpn ' , ' client ' , " -cn " , " roadwarrior1 " , " ca.example.lan " , " vpn.example.lan " ] )
assert not result . exception , result . output
2017-05-04 06:40:47 +00:00
result = runner . invoke ( cli , [ ' setup ' , ' openvpn ' , ' client ' , " -cn " , " roadwarrior1 " , " ca.example.lan " , " vpn.example.lan " ] )
2017-05-04 09:14:47 +00:00
assert not result . exception , result . output # client conf already exists, remove to regenerate
2017-05-04 06:40:47 +00:00
2017-05-03 21:03:51 +00:00
with open ( " /etc/certidude/client.conf " , " a " ) as fh :
fh . write ( " insecure = true \n " )
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ " enroll " , " --skip-self " , " --no-wait " ] )
2017-05-03 21:03:51 +00:00
assert not result . exception , result . output
2017-06-04 14:19:29 +00:00
assert not os . path . exists ( " /run/certidude/ca.example.lan.pid " ) , result . output
2017-05-03 21:03:51 +00:00
assert " Writing certificate to: " in result . output , result . output
2017-05-06 21:07:41 +00:00
assert os . path . exists ( " /etc/openvpn/client-to-site.conf " )
2017-05-03 21:03:51 +00:00
2017-05-07 19:11:24 +00:00
# TODO: Check that tunnel interfaces came up, perhaps try to ping?
2017-05-04 17:56:53 +00:00
# TODO: assert key, req, cert paths were included correctly in OpenVPN config
2017-05-07 19:11:24 +00:00
2017-08-16 20:25:16 +00:00
clean_client ( )
2017-05-03 21:03:51 +00:00
2017-08-16 20:25:16 +00:00
result = runner . invoke ( cli , [ ' setup ' , ' openvpn ' , ' networkmanager ' , " -cn " , " roadwarrior3 " , " ca.example.lan " , " vpn.example.lan " ] )
assert not result . exception , result . output
with open ( " /etc/certidude/client.conf " , " a " ) as fh :
fh . write ( " insecure = true \n " )
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ " enroll " , " --skip-self " , " --no-wait " ] )
2017-08-16 20:25:16 +00:00
assert not result . exception , result . output
assert not os . path . exists ( " /run/certidude/ca.example.lan.pid " ) , result . output
assert " Writing certificate to: " in result . output , result . output
#################################
### Subscribe to event source ###
#################################
ev_pid = os . fork ( )
if not ev_pid :
r = requests . get ( ev_url , headers = { " Accept " : " text/event-stream " } , stream = True )
assert r . status_code == 200 , r . text
2017-12-30 13:57:48 +00:00
i = r . iter_lines ( decode_unicode = True )
assert i . __next__ ( ) == " : hi "
assert not i . __next__ ( )
2017-08-16 20:25:16 +00:00
# IPSec gateway below
2017-12-30 13:57:48 +00:00
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( )
assert i . __next__ ( ) . startswith ( " id: " )
"""
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Served CA certificate ' )
assert not i . __next__ ( )
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( )
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Serving revocation list (PEM) ' )
assert not i . __next__ ( )
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( ) # FIXME
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Serving revocation list (PEM) ' )
assert not i . __next__ ( )
assert i . __next__ ( ) == " event: request-submitted " , " %s ; %s " % ( i . __next__ ( ) , i . __next__ ( ) )
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) == " data: ipsec.example.lan "
assert not i . __next__ ( )
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( )
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Stored signing request ipsec.example.lan ' )
assert not i . __next__ ( )
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( ) # FIXME
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Stored signing request ipsec.example.lan ' )
assert not i . __next__ ( )
assert i . __next__ ( ) == " event: request-signed "
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: ipsec.example.lan ' )
assert not i . __next__ ( )
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( )
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Serving revocation list (PEM) ' )
assert not i . __next__ ( )
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( ) # FIXME
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Serving revocation list (PEM) ' )
assert not i . __next__ ( )
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( )
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Served certificate ipsec.example.lan ' )
assert not i . __next__ ( )
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( ) # FIXME
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Served certificate ipsec.example.lan ' )
assert not i . __next__ ( )
2017-08-16 20:25:16 +00:00
# IPsec client as service enroll
2017-12-30 13:57:48 +00:00
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( )
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Serving revocation list (PEM) ' )
assert not i . __next__ ( )
2017-08-16 20:25:16 +00:00
2017-12-30 13:57:48 +00:00
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( ) # FIXME
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Serving revocation list (PEM) ' )
assert not i . __next__ ( )
2017-08-16 20:25:16 +00:00
2017-12-30 13:57:48 +00:00
assert i . __next__ ( ) == " event: request-signed " , i . __next__ ( )
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: roadwarrior2 ' )
assert not i . __next__ ( )
2017-08-16 20:25:16 +00:00
2017-12-30 13:57:48 +00:00
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( )
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Autosigned roadwarrior2 ' )
assert not i . __next__ ( )
2017-08-16 20:25:16 +00:00
2017-12-30 13:57:48 +00:00
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( ) # FIXME
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Autosigned roadwarrior2 ' )
assert not i . __next__ ( )
2017-08-16 20:25:16 +00:00
# IPSec client using Networkmanger enroll
2017-12-30 13:57:48 +00:00
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( )
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Served CA certificate ' )
assert not i . __next__ ( )
2017-08-16 20:25:16 +00:00
2017-12-30 13:57:48 +00:00
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( )
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Serving revocation list (PEM) ' )
assert not i . __next__ ( )
2017-08-16 20:25:16 +00:00
2017-12-30 13:57:48 +00:00
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( ) # FIXME
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Serving revocation list (PEM) ' )
assert not i . __next__ ( )
2017-08-16 20:25:16 +00:00
2017-12-30 13:57:48 +00:00
assert i . __next__ ( ) == " event: request-signed " , i . __next__ ( )
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: roadwarrior4 ' )
assert not i . __next__ ( )
2017-08-16 20:25:16 +00:00
2017-12-30 13:57:48 +00:00
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( )
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Autosigned roadwarrior4 ' )
assert not i . __next__ ( )
2017-08-16 20:25:16 +00:00
2017-12-30 13:57:48 +00:00
assert i . __next__ ( ) == " event: log-entry " , i . __next__ ( ) # FIXME
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: { " message " : " Autosigned roadwarrior4 ' )
assert not i . __next__ ( )
2017-08-16 20:25:16 +00:00
# Revoke
2017-12-30 13:57:48 +00:00
assert i . __next__ ( ) == " event: certificate-revoked " , i . __next__ ( ) # why?!
assert i . __next__ ( ) . startswith ( " id: " )
assert i . __next__ ( ) . startswith ( ' data: roadwarrior4 ' )
assert not i . __next__ ( )
"""
2017-08-16 20:25:16 +00:00
return
#############
2017-05-03 21:03:51 +00:00
### IPSec ###
2017-08-16 20:25:16 +00:00
#############
# Setup gateway
2017-05-03 21:03:51 +00:00
clean_client ( )
2017-12-30 13:57:48 +00:00
assert not os . path . exists ( " /var/lib/certidude/ca.example.lan/signed/ipsec.example.lan.pem " )
2017-05-03 21:03:51 +00:00
2017-05-04 06:40:47 +00:00
result = runner . invoke ( cli , [ ' setup ' , ' strongswan ' , ' server ' , " -cn " , " ipsec " , " ca.example.lan " ] )
assert result . exception , result . output # FQDN required
2017-12-30 13:57:48 +00:00
assert not os . path . exists ( " /var/lib/certidude/ca.example.lan/signed/ipsec.example.lan.pem " )
2017-05-04 06:40:47 +00:00
2017-05-03 21:03:51 +00:00
result = runner . invoke ( cli , [ ' setup ' , ' strongswan ' , ' server ' , " -cn " , " ipsec.example.lan " , " ca.example.lan " ] )
assert not result . exception , result . output
2018-04-27 07:48:15 +00:00
assert open ( " /etc/ipsec.secrets " ) . read ( ) == " : RSA /etc/certidude/authority/ca.example.lan/server_key.pem \n "
2017-12-30 13:57:48 +00:00
assert not os . path . exists ( " /var/lib/certidude/ca.example.lan/signed/ipsec.example.lan.pem " )
2017-05-03 21:03:51 +00:00
2017-05-04 06:40:47 +00:00
result = runner . invoke ( cli , [ ' setup ' , ' strongswan ' , ' server ' , " -cn " , " ipsec.example.lan " , " ca.example.lan " ] )
2017-05-04 09:14:47 +00:00
assert not result . exception , result . output # client conf already exists, remove to regenerate
2017-12-30 13:57:48 +00:00
assert not os . path . exists ( " /var/lib/certidude/ca.example.lan/signed/ipsec.example.lan.pem " )
2017-05-04 06:40:47 +00:00
2017-05-03 21:03:51 +00:00
with open ( " /etc/certidude/client.conf " , " a " ) as fh :
fh . write ( " insecure = true \n " )
2018-04-27 07:48:15 +00:00
fh . write ( " autosign = false \n " )
2017-05-03 21:03:51 +00:00
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ " enroll " , " --skip-self " , " --no-wait " ] )
2017-05-03 21:03:51 +00:00
assert not result . exception , result . output
2018-04-27 07:48:15 +00:00
assert " (autosign not requested) " in result . output , result . output
2017-06-04 14:19:29 +00:00
assert not os . path . exists ( " /run/certidude/ca.example.lan.pid " ) , result . output
2017-08-16 20:25:16 +00:00
assert not os . path . exists ( " /var/lib/certidude/ca.example.lan/signed/ipsec.example.lan.pem " )
2017-05-03 21:03:51 +00:00
child_pid = os . fork ( )
if not child_pid :
2017-08-16 20:25:16 +00:00
assert not os . path . exists ( " /var/lib/certidude/ca.example.lan/signed/ipsec.example.lan.pem " )
2018-04-10 09:29:05 +00:00
result = runner . invoke ( cli , [ " sign " , " ipsec.example.lan " , " --profile " , " srv " ] )
2017-05-03 21:03:51 +00:00
assert not result . exception , result . output
2017-08-16 20:25:16 +00:00
assert " overwrit " not in result . output , result . output
assert " Publishing request-signed event ' ipsec.example.lan ' on http://localhost/ev/pub/ " in result . output , result . output
2017-05-03 21:03:51 +00:00
return
else :
os . waitpid ( child_pid , 0 )
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ " enroll " , " --skip-self " , " --no-wait " ] )
2017-05-03 21:03:51 +00:00
assert not result . exception , result . output
2017-06-04 14:19:29 +00:00
assert not os . path . exists ( " /run/certidude/ca.example.lan.pid " ) , result . output
2017-05-03 21:03:51 +00:00
assert " Writing certificate to: " in result . output , result . output
2018-04-27 07:48:15 +00:00
assert os . path . exists ( " /etc/certidude/authority/ca.example.lan/server_cert.pem " )
2017-05-03 21:03:51 +00:00
2017-08-16 20:25:16 +00:00
# IPSec client as service
2017-05-03 21:03:51 +00:00
os . unlink ( " /etc/certidude/client.conf " )
os . unlink ( " /etc/certidude/services.conf " )
result = runner . invoke ( cli , [ ' setup ' , ' strongswan ' , ' client ' , " -cn " , " roadwarrior2 " , " ca.example.lan " , " ipsec.example.lan " ] )
2017-05-02 06:11:28 +00:00
assert not result . exception , result . output
2017-05-03 07:04:52 +00:00
2017-05-04 06:40:47 +00:00
result = runner . invoke ( cli , [ ' setup ' , ' strongswan ' , ' client ' , " -cn " , " roadwarrior2 " , " ca.example.lan " , " ipsec.example.lan " ] )
2017-05-04 09:14:47 +00:00
assert not result . exception , result . output # client conf already exists, remove to regenerate
2017-05-04 06:40:47 +00:00
2017-05-03 21:03:51 +00:00
with open ( " /etc/certidude/client.conf " , " a " ) as fh :
fh . write ( " insecure = true \n " )
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ " enroll " , " --skip-self " , " --no-wait " ] )
2017-05-03 21:03:51 +00:00
assert not result . exception , result . output
2017-06-04 14:19:29 +00:00
assert not os . path . exists ( " /run/certidude/ca.example.lan.pid " ) , result . output
2017-05-03 21:03:51 +00:00
assert " Writing certificate to: " in result . output , result . output
2017-08-16 20:25:16 +00:00
# IPSec using NetworkManager
2017-05-04 06:40:47 +00:00
clean_client ( )
2017-05-03 21:03:51 +00:00
result = runner . invoke ( cli , [ ' setup ' , ' strongswan ' , ' networkmanager ' , " -cn " , " roadwarrior4 " , " ca.example.lan " , " ipsec.example.lan " ] )
assert not result . exception , result . output
2017-05-04 06:40:47 +00:00
with open ( " /etc/certidude/client.conf " , " a " ) as fh :
fh . write ( " insecure = true \n " )
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ " enroll " , " --skip-self " , " --no-wait " ] )
2017-05-04 06:40:47 +00:00
assert not result . exception , result . output
2017-06-04 14:19:29 +00:00
assert not os . path . exists ( " /run/certidude/ca.example.lan.pid " ) , result . output
2017-05-04 06:40:47 +00:00
assert " Writing certificate to: " in result . output , result . output
2017-05-08 16:25:59 +00:00
######################################
### Test revocation on client side ###
######################################
# First revoke on server side
child_pid = os . fork ( )
if not child_pid :
result = runner . invoke ( cli , [ ' revoke ' , ' roadwarrior4 ' ] )
assert not result . exception , result . output
return
else :
os . waitpid ( child_pid , 0 )
# Make sure check is ran on the client side
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ " enroll " , " --skip-self " , " --no-wait " ] )
2017-05-08 16:25:59 +00:00
assert not result . exception , result . output
2017-06-04 14:19:29 +00:00
assert not os . path . exists ( " /run/certidude/ca.example.lan.pid " ) , result . output
#assert "Certificate has been revoked, wiping keys and certificates" in result.output, result.output
#assert "Writing certificate to:" in result.output, result.output
#########################################################
### Test that legacy features are disabled by default ###
#########################################################
2018-01-05 12:42:14 +00:00
r = requests . get ( " http://ca.example.lan/api/scep/ " )
2017-06-04 14:19:29 +00:00
assert r . status_code == 404
2018-01-05 12:42:14 +00:00
r = requests . post ( " http://ca.example.lan/api/scep/ " )
2017-06-04 14:19:29 +00:00
assert r . status_code == 404
2018-04-27 07:48:15 +00:00
#################
### Test OCSP ###
#################
r = requests . get ( " http://ca.example.lan/api/ocsp/ " )
assert r . status_code == 400
2018-01-05 12:42:14 +00:00
r = requests . post ( " http://ca.example.lan/api/ocsp/ " )
2018-04-27 07:48:15 +00:00
assert r . status_code == 400
assert os . system ( " openssl ocsp -issuer /var/lib/certidude/ca.example.lan/ca_cert.pem -CAfile /var/lib/certidude/ca.example.lan/ca_cert.pem -cert /var/lib/certidude/ca.example.lan/signed/roadwarrior2.pem -text -url http://ca.example.lan/api/ocsp/ -out /tmp/ocsp1.log " ) == 0
assert os . system ( " openssl ocsp -issuer /var/lib/certidude/ca.example.lan/ca_cert.pem -CAfile /var/lib/certidude/ca.example.lan/ca_cert.pem -cert /var/lib/certidude/ca.example.lan/ca_cert.pem -text -url http://ca.example.lan/api/ocsp/ -out /tmp/ocsp2.log " ) == 0
for filename in os . listdir ( " /var/lib/certidude/ca.example.lan/revoked " ) :
if not filename . endswith ( " .pem " ) :
continue
assert os . system ( " openssl ocsp -issuer /var/lib/certidude/ca.example.lan/ca_cert.pem -CAfile /var/lib/certidude/ca.example.lan/ca_cert.pem -cert /var/lib/certidude/ca.example.lan/revoked/ %s -text -url http://ca.example.lan/api/ocsp/ -out /tmp/ocsp3.log " % filename ) == 0
break
with open ( " /tmp/ocsp1.log " ) as fh :
buf = fh . read ( )
assert " : good " in buf , buf
with open ( " /tmp/ocsp2.log " ) as fh :
buf = fh . read ( )
assert " : unknown " in buf , buf
with open ( " /tmp/ocsp3.log " ) as fh :
buf = fh . read ( )
assert " : revoked " in buf , buf
2017-05-08 16:25:59 +00:00
2017-05-03 21:03:51 +00:00
2017-05-07 19:11:24 +00:00
####################################
### Switch to Kerberos/LDAP auth ###
####################################
# Shut down current instance
2017-07-11 18:57:19 +00:00
os . kill ( server_pid , 15 )
2017-05-07 19:11:24 +00:00
requests . get ( " http://ca.example.lan/api/ " )
2018-04-27 07:48:15 +00:00
# sleep(2)
# os.kill(server_pid, 9) # TODO: Figure out why doesn't shut down gracefully
2017-05-07 19:11:24 +00:00
os . waitpid ( server_pid , 0 )
2018-04-27 07:48:15 +00:00
# Install packages
os . system ( " apt-get install -y samba krb5-user winbind bc " )
clean_server ( )
# Bootstrap domain controller here,
# Samba startup takes some timec
os . system ( " samba-tool domain provision --server-role=dc --domain=EXAMPLE --realm=EXAMPLE.LAN --host-name=ca " )
os . system ( " samba-tool user add userbot S4l4k4l4 --given-name= ' User ' --surname= ' Bot ' " )
os . system ( " samba-tool user add adminbot S4l4k4l4 --given-name= ' Admin ' --surname= ' Bot ' " )
os . system ( " samba-tool group addmembers ' Domain Admins ' adminbot " )
os . system ( " samba-tool user setpassword administrator --newpassword=S4l4k4l4 " )
os . symlink ( " /var/lib/samba/private/secrets.keytab " , " /etc/krb5.keytab " )
os . chmod ( " /var/lib/samba/private/secrets.keytab " , 0o644 ) # To allow access to certidude server
if os . path . exists ( " /etc/krb5.conf " ) : # Remove the one from krb5-user package
os . unlink ( " /etc/krb5.conf " )
os . symlink ( " /var/lib/samba/private/krb5.conf " , " /etc/krb5.conf " )
with open ( " /etc/resolv.conf " , " w " ) as fh :
fh . write ( " nameserver 127.0.0.1 \n search example.lan \n " )
# TODO: dig -t srv perhaps?
os . system ( " samba " )
# Samba bind 636 late (probably generating keypair)
# so LDAPS connections below will fail
timeout = 0
while timeout < 30 :
if os . path . exists ( " /var/lib/samba/private/tls/cert.pem " ) :
break
sleep ( 1 )
timeout + = 1
else :
assert False , " Samba startup timed out "
# Bootstrap authority
bootstrap_pid = os . fork ( ) # TODO: this shouldn't be necessary
if not bootstrap_pid :
result = runner . invoke ( cli , [ " setup " , " authority " , " --skip-packages " , " --elliptic-curve " ] )
assert not result . exception , result . output
return
else :
os . waitpid ( bootstrap_pid , 0 )
assert os . getuid ( ) == 0 and os . getgid ( ) == 0 , " Environment contaminated "
2017-05-07 19:51:40 +00:00
# (re)auth against DC
2017-05-07 19:11:24 +00:00
assert os . system ( " kdestroy " ) == 0
assert not os . path . exists ( " /tmp/krb5cc_0 " )
assert os . system ( " echo S4l4k4l4 | kinit administrator " ) == 0
assert os . path . exists ( " /tmp/krb5cc_0 " )
2017-05-07 19:51:40 +00:00
# Fork to not contaminate environment while creating service principal
2017-05-07 19:11:24 +00:00
spn_pid = os . fork ( )
if not spn_pid :
2017-05-07 19:51:40 +00:00
os . system ( " sed -e ' s/CA/CA \\ nkerberos method = system keytab/ ' -i /etc/samba/smb.conf " )
2017-05-07 19:11:24 +00:00
os . environ [ " KRB5_KTNAME " ] = " FILE:/etc/certidude/server.keytab "
assert os . system ( " net ads keytab add HTTP -k " ) == 0
assert os . path . exists ( " /etc/certidude/server.keytab " )
os . system ( " chown root:certidude /etc/certidude/server.keytab " )
os . system ( " chmod 640 /etc/certidude/server.keytab " )
return
else :
os . waitpid ( spn_pid , 0 )
2018-01-05 12:42:14 +00:00
r = requests . get ( " http://ca.example.lan/api/ " )
assert r . status_code == 502 , r . text
2017-05-07 19:51:40 +00:00
# Make modifications to /etc/certidude/server.conf so
# Certidude would auth against domain controller
2017-05-07 19:11:24 +00:00
os . system ( " sed -e ' s/ldap uri = ldaps:.*/ldap uri = ldaps: \\ / \\ /ca.example.lan/g ' -i /etc/certidude/server.conf " )
os . system ( " sed -e ' s/ldap uri = ldap:.*/ldap uri = ldap: \\ / \\ /ca.example.lan/g ' -i /etc/certidude/server.conf " )
2017-05-07 19:51:40 +00:00
os . system ( " sed -e ' s/dc1/ca/g ' -i /etc/cron.hourly/certidude " )
2017-05-08 16:25:59 +00:00
os . system ( " sed -e ' s/autosign subnets =.*/autosign subnets =/g ' -i /etc/certidude/server.conf " )
2018-04-27 07:48:15 +00:00
os . system ( " sed -e ' s/machine enrollment subnets =.*/machine enrollment subnets = 0.0.0.0 \\ /0/g ' -i /etc/certidude/server.conf " )
2017-06-04 14:19:29 +00:00
os . system ( " sed -e ' s/scep subnets =.*/scep subnets = 0.0.0.0 \\ /0/g ' -i /etc/certidude/server.conf " )
2018-04-27 07:48:15 +00:00
os . system ( " sed -e ' s/ocsp subnets =.*/ocsp subnets =/g ' -i /etc/certidude/server.conf " )
2017-07-07 21:07:25 +00:00
os . system ( " sed -e ' s/crl subnets =.*/crl subnets =/g ' -i /etc/certidude/server.conf " )
2017-07-05 21:22:02 +00:00
os . system ( " sed -e ' s/address = certificates@example.lan/address =/g ' -i /etc/certidude/server.conf " )
2017-06-04 14:19:29 +00:00
from certidude . common import pip
2017-05-07 19:51:40 +00:00
# Update server credential cache
with open ( " /etc/cron.hourly/certidude " ) as fh :
cronjob = fh . read ( )
assert " ldap/ca.example.lan " in cronjob , cronjob
os . system ( " /etc/cron.hourly/certidude " )
2017-05-07 19:11:24 +00:00
server_pid = os . fork ( ) # Fork to prevent environment contamination
if not server_pid :
# Apply /etc/certidude/server.conf changes
reload ( config )
reload ( user )
reload ( auth )
assert isinstance ( user . User . objects , user . ActiveDirectoryUserManager ) , user . User . objects
2017-05-07 22:14:58 +00:00
result = runner . invoke ( cli , [ ' users ' ] )
assert not result . exception , result . output
assert " user;userbot;User;Bot;userbot@example.lan " in result . output
assert " admin;adminbot;Admin;Bot;adminbot@example.lan " in result . output
assert " admin;Administrator;Administrator;;Administrator@example.lan " in result . output
2017-07-11 18:57:19 +00:00
result = runner . invoke ( cli , [ ' serve ' ] )
2017-05-07 19:11:24 +00:00
assert not result . exception , result . output
return
2018-01-05 12:42:14 +00:00
# Wait for serve to start up
for j in range ( 0 , 10 ) :
r = requests . get ( " http://ca.example.lan/api/ " )
if r . status_code != 502 :
break
sleep ( 1 )
2018-04-27 07:48:15 +00:00
assert r . status_code == 401 , " Timed out starting up the API backend "
2017-05-07 19:11:24 +00:00
2017-07-29 21:27:15 +00:00
# CRL-s disabled now
r = requests . get ( " http://ca.example.lan/api/revoked/ " )
assert r . status_code == 404 , r . text
2018-04-27 07:48:15 +00:00
# SCEP should be enabled now
2018-01-05 12:42:14 +00:00
r = requests . get ( " http://ca.example.lan/api/scep/ " )
assert r . status_code == 400
r = requests . post ( " http://ca.example.lan/api/scep/ " )
assert r . status_code == 405
2018-04-27 07:48:15 +00:00
# OCSP should be disabled now
r = requests . get ( " http://ca.example.lan/api/ocsp/ " )
assert r . status_code == 404
r = requests . post ( " http://ca.example.lan/api/ocsp/ " )
assert r . status_code == 404
2018-01-05 12:42:14 +00:00
2017-06-04 14:19:29 +00:00
2017-05-07 22:14:58 +00:00
#####################
### Kerberos auth ###
#####################
2017-05-07 19:11:24 +00:00
# TODO: pip install requests-kerberos
from requests_kerberos import HTTPKerberosAuth , OPTIONAL
auth = HTTPKerberosAuth ( mutual_authentication = OPTIONAL , force_preemptive = True )
2017-05-07 22:14:58 +00:00
# Test Kerberos auth
2017-05-07 19:11:24 +00:00
r = requests . get ( " http://ca.example.lan/api/ " )
assert r . status_code == 401 , r . text
assert " No Kerberos ticket offered " in r . text , r . text
r = requests . get ( " http://ca.example.lan/api/ " , headers = { " Authorization " : " Negotiate blerrgh " } )
assert r . status_code == 400 , r . text
2017-05-08 10:26:11 +00:00
assert " Malformed token " in r . text
r = requests . get ( " http://ca.example.lan/api/ " , headers = { " Authorization " : " Negotiate TlRMTVNTUAABAAAAl4II4gAAAAAAAAAAAAAAAAAAAAAKADk4AAAADw== " } )
assert r . status_code == 400 , r . text
assert " Unsupported authentication mechanism (NTLM " in r . text
2017-05-07 19:11:24 +00:00
r = requests . get ( " http://ca.example.lan/api/ " , auth = auth )
assert r . status_code == 200 , r . text
2017-05-07 22:14:58 +00:00
#################
### LDAP auth ###
#################
# Test LDAP bind auth fallback
usertoken = " Basic dXNlcmJvdDpTNGw0azRsNA== "
admintoken = " Basic YWRtaW5ib3Q6UzRsNGs0bDQ= "
with open ( " /etc/ldap/ldap.conf " , " w " ) as fh :
2017-05-08 10:27:27 +00:00
fh . write ( " TLS_REQCERT never \n " ) # TODO: Correct way
2017-05-07 22:14:58 +00:00
# curl http://ca.example.lan/api/ -u adminbot:S4l4k4l4 -H "User-agent: Android" -H "Referer: http://ca.example.lan"
r = requests . get ( " http://ca.example.lan/api/ " ,
headers = { " Authorization " : usertoken , " User-Agent " : " Android " , " Referer " : " http://ca.example.lan/ " } )
2017-05-08 16:49:45 +00:00
#assert r.status_code == 200, r.text # TODO: Fails with 500 in Travis
2017-05-07 22:14:58 +00:00
2017-05-08 16:25:59 +00:00
###########################
### Machine keytab auth ###
###########################
2017-05-03 21:03:51 +00:00
2017-05-08 16:25:59 +00:00
assert not os . environ . get ( " KRB5_KTNAME " ) , " Environment contaminated "
mach_pid = os . fork ( ) # Otherwise results in Terminated, needs investigation why
if not mach_pid :
clean_client ( )
# Test non-matching CN
result = runner . invoke ( cli , [ ' setup ' , ' openvpn ' , ' client ' , " -cn " , " somethingelse " , " ca.example.lan " , " vpn.example.lan " ] )
2017-05-03 07:04:52 +00:00
assert not result . exception , result . output
2017-05-08 16:25:59 +00:00
with open ( " /etc/certidude/client.conf " , " a " ) as fh :
fh . write ( " insecure = true \n " )
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ " enroll " , " --skip-self " , " --no-wait " , " --kerberos " ] )
2017-05-08 16:25:59 +00:00
assert result . exception , result . output # Bad request 400
# With matching CN it should work
clean_client ( )
result = runner . invoke ( cli , [ ' setup ' , ' openvpn ' , ' client ' , " -cn " , " ca " , " ca.example.lan " , " vpn.example.lan " ] )
assert not result . exception , result . output
with open ( " /etc/certidude/client.conf " , " a " ) as fh :
fh . write ( " insecure = true \n " )
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ " enroll " , " --skip-self " , " --no-wait " , " --kerberos " ] )
2017-05-08 16:25:59 +00:00
assert not result . exception , result . output
assert " Writing certificate to: " in result . output , result . output
2017-06-04 14:19:29 +00:00
return
2017-05-03 07:04:52 +00:00
else :
2017-05-08 16:25:59 +00:00
os . waitpid ( mach_pid , 0 )
2017-07-05 21:22:02 +00:00
##################
### SCEP tests ###
##################
2017-08-16 20:25:16 +00:00
if not os . path . exists ( " /tmp/sscep " ) :
assert not os . system ( " git clone https://github.com/certnanny/sscep /tmp/sscep " )
if not os . path . exists ( " /tmp/sscep/sscep_dyn " ) :
assert not os . system ( " cd /tmp/sscep && ./Configure && make sscep_dyn " )
2017-07-05 21:22:02 +00:00
assert not os . system ( " /tmp/sscep/sscep_dyn getca -c /tmp/sscep/ca.pem -u http://ca.example.lan/cgi-bin/pkiclient.exe " )
2017-08-16 20:25:16 +00:00
if not os . path . exists ( " /tmp/key.pem " ) :
assert not os . system ( " openssl genrsa -out /tmp/key.pem 1024 " )
if not os . path . exists ( " /tmp/req.pem " ) :
2018-04-27 07:48:15 +00:00
assert not os . system ( " echo ' . \n . \n . \n . \n Gateway \n test8 \n \n \n \n ' | openssl req -new -sha256 -key /tmp/key.pem -out /tmp/req.pem " )
2017-07-05 21:22:02 +00:00
assert not os . system ( " /tmp/sscep/sscep_dyn enroll -c /tmp/sscep/ca.pem -u http://ca.example.lan/cgi-bin/pkiclient.exe -k /tmp/key.pem -r /tmp/req.pem -l /tmp/cert.pem " )
# TODO: test e-mails at this point
2017-05-08 16:25:59 +00:00
###################
### Final tests ###
###################
2017-05-03 07:04:52 +00:00
2017-05-04 07:38:49 +00:00
2017-05-03 07:04:52 +00:00
result = runner . invoke ( cli , [ ' list ' , ' -srv ' ] )
assert not result . exception , result . output
2017-12-30 13:57:48 +00:00
result = runner . invoke ( cli , [ ' expire ' ] )
2017-05-03 07:04:52 +00:00
assert not result . exception , result . output
# Shut down server
2017-06-04 14:19:29 +00:00
assert os . path . exists ( " /proc/ %d " % server_pid )
2017-07-11 18:57:19 +00:00
os . kill ( server_pid , 15 )
2018-04-27 07:48:15 +00:00
# sleep(2)
# os.kill(server_pid, 9)
2017-05-07 19:11:24 +00:00
os . waitpid ( server_pid , 0 )
2017-05-03 07:04:52 +00:00
2017-05-06 21:07:41 +00:00
# Note: STORAGE_PATH was mangled above, hence it's /tmp not /var/lib/certidude
2017-12-30 13:57:48 +00:00
assert open ( " /etc/apparmor.d/local/usr.lib.ipsec.charon " ) . read ( ) == \
2018-04-27 07:48:15 +00:00
" /etc/certidude/authority/ca.example.lan/client_key.pem r, \n " + \
" /etc/certidude/authority/ca.example.lan/ca_cert.pem r, \n " + \
" /etc/certidude/authority/ca.example.lan/client_cert.pem r, \n "
2017-05-03 14:42:37 +00:00
assert len ( inbox ) == 0 , inbox # Make sure all messages were checked
2017-05-06 21:07:41 +00:00
os . system ( " service nginx stop " )
os . system ( " service openvpn stop " )
os . system ( " ipsec stop " )
2017-05-06 21:35:02 +00:00
clean_server ( )
2017-05-07 19:11:24 +00:00
if __name__ == " __main__ " :
test_cli_setup_authority ( )