1
0
mirror of https://github.com/laurivosandi/certidude synced 2025-09-05 21:31:19 +00:00

Several updates #4

* Improved offline install docs
* Migrated token mechanism backend to SQL
* Preliminary token mechanism frontend integration
* Add clock skew tolerance for OCSP
* Add 'ldap computer filter' support for Kerberized machine enroll
* Include OCSP and CRL URL-s in certificates, controlled by profile.conf
* Better certificate extension handling
* Place DH parameters file in /etc/ssl/dhparam.pem
* Always talk to CA over port 8443 for 'certidude enroll'
* Hardened frontend nginx config
* Separate log files for frontend nginx
* Better provisioning heuristics
* Add sample site.sh config for LEDE image builder
* Add more device profiles for LEDE image builder
* Various bugfixes and improvements
This commit is contained in:
2018-05-15 07:45:29 +00:00
parent 728a56a975
commit ce93fbb58b
76 changed files with 1738 additions and 603 deletions

View File

@@ -1,6 +1,8 @@
import pwd
from asn1crypto import pem, x509
from oscrypto import asymmetric
from csrbuilder import CSRBuilder, pem_armor_csr
from asn1crypto.util import OrderedDict
from subprocess import check_output
from importlib import reload
from click.testing import CliRunner
@@ -86,6 +88,9 @@ def clean_client():
def clean_server():
# Stop Samba
os.system("systemctl stop samba-ad-dc")
os.umask(0o22)
if os.path.exists("/run/certidude/server.pid"):
@@ -134,14 +139,9 @@ def clean_server():
if os.path.exists("/etc/openvpn/keys"):
shutil.rmtree("/etc/openvpn/keys")
# 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
# Remove Samba stuff
os.system("rm -Rfv /var/lib/samba/*")
assert not os.path.exists("/var/lib/samba/private/secrets.keytab")
# Restore initial resolv.conf
shutil.copyfile("/etc/resolv.conf.orig", "/etc/resolv.conf")
@@ -156,7 +156,7 @@ def test_cli_setup_authority():
assert os.getuid() == 0, "Run tests as root in a clean VM or container"
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"
os.system("DEBIAN_FRONTEND=noninteractive apt-get install -qq -y git build-essential python-dev libkrb5-dev samba krb5-user winbind bc")
os.system("DEBIAN_FRONTEND=noninteractive apt-get install -qq -y git build-essential python-dev libkrb5-dev samba krb5-user")
assert_cleanliness()
@@ -211,10 +211,19 @@ def test_cli_setup_authority():
assert const.HOSTNAME == "ca"
assert const.DOMAIN == "example.lan"
# Bootstrap authority again with:
# - ECDSA certificates
# - POSIX auth
# - OCSP enabled
# - SCEP disabled
# - CRL enabled
assert os.system("certidude setup authority --elliptic-curve") == 0
assert_cleanliness()
assert os.path.exists("/var/lib/certidude/signed/ca.example.lan.pem"), "provisioning failed"
assert not os.path.exists("/etc/cron.hourly/certidude")
# Make sure nginx is running
assert os.system("nginx -t") == 0, "invalid nginx configuration"
@@ -222,12 +231,6 @@ def test_cli_setup_authority():
# Make sure we generated legit CA certificate
from certidude import config, authority, user
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)
assert authority.certificate["tbs_certificate"]["validity"]["not_before"].native.replace(tzinfo=None) < datetime.utcnow()
assert authority.public_key.algorithm == "ec"
# Generate garbage
with open("/var/lib/certidude/bla", "w") as fh:
@@ -240,7 +243,6 @@ def test_cli_setup_authority():
pass
# Start server before any signing operations are performed
config.CERTIFICATE_RENEWAL_ALLOWED = True
assert_cleanliness()
import requests
@@ -255,16 +257,40 @@ def test_cli_setup_authority():
# Test CA certificate fetch
buf = open("/var/lib/certidude/ca_cert.pem").read()
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
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"
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
# Password is bot, users created by Travis
usertoken = "Basic dXNlcmJvdDpib3Q="
@@ -419,6 +445,38 @@ def test_cli_setup_authority():
assert r.status_code == 200, r.text
assert r.headers.get('content-type') == "application/x-pem-file"
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
"""
r = client().simulate_get("/api/signed/test/", headers={"Accept":"application/json"})
assert r.status_code == 200, r.text
assert r.headers.get('content-type') == "application/json"
@@ -628,12 +686,7 @@ def test_cli_setup_authority():
assert ev_url.startswith("http://ca.example.lan/ev/sub/")
#######################
### Token mechanism ###
#######################
# TODO
# TODO: issue token, should fail because there are no routers
#############
### nginx ###
@@ -768,6 +821,19 @@ def test_cli_setup_authority():
assert "Writing certificate to:" in result.output, result.output
#######################
### 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
# TODO: check consume
#################################
### Subscribe to event source ###
#################################
@@ -1055,8 +1121,9 @@ def test_cli_setup_authority():
clean_server()
# Bootstrap domain controller here,
# Samba startup takes some timec
# Samba startup takes some time
os.system("samba-tool domain provision --server-role=dc --domain=EXAMPLE --realm=EXAMPLE.LAN --host-name=ca")
os.system("systemctl restart samba-ad-dc")
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")
@@ -1069,7 +1136,7 @@ def test_cli_setup_authority():
with open("/etc/resolv.conf", "w") as fh:
fh.write("nameserver 127.0.0.1\nsearch example.lan\n")
# TODO: dig -t srv perhaps?
os.system("samba")
# Samba bind 636 late (probably generating keypair)
# so LDAPS connections below will fail
@@ -1088,28 +1155,29 @@ def test_cli_setup_authority():
assert os.system("echo S4l4k4l4 | kinit administrator") == 0
assert os.path.exists("/tmp/krb5cc_0")
# Fork to not contaminate environment while creating service principal
spn_pid = os.fork()
if not spn_pid:
os.system("sed -e 's/CA/CA\\nkerberos method = system keytab/' -i /etc/samba/smb.conf ")
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)
# 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")
assert_cleanliness()
r = requests.get("http://ca.example.lan/api/")
assert r.status_code == 502, r.text
# Bootstrap authority again with:
# - RSA certificates
# - Kerberos auth
# - OCSP disabled
# - SCEP enabled
# - CRL disabled
# Bootstrap authority
assert not os.path.exists("/var/lib/certidude/ca_key.pem")
assert os.system("certidude setup authority --skip-packages") == 0
assert os.path.exists("/var/lib/certidude/ca_key.pem")
assert os.path.exists("/etc/cron.hourly/certidude")
# Make modifications to /etc/certidude/server.conf so
@@ -1289,12 +1357,11 @@ def test_cli_setup_authority():
assert os.system("systemctl stop certidude") == 0
# Note: STORAGE_PATH was mangled above, hence it's /tmp not /var/lib/certidude
assert open("/etc/apparmor.d/local/usr.lib.ipsec.charon").read() == \
"/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"
assert len(inbox) == 0, inbox # Make sure all messages were checked
# TODO: pop mails from /var/mail and check content
os.system("service nginx stop")
os.system("service openvpn stop")