2018-05-17 09:00:13 +00:00
import coverage
2018-05-20 13:46:27 +00:00
import json
import os
import pytest
2017-04-25 18:47:41 +00:00
import pwd
2018-05-20 13:46:27 +00:00
import re
import shutil
import sys
2018-05-15 07:45:29 +00:00
from asn1crypto import pem , x509
2017-12-30 13:57:48 +00:00
from oscrypto import asymmetric
from csrbuilder import CSRBuilder , pem_armor_csr
2018-05-15 07:45:29 +00:00
from asn1crypto . util import OrderedDict
2017-12-30 13:57:48 +00:00
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-04-25 13:04:11 +00:00
2018-05-17 09:00:13 +00:00
coverage . process_startup ( )
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 "
2018-05-24 14:22:46 +00:00
NM_OPENVPN = """
type = vpn
[ vpn ]
service - type = org . freedesktop . NetworkManager . openvpn
connection - type = tls
comp - lzo = no
cert - pass - flags = 0
tap - dev = no
remote - cert - tls = server
remote = vpn . example . lan
key = / etc / certidude / authority / ca . example . lan / client_key . pem
cert = / etc / certidude / authority / ca . example . lan / client_cert . pem
ca = / etc / certidude / authority / ca . example . lan / ca_cert . pem
tls - cipher = TLS - ECDHE - ECDSA - WITH - AES - 256 - GCM - SHA384
cipher = AES - 128 - GCM
auth = SHA384
port = 1194
[ ipv4 ]
method = auto
never - default = true
[ ipv6 ]
method = auto
"""
NM_STRONGSWAN = """
type = vpn
[ vpn ]
service - type = org . freedesktop . NetworkManager . strongswan
encap = no
virtual = yes
method = key
ipcomp = no
address = ipsec . example . lan
userkey = / etc / certidude / authority / ca . example . lan / client_key . pem
usercert = / etc / certidude / authority / ca . example . lan / client_cert . pem
certificate = / etc / certidude / authority / ca . example . lan / ca_cert . pem
ike = aes256 - sha384 - prfsha384 - ecp384
esp = aes128gcm16 - aes128gmac - ecp384
proposal = yes
[ ipv4 ]
method = auto
"""
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
2018-05-02 08:11:01 +00:00
public_key , private_key = asymmetric . generate_pair ( ' ec ' , curve = " secp384r1 " )
2017-12-30 13:57:48 +00:00
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-05-20 13:46:27 +00:00
" /etc/certidude/client.conf.d/ca.conf " ,
" /etc/certidude/services.conf.d/ca.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 " ,
2018-05-17 09:00:13 +00:00
" /etc/NetworkManager/system-connections/IPSec to ipsec.example.lan " ,
" /etc/NetworkManager/system-connections/OpenVPN to vpn.example.lan " ,
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-05-15 07:45:29 +00:00
# Stop Samba
os . system ( " systemctl stop samba-ad-dc " )
2018-04-27 07:48:15 +00:00
os . umask ( 0o22 )
2018-05-07 11:18:29 +00:00
if os . path . exists ( " /var/lib/certidude " ) :
shutil . rmtree ( " /var/lib/certidude " )
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 " ,
2018-05-25 08:55:39 +00:00
" /etc/cron.daily/certidude " ,
2018-04-27 07:48:15 +00:00
" /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 " ,
2018-05-02 08:11:01 +00:00
" /tmp/sscep/ca.pem " ,
" /tmp/key.pem " ,
" /tmp/req.pem " ,
" /tmp/cert.pem " ,
2018-05-17 09:00:13 +00:00
" /usr/bin/node " ,
2018-04-27 07:48:15 +00:00
]
for filename in files :
2018-05-17 09:00:13 +00:00
try :
2018-04-27 07:48:15 +00:00
os . unlink ( filename )
2018-05-17 09:00:13 +00:00
except :
pass
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
2018-05-15 07:45:29 +00:00
# Remove Samba stuff
2017-05-09 09:48:24 +00:00
os . system ( " rm -Rfv /var/lib/samba/* " )
2018-05-15 07:45:29 +00:00
assert not os . path . exists ( " /var/lib/samba/private/secrets.keytab " )
2018-05-17 09:00:13 +00:00
assert not os . path . exists ( " /etc/krb5.keytab " )
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
2018-05-02 08:11:01 +00:00
def assert_cleanliness ( ) :
assert os . getuid ( ) == 0 , " Environment contaminated, UID: %d " % os . getuid ( )
assert os . getgid ( ) == 0 , " Environment contaminated, GID: %d " % os . getgid ( )
assert not os . environ . get ( " KRB5_KTNAME " ) , " Environment contaminated, KRB5_KTNAME= %s " % os . environ . get ( " KRB5_KTNAME " )
assert not os . environ . get ( " KRB5CCNAME " ) , " Environment contaminated, KRB5CCNAME= %s " % os . environ . get ( " KRB5CCNAME " )
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-05-15 07:45:29 +00:00
os . system ( " DEBIAN_FRONTEND=noninteractive apt-get install -qq -y git build-essential python-dev libkrb5-dev samba krb5-user " )
2017-08-16 21:08:20 +00:00
2018-05-02 08:11:01 +00:00
assert_cleanliness ( )
2017-05-08 16:25:59 +00:00
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/ " )
2018-05-04 08:54:55 +00:00
if not os . path . exists ( " /bin/systemctl " ) :
with open ( " /usr/bin/systemctl " , " w " ) as fh :
fh . write ( " #!/bin/bash \n " )
fh . write ( " service $2 $1 \n " )
os . chmod ( " /usr/bin/systemctl " , 0o755 )
2017-07-06 09:29:02 +00:00
# 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
2018-05-15 07:45:29 +00:00
# Bootstrap authority again with:
# - ECDSA certificates
# - POSIX auth
# - OCSP enabled
# - SCEP disabled
# - CRL enabled
2018-05-29 09:06:07 +00:00
assert os . system ( " certidude provision authority --elliptic-curve " ) == 0
2017-12-30 13:57:48 +00:00
2018-05-02 08:11:01 +00:00
assert_cleanliness ( )
2017-05-04 06:40:47 +00:00
2018-05-15 07:45:29 +00:00
assert os . path . exists ( " /var/lib/certidude/signed/ca.example.lan.pem " ) , " provisioning failed "
2017-05-03 07:04:52 +00:00
2017-08-16 20:25:16 +00:00
# Make sure nginx is running
2018-05-29 09:06:07 +00:00
os . system ( " systemctl restart certidude-backend " )
os . system ( " systemctl start certidude-ocsp-cache.service " )
2017-05-06 21:35:02 +00:00
assert os . system ( " nginx -t " ) == 0 , " invalid nginx configuration "
2018-05-17 09:00:13 +00:00
os . system ( " systemctl restart nginx " )
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
2018-05-02 08:11:01 +00:00
from certidude import config , authority , user
2017-05-04 09:14:47 +00:00
# Generate garbage
2018-05-07 11:18:29 +00:00
with open ( " /var/lib/certidude/bla " , " w " ) as fh :
2017-05-04 09:14:47 +00:00
pass
2018-05-07 11:18:29 +00:00
with open ( " /var/lib/certidude/requests/bla " , " w " ) as fh :
2017-05-04 09:14:47 +00:00
pass
2018-05-07 11:18:29 +00:00
with open ( " /var/lib/certidude/signed/bla " , " w " ) as fh :
2017-05-04 09:14:47 +00:00
pass
2018-05-07 11:18:29 +00:00
with open ( " /var/lib/certidude/revoked/bla " , " w " ) as fh :
2017-05-04 09:14:47 +00:00
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
2018-05-02 08:11:01 +00:00
assert_cleanliness ( )
2017-05-03 07:04:52 +00:00
2018-05-02 08:11:01 +00:00
import requests
for j in range ( 0 , 10 ) :
r = requests . get ( " http://ca.example.lan/api/ " )
if r . status_code != 502 :
break
sleep ( 1 )
assert r . status_code == 401 , " Timed out starting up the API backend "
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
2017-05-01 21:52:27 +00:00
# Test CA certificate fetch
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 "
2018-05-15 07:45:29 +00:00
header , _ , certificate_der_bytes = pem . unarmor ( r . text . encode ( " ascii " ) )
cert = x509 . Certificate . load ( certificate_der_bytes )
assert cert . subject . native . get ( " common_name " ) == " Certidude at ca.example.lan "
assert cert . subject . native . get ( " organizational_unit_name " ) == " Certificate Authority "
assert cert . serial_number > = 0x150000000000000000000000000000
assert cert . serial_number < = 0xfffffffffffffffffffffffffffffffffffffff
assert cert [ " tbs_certificate " ] [ " validity " ] [ " not_before " ] . native . replace ( tzinfo = None ) < datetime . utcnow ( )
assert cert [ " tbs_certificate " ] [ " validity " ] [ " not_after " ] . native . replace ( tzinfo = None ) > datetime . utcnow ( ) + timedelta ( days = 7000 )
assert cert [ " tbs_certificate " ] [ " validity " ] [ " not_before " ] . native . replace ( tzinfo = None ) < datetime . utcnow ( )
extensions = cert [ " tbs_certificate " ] [ " extensions " ] . native
assert extensions [ 0 ] == OrderedDict ( [
( ' extn_id ' , ' basic_constraints ' ) ,
( ' critical ' , True ) ,
( ' extn_value ' , OrderedDict ( [
( ' ca ' , True ) ,
( ' path_len_constraint ' , None ) ]
) ) ] ) , extensions [ 0 ]
# assert extensions[1][0] == "key_identifier", extensions[1]
assert extensions [ 2 ] == OrderedDict ( [
( ' extn_id ' , ' key_usage ' ) ,
( ' critical ' , True ) ,
( ' extn_value ' , { ' key_cert_sign ' , ' crl_sign ' } ) ] ) , extensions [ 3 ]
assert len ( extensions ) == 3
public_key = asymmetric . load_public_key ( cert [ " tbs_certificate " ] [ " subject_public_key_info " ] )
assert public_key . algorithm == " ec "
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 ( " / " )
2018-05-04 08:54:55 +00:00
assert r . status_code == 404 , r . text # backend doesn't serve static
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
2018-05-07 11:18:29 +00:00
assert os . path . exists ( " /var/lib/certidude/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
2018-05-07 11:18:29 +00:00
assert os . path . exists ( " /var/lib/certidude/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 "
2018-05-15 07:45:29 +00:00
header , _ , certificate_der_bytes = pem . unarmor ( r . text . encode ( " ascii " ) )
cert = x509 . Certificate . load ( certificate_der_bytes )
assert cert . subject . native . get ( " common_name " ) == " test "
assert cert . subject . native . get ( " organizational_unit_name " ) == " Roadwarrior "
assert cert . serial_number > = 0x150000000000000000000000000000
assert cert . serial_number < = 0xfffffffffffffffffffffffffffffffffffffff
assert cert [ " tbs_certificate " ] [ " validity " ] [ " not_before " ] . native . replace ( tzinfo = None ) < datetime . utcnow ( )
assert cert [ " tbs_certificate " ] [ " validity " ] [ " not_after " ] . native . replace ( tzinfo = None ) > datetime . utcnow ( ) + timedelta ( days = 100 )
assert cert [ " tbs_certificate " ] [ " validity " ] [ " not_before " ] . native . replace ( tzinfo = None ) < datetime . utcnow ( )
public_key = asymmetric . load_public_key ( cert [ " tbs_certificate " ] [ " subject_public_key_info " ] )
assert public_key . algorithm == " ec "
"""
extensions = cert [ " tbs_certificate " ] [ " extensions " ] . native
assert extensions [ 0 ] == OrderedDict ( [
( ' extn_id ' , ' basic_constraints ' ) ,
( ' critical ' , True ) ,
( ' extn_value ' , OrderedDict ( [
( ' ca ' , True ) ,
( ' path_len_constraint ' , None ) ]
) ) ] ) , extensions [ 0 ]
# assert extensions[1][0] == "key_identifier", extensions[1]
assert extensions [ 2 ] == OrderedDict ( [
( ' extn_id ' , ' key_usage ' ) ,
( ' critical ' , True ) ,
( ' extn_value ' , { ' key_cert_sign ' , ' crl_sign ' } ) ] ) , extensions [ 3 ]
assert len ( extensions ) == 3
"""
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-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 " ,
2018-05-07 11:18:29 +00:00
headers = { " X-SSL-CERT " : open ( " /var/lib/certidude/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
2018-05-07 11:18:29 +00:00
assert not [ j for j in listxattr ( " /var/lib/certidude/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
2018-05-07 11:18:29 +00:00
assert getxattr ( " /var/lib/certidude/signed/test.pem " , " user.machine.cpu " ) == b " i5 "
assert getxattr ( " /var/lib/certidude/signed/test.pem " , " user.machine.mem " ) == b " 512M "
assert getxattr ( " /var/lib/certidude/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-05-29 09:06:07 +00:00
assert " curl --cert-status 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
2018-05-17 09:00:13 +00:00
r = requests . get ( " http://ca.example.lan/api/signed/test/script/ " )
2017-07-06 09:29:02 +00:00
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 " ,
2018-05-07 11:18:29 +00:00
headers = { " X-SSL-CERT " : open ( " /var/lib/certidude/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 " ,
2018-05-07 11:18:29 +00:00
headers = { " X-SSL-CERT " : open ( " /var/lib/certidude/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
2017-04-25 10:52:10 +00:00
2017-04-25 21:10:12 +00:00
# Log can be read only by admin
2018-05-17 09:00:13 +00:00
r = requests . get ( " http://ca.example.lan/api/log/?limit=100 " )
2017-05-01 20:49:25 +00:00
assert r . status_code == 401 , r . text
2018-05-17 09:00:13 +00:00
r = requests . get ( " http://ca.example.lan/api/log/?limit=100 " ,
2017-04-25 21:10:12 +00:00
headers = { " Authorization " : usertoken } )
2017-05-01 20:49:25 +00:00
assert r . status_code == 403 , r . text
2018-05-17 09:00:13 +00:00
r = requests . get ( " http://ca.example.lan/api/log/?limit=100 " ,
2017-04-25 21:10:12 +00:00
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 "
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 } )
2018-05-02 08:11:01 +00:00
assert r . status_code == 403 # regular users have no access
2017-05-09 09:48:24 +00:00
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
2018-05-15 07:45:29 +00:00
# TODO: issue token, should fail because there are no routers
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
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ " provision " , " nginx " , " -cn " , " www " , " ca.example.lan " ] )
2018-05-17 09:00:13 +00:00
assert result . exception
2017-05-04 06:40:47 +00:00
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ " provision " , " nginx " , " -cn " , " www.example.lan " , " ca.example.lan " ] )
2017-05-03 14:42:37 +00:00
assert not result . exception , result . output
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ " provision " , " 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 :
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 " )
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ ' provision ' , ' openvpn ' , ' server ' , " -cn " , " vpn " , " ca.example.lan " ] )
2017-05-04 06:40:47 +00:00
assert result . exception , result . output
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ ' provision ' , ' openvpn ' , ' server ' , " -cn " , " vpn.example.lan " , " ca.example.lan " ] )
2017-05-03 21:03:51 +00:00
assert not result . exception , result . output
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ ' provision ' , ' 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 :
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
2018-05-07 11:18:29 +00:00
assert not os . path . exists ( " /var/lib/certidude/signed/vpn.example.lan.pem " )
2017-05-03 07:04:52 +00:00
child_pid = os . fork ( )
if not child_pid :
2018-05-07 11:18:29 +00:00
assert not os . path . exists ( " /var/lib/certidude/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 " )
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ ' provision ' , ' openvpn ' , ' client ' , " -cn " , " roadwarrior1 " , " ca.example.lan " , " vpn.example.lan " ] )
2017-05-03 21:03:51 +00:00
assert not result . exception , result . output
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ ' provision ' , ' 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-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
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ ' provision ' , ' openvpn ' , ' networkmanager ' , " -cn " , " roadwarrior3 " , " ca.example.lan " , " vpn.example.lan " ] )
2017-08-16 20:25:16 +00:00
assert not result . exception , result . output
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
2018-05-17 09:00:13 +00:00
assert os . path . exists ( " /etc/NetworkManager/system-connections/OpenVPN to vpn.example.lan " )
2018-05-24 14:22:46 +00:00
with open ( " /etc/NetworkManager/system-connections/OpenVPN to vpn.example.lan " ) as fh :
buf = fh . read ( )
assert buf . endswith ( NM_OPENVPN ) , buf
2017-08-16 20:25:16 +00:00
2018-05-17 09:00:13 +00:00
# Issue token, needs legit router ^
os . system ( " certidude token issue userbot " )
2018-05-20 13:46:27 +00:00
clean_client ( )
try :
os . makedirs ( " /etc/certidude/client.conf.d " )
except FileExistsError :
pass
try :
os . makedirs ( " /etc/certidude/services.conf.d " )
except FileExistsError :
pass
with open ( " /etc/certidude/client.conf.d/ca.conf " , " w " ) as fh :
fh . write ( " [ca.example.lan] \n " )
fh . write ( " trigger = interface up \n " )
fh . write ( " system wide = true \n " )
fh . write ( " common name = roadwarrior5 \n " )
fh . write ( " autosign = false \n " )
with open ( " /etc/certidude/services.conf.d/ca.conf " , " w " ) as fh :
fh . write ( " [OpenVPN to vpn.example.lan] \n " )
fh . write ( " authority = ca.example.lan \n " )
fh . write ( " remote = vpn.example.lan \n " )
fh . write ( " service = network-manager/openvpn \n " )
fh . write ( " [IPSec to ipsec.example.lan] \n " )
fh . write ( " authority = ca.example.lan \n " )
fh . write ( " remote = ipsec.example.lan \n " )
fh . write ( " service = network-manager/strongswan \n " )
assert os . system ( " certidude enroll --skip-self " ) == 0
2018-05-24 14:22:46 +00:00
2018-05-17 09:00:13 +00:00
########################
# Test image builder ###
########################
r = client ( ) . simulate_get ( " /api/build/ar150-mfp-sysupgrade/mfp-gl-ar150-squashfs-sysupgrade.bin " )
assert r . status_code == 401 , r . text
r = client ( ) . simulate_get ( " /api/build/ar150-mfp-sysupgrade/mfp-gl-ar150-squashfs-sysupgrade.bin " ,
headers = { " Authorization " : usertoken } )
assert r . status_code == 403 , r . text
r = client ( ) . simulate_get ( " /api/build/ar150-mfp-sysupgrade/mfp-gl-ar150-squashfs-sysupgrade.bin " ,
headers = { " Authorization " : admintoken } )
assert r . status_code == 200 , r . text
2018-05-15 07:45:29 +00:00
#######################
### Token mechanism ###
#######################
r = client ( ) . simulate_post ( " /api/token/ " ,
body = " username=userbot " ,
headers = { " content-type " : " application/x-www-form-urlencoded " , " Authorization " : admintoken } )
assert r . status_code == 200
2018-05-20 13:46:27 +00:00
from certidude . tokens import TokenManager
from certidude . user import User
token_manager = TokenManager ( config . TOKEN_DATABASE )
token = token_manager . issue ( None , User . objects . get ( " userbot " ) )
assert re . match ( " [A-Za-z0-9] {32} $ " , token ) , token
# TODO: submit garbage instead CSR
# Invalid common name
r = client ( ) . simulate_put ( " /api/token/ " ,
body = generate_csr ( " random " ) ,
query_string = " token= %s " % token )
assert r . status_code == 400 , r . text
# Unknown token
token = token_manager . issue ( None , User . objects . get ( " userbot " ) )
r = client ( ) . simulate_put ( " /api/token/ " ,
body = generate_csr ( " userbot@random " ) ,
query_string = " token=WpPQAgbnak84QgWjbMY4230JHi0hVYJP " )
assert r . status_code == 403 , r . text
# Correct token
r = client ( ) . simulate_put ( " /api/token/ " ,
body = generate_csr ( " userbot@random " ) ,
query_string = " token= %s " % token )
assert r . status_code == 200 , r . text
# Overwrite prohibited
token = token_manager . issue ( None , User . objects . get ( " userbot " ) )
r = client ( ) . simulate_put ( " /api/token/ " ,
body = generate_csr ( " userbot@random " ) ,
query_string = " token= %s " % token )
assert r . status_code == 409 , r . text
2018-05-15 07:45:29 +00:00
2017-08-16 20:25:16 +00:00
#################################
### 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 ( )
2018-05-07 11:18:29 +00:00
assert not os . path . exists ( " /var/lib/certidude/signed/ipsec.example.lan.pem " )
2017-05-03 21:03:51 +00:00
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ ' provision ' , ' strongswan ' , ' server ' , " -cn " , " ipsec " , " ca.example.lan " ] )
2017-05-04 06:40:47 +00:00
assert result . exception , result . output # FQDN required
2018-05-07 11:18:29 +00:00
assert not os . path . exists ( " /var/lib/certidude/signed/ipsec.example.lan.pem " )
2017-05-04 06:40:47 +00:00
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ ' provision ' , ' strongswan ' , ' server ' , " -cn " , " ipsec.example.lan " , " ca.example.lan " ] )
2017-05-03 21:03:51 +00:00
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 "
2018-05-07 11:18:29 +00:00
assert not os . path . exists ( " /var/lib/certidude/signed/ipsec.example.lan.pem " )
2017-05-03 21:03:51 +00:00
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ ' provision ' , ' 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
2018-05-07 11:18:29 +00:00
assert not os . path . exists ( " /var/lib/certidude/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 :
2018-04-27 07:48:15 +00:00
fh . write ( " autosign = false \n " )
2018-05-17 09:00:13 +00:00
fh . write ( " system wide = yes \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
2018-05-07 11:18:29 +00:00
assert not os . path . exists ( " /var/lib/certidude/signed/ipsec.example.lan.pem " )
2017-05-03 21:03:51 +00:00
child_pid = os . fork ( )
if not child_pid :
2018-05-07 11:18:29 +00:00
assert not os . path . exists ( " /var/lib/certidude/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 " )
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ ' provision ' , ' 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
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ ' provision ' , ' 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-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 ( )
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ ' provision ' , ' strongswan ' , ' networkmanager ' , " -cn " , " roadwarrior4 " , " ca.example.lan " , " ipsec.example.lan " ] )
2017-05-03 21:03:51 +00:00
assert not result . exception , result . output
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
2018-05-17 09:00:13 +00:00
assert os . path . exists ( " /etc/NetworkManager/system-connections/IPSec to ipsec.example.lan " )
2018-05-24 14:22:46 +00:00
with open ( " /etc/NetworkManager/system-connections/IPSec to ipsec.example.lan " ) as fh :
buf = fh . read ( )
assert buf . endswith ( NM_STRONGSWAN ) , buf
2017-05-04 06:40:47 +00:00
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
2018-05-07 11:18:29 +00:00
assert os . system ( " openssl ocsp -issuer /var/lib/certidude/ca_cert.pem -CAfile /var/lib/certidude/ca_cert.pem -cert /var/lib/certidude/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_cert.pem -CAfile /var/lib/certidude/ca_cert.pem -cert /var/lib/certidude/ca_cert.pem -text -url http://ca.example.lan/api/ocsp/ -out /tmp/ocsp2.log " ) == 0
2018-04-27 07:48:15 +00:00
2018-05-07 11:18:29 +00:00
for filename in os . listdir ( " /var/lib/certidude/revoked " ) :
2018-04-27 07:48:15 +00:00
if not filename . endswith ( " .pem " ) :
continue
2018-05-07 11:18:29 +00:00
assert os . system ( " openssl ocsp -issuer /var/lib/certidude/ca_cert.pem -CAfile /var/lib/certidude/ca_cert.pem -cert /var/lib/certidude/revoked/ %s -text -url http://ca.example.lan/api/ocsp/ -out /tmp/ocsp3.log " % filename ) == 0
2018-04-27 07:48:15 +00:00
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 ###
####################################
2018-05-17 09:00:13 +00:00
assert os . path . exists ( " /run/certidude/server.pid " )
pid_certidude = int ( open ( " /run/certidude/server.pid " ) . read ( ) )
2018-05-29 09:06:07 +00:00
os . system ( " systemctl stop certidude-backend " )
2018-05-17 09:00:13 +00:00
assert not os . path . exists ( " /run/certidude/server.pid " )
2017-05-07 19:11:24 +00:00
2018-04-27 07:48:15 +00:00
# Install packages
clean_server ( )
# Bootstrap domain controller here,
2018-05-15 07:45:29 +00:00
# Samba startup takes some time
2018-05-17 09:00:13 +00:00
assert not os . path . exists ( " /var/lib/samba/private/secrets.keytab " )
assert not os . path . exists ( " /etc/krb5.keytab " )
2018-04-27 07:48:15 +00:00
os . system ( " samba-tool domain provision --server-role=dc --domain=EXAMPLE --realm=EXAMPLE.LAN --host-name=ca " )
2018-05-17 09:00:13 +00:00
assert not os . path . exists ( " /run/samba/samba.pid " )
2018-05-15 07:45:29 +00:00
os . system ( " systemctl restart samba-ad-dc " )
2018-04-27 07:48:15 +00:00
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 " )
2018-05-17 09:00:13 +00:00
try :
os . symlink ( " /var/lib/samba/private/secrets.keytab " , " /etc/krb5.keytab " )
except :
pass
2018-04-27 07:48:15 +00:00
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?
2018-05-15 07:45:29 +00:00
2018-04-27 07:48:15 +00:00
# Samba bind 636 late (probably generating keypair)
# so LDAPS connections below will fail
2018-05-17 09:00:13 +00:00
timeout = 30
while timeout > 0 :
2018-04-27 07:48:15 +00:00
if os . path . exists ( " /var/lib/samba/private/tls/cert.pem " ) :
break
sleep ( 1 )
2018-05-17 09:00:13 +00:00
timeout - = 1
2018-04-27 07:48:15 +00:00
else :
assert False , " Samba startup timed out "
2018-05-17 09:00:13 +00:00
assert os . path . exists ( " /run/samba/samba.pid " )
2018-04-27 07:48:15 +00:00
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 " )
2018-05-15 07:45:29 +00:00
# Set up HTTP service principal
os . system ( " sed -e ' s/CA/CA \\ nkerberos method = system keytab/ ' -i /etc/samba/smb.conf " )
assert os . system ( " KRB5_KTNAME=FILE:/etc/certidude/server.keytab 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 " )
2017-05-07 19:11:24 +00:00
2018-05-02 08:11:01 +00:00
assert_cleanliness ( )
2018-01-05 12:42:14 +00:00
r = requests . get ( " http://ca.example.lan/api/ " )
assert r . status_code == 502 , r . text
2018-05-02 08:11:01 +00:00
2018-05-15 07:45:29 +00:00
# Bootstrap authority again with:
# - RSA certificates
# - Kerberos auth
# - SCEP enabled
# - CRL disabled
2018-05-02 08:11:01 +00:00
2018-05-07 11:18:29 +00:00
assert not os . path . exists ( " /var/lib/certidude/ca_key.pem " )
2018-05-29 09:06:07 +00:00
os . unlink ( " /etc/certidude/authority/ca.example.lan/ca_cert.pem " )
assert os . system ( " certidude provision authority --skip-packages -o ' Demola LLC ' " ) == 0
2018-05-15 07:45:29 +00:00
assert os . path . exists ( " /var/lib/certidude/ca_key.pem " )
2018-05-02 08:11:01 +00:00
2017-05-07 19:51:40 +00:00
# Make modifications to /etc/certidude/server.conf so
# Certidude would auth against domain controller
2018-05-04 08:54:55 +00:00
assert os . system ( " sed -e ' s/ldap uri = ldaps:.*/ldap uri = ldaps: \\ / \\ /ca.example.lan/g ' -i /etc/certidude/server.conf " ) == 0
assert os . system ( " sed -e ' s/ldap uri = ldap:.*/ldap uri = ldap: \\ / \\ /ca.example.lan/g ' -i /etc/certidude/server.conf " ) == 0
assert os . system ( " sed -e ' s/autosign subnets =.*/autosign subnets =/g ' -i /etc/certidude/server.conf " ) == 0
assert os . system ( " sed -e ' s/machine enrollment subnets =.*/machine enrollment subnets = 0.0.0.0 \\ /0/g ' -i /etc/certidude/server.conf " ) == 0
assert os . system ( " sed -e ' s/scep subnets =.*/scep subnets = 0.0.0.0 \\ /0/g ' -i /etc/certidude/server.conf " ) == 0
assert os . system ( " sed -e ' s/crl subnets =.*/crl subnets =/g ' -i /etc/certidude/server.conf " ) == 0
assert os . system ( " sed -e ' s/address = certificates@example.lan/address =/g ' -i /etc/certidude/server.conf " ) == 0
assert os . system ( " sed -e ' s/kerberos subnets =.*/kerberos subnets = 0.0.0.0 \\ /0/g ' -i /etc/certidude/server.conf " ) == 0
2017-05-07 19:51:40 +00:00
# Update server credential cache
2018-05-29 09:06:07 +00:00
assert os . system ( " systemctl start certidude-ldap-kinit " ) == 0
2018-05-02 08:11:01 +00:00
assert os . path . exists ( " /run/certidude/krb5cc " )
assert os . stat ( " /run/certidude/krb5cc " ) . st_uid != 0 , " Incorrect persmissions for /run/certidude/krb5cc "
2017-05-07 19:11:24 +00:00
2018-05-02 08:11:01 +00:00
# Start certidude backend
2018-05-29 09:06:07 +00:00
assert os . system ( " systemctl restart certidude-backend " ) == 0
2018-05-17 09:00:13 +00:00
cov_finished = False
for path in os . listdir ( " /tmp/ " ) :
if path . startswith ( " .coverage.ca. %d . " % pid_certidude ) :
cov_finished = True
assert cov_finished , " Didn ' t find %d in %s " % ( pid_certidude , os . listdir ( " /tmp " ) )
2018-05-02 08:11:01 +00:00
assert_cleanliness ( )
2017-05-07 22:14:58 +00:00
2018-05-02 08:11:01 +00:00
# Apply /etc/certidude/server.conf changes
reload ( config )
reload ( user )
reload ( authority )
assert authority . public_key . algorithm == " rsa "
assert isinstance ( user . User . objects , user . ActiveDirectoryUserManager ) , user . User . objects
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-05-07 22:14:58 +00:00
2017-05-07 19:11:24 +00:00
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
2017-05-07 22:14:58 +00:00
#####################
### Kerberos auth ###
#####################
2018-05-02 08:11:01 +00:00
# TODO: pip3 install requests-kerberos
assert_cleanliness ( )
assert os . stat ( " /run/certidude/krb5cc " ) . st_uid != 0 , " Incorrect persmissions for /run/certidude/krb5cc "
2017-05-07 19:11:24 +00:00
from requests_kerberos import HTTPKerberosAuth , OPTIONAL
auth = HTTPKerberosAuth ( mutual_authentication = OPTIONAL , force_preemptive = True )
2018-05-02 08:11:01 +00:00
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
2018-05-02 08:11:01 +00:00
assert os . system ( " echo S4l4k4l4 | kinit administrator " ) == 0
assert os . stat ( " /run/certidude/krb5cc " ) . st_uid != 0 , " Incorrect persmissions for /run/certidude/krb5cc "
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 ###
#################
2018-05-20 13:46:27 +00:00
# TODO: Test LDAP bind auth fallback
2017-05-07 22:14:58 +00:00
usertoken = " Basic dXNlcmJvdDpTNGw0azRsNA== "
admintoken = " Basic YWRtaW5ib3Q6UzRsNGs0bDQ= "
with open ( " /etc/ldap/ldap.conf " , " w " ) as fh :
2018-05-17 09:00:13 +00:00
fh . write ( " TLS_CACERT /var/lib/samba/private/tls/ca.pem " )
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/ " } )
2018-05-20 13:46:27 +00:00
assert r . status_code == 401 , r . text
2018-05-02 08:11:01 +00:00
assert " expected Negotiate " in r . text , r . text
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
2018-05-02 08:11:01 +00:00
assert_cleanliness ( )
2017-05-08 16:25:59 +00:00
mach_pid = os . fork ( ) # Otherwise results in Terminated, needs investigation why
if not mach_pid :
clean_client ( )
# Test non-matching CN
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ ' provision ' , ' 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
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 ( )
2018-05-29 09:06:07 +00:00
result = runner . invoke ( cli , [ ' provision ' , ' openvpn ' , ' client ' , " -cn " , " ca " , " ca.example.lan " , " vpn.example.lan " ] )
2017-05-08 16:25:59 +00:00
assert not result . exception , result . output
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 ###
##################
2018-05-02 08:11:01 +00:00
assert not os . path . exists ( " /tmp/sscep/ca.pem " )
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
2018-05-02 08:11:01 +00:00
# TODO: add strongswan scep client tests here
2017-07-05 21:22:02 +00:00
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
2018-05-17 09:00:13 +00:00
pid_certidude = int ( open ( " /run/certidude/server.pid " ) . read ( ) )
2018-05-29 09:06:07 +00:00
assert os . system ( " systemctl stop certidude-backend " ) == 0
2017-05-03 07:04:52 +00:00
2018-05-17 09:00:13 +00:00
cov_finished = False
for path in os . listdir ( " /tmp/ " ) :
if path . startswith ( " .coverage.ca. %d . " % pid_certidude ) :
cov_finished = True
assert cov_finished
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 "
2018-05-15 07:45:29 +00:00
# TODO: pop mails from /var/mail and check content
2017-05-03 14:42:37 +00:00
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
2018-05-17 09:00:13 +00:00
os . system ( " certidude token list " )
os . system ( " certidude token purge " )
2018-05-20 13:46:27 +00:00
os . system ( " certidude token purge -a " )
2018-05-17 09:00:13 +00:00
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 ( )