Fix formatting issues

This commit is contained in:
Lauri Võsandi 2021-06-05 04:50:27 +00:00
parent 22290db3bb
commit d549ab4999
7 changed files with 76 additions and 72 deletions

6
.flake8 Normal file
View File

@ -0,0 +1,6 @@
[flake8]
inline-quotes = "
multiline-quotes = """
indent-size = 4
max-line-length = 160
ignore = Q003 E128 E704 E731

9
.gitlint Normal file
View File

@ -0,0 +1,9 @@
[general]
ignore=body-is-missing,T3
ignore-stdin=true
[title-match-regex]
regex=[A-Z]
[author-valid-email]
regex=[^@]+@pinecrypt.com

11
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,11 @@
repos:
- repo: https://github.com/PyCQA/flake8
rev: 3.9.2
hooks:
- id: flake8
additional_dependencies: [flake8-typing-imports==1.10.0,flake8-quotes==3.2.0]
- repo: https://github.com/jorisroovers/gitlint
rev: v0.15.1
hooks:
- id: gitlint

View File

@ -1,3 +1,5 @@
# Background # Background
Certidude is the VPN connectivity client for Pinecrypt Gateway Certidude is the VPN connectivity client for Pinecrypt Gateway.
Code snippet for installing the utility and provisioning the
connection is exposed in the Pinecrypt Gateway user interface

View File

@ -2,24 +2,17 @@
import click import click
import hashlib import hashlib
import logging
import ipsecparse import ipsecparse
import json import json
import os import os
import random
import re import re
import signal import signal
import string
import socket
import subprocess import subprocess
import sys
import requests import requests
from asn1crypto import pem, x509 from asn1crypto import pem, x509
from asn1crypto.csr import CertificationRequest from asn1crypto.csr import CertificationRequest
from certbuilder import CertificateBuilder, pem_armor_certificate
from csrbuilder import CSRBuilder, pem_armor_csr from csrbuilder import CSRBuilder, pem_armor_csr
from configparser import ConfigParser, NoOptionError from configparser import ConfigParser, NoOptionError
from datetime import datetime, timedelta
from email.utils import formatdate from email.utils import formatdate
from oscrypto import asymmetric from oscrypto import asymmetric
from pinecrypt.client import const from pinecrypt.client import const
@ -37,8 +30,9 @@ retry = Retry(
status_forcelist=(500, 502, 504), status_forcelist=(500, 502, 504),
) )
adapter = HTTPAdapter(max_retries=retry) adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter) session.mount("http://", adapter)
session.mount('https://', adapter) session.mount("https://", adapter)
def selinux_fixup(path): def selinux_fixup(path):
""" """
@ -87,19 +81,19 @@ def certidude_provision(authority, method):
client_config.set(authority, "request path", os.path.join(b, "host_req.pem")) client_config.set(authority, "request path", os.path.join(b, "host_req.pem"))
client_config.set(authority, "key path", os.path.join(b, "host_key.pem")) client_config.set(authority, "key path", os.path.join(b, "host_key.pem"))
client_config.set(authority, "certificate path", os.path.join(b, "host_cert.pem")) client_config.set(authority, "certificate path", os.path.join(b, "host_cert.pem"))
client_config.set(authority, "authority path", os.path.join(b, "ca_cert.pem")) client_config.set(authority, "authority path", os.path.join(b, "ca_cert.pem"))
if method: if method:
client_config.set(authority, "method", method) client_config.set(authority, "method", method)
with open(const.CLIENT_CONFIG_PATH + ".part", 'w') as fh: with open(const.CLIENT_CONFIG_PATH + ".part", "w") as fh:
client_config.write(fh) client_config.write(fh)
os.rename(const.CLIENT_CONFIG_PATH + ".part", const.CLIENT_CONFIG_PATH) os.rename(const.CLIENT_CONFIG_PATH + ".part", const.CLIENT_CONFIG_PATH)
os.system("certidude enroll") os.system("certidude enroll")
@click.command("enroll", help="Run processes for requesting certificates and configuring services") @click.command("enroll", help="Run processes for requesting certificates and configuring services")
@click.option("-k", "--kerberos", default=False, is_flag=True, help="Offer system keytab for auth") @click.option("-k", "--kerberos", default=False, is_flag=True, help="Offer system keytab for auth")
@click.option("-f", "--fork", default=False, is_flag=True, help="Fork to background") @click.option("-f", "--fork", default=False, is_flag=True, help="Fork to background")
@click.option("-nw", "--no-wait", default=False, is_flag=True, help="Return immediately if server doesn't autosign") @click.option("-nw", "--no-wait", default=False, is_flag=True, help="Return immediately if server doesn't autosign")
def certidude_enroll(fork, no_wait, kerberos): def certidude_enroll(fork, no_wait, kerberos):
try: try:
os.makedirs(const.RUN_DIR) os.makedirs(const.RUN_DIR)
@ -128,10 +122,9 @@ def certidude_enroll(fork, no_wait, kerberos):
else: else:
raise raise
#####################
######################### # Fork if requested #
### Fork if requested ### #####################
#########################
pid_path = os.path.join(const.RUN_DIR, authority_name + ".pid") pid_path = os.path.join(const.RUN_DIR, authority_name + ".pid")
@ -233,10 +226,9 @@ def certidude_enroll(fork, no_wait, kerberos):
if not re.match(const.RE_COMMON_NAME, common_name): if not re.match(const.RE_COMMON_NAME, common_name):
raise ValueError("Supplied common name %s doesn't match the expression %s" % (common_name, const.RE_COMMON_NAME)) raise ValueError("Supplied common name %s doesn't match the expression %s" % (common_name, const.RE_COMMON_NAME))
############################
################################ # Generate keypair and CSR #
### Generate keypair and CSR ### ############################
################################
try: try:
key_path = clients.get(authority_name, "key path") key_path = clients.get(authority_name, "key path")
@ -266,19 +258,18 @@ def certidude_enroll(fork, no_wait, kerberos):
builder = CSRBuilder({"common_name": common_name}, self_public_key) builder = CSRBuilder({"common_name": common_name}, self_public_key)
request = builder.build(private_key) request = builder.build(private_key)
with open(key_partial, 'wb') as f: with open(key_partial, "wb") as f:
f.write(asymmetric.dump_private_key(private_key, None)) f.write(asymmetric.dump_private_key(private_key, None))
with open(request_partial, 'wb') as f: with open(request_partial, "wb") as f:
f.write(pem_armor_csr(request)) f.write(pem_armor_csr(request))
selinux_fixup(key_partial) selinux_fixup(key_partial)
selinux_fixup(request_partial) selinux_fixup(request_partial)
os.rename(key_partial, key_path) os.rename(key_partial, key_path)
os.rename(request_partial, request_path) os.rename(request_partial, request_path)
##########################################
############################################## # Submit CSR and save signed certificate #
### Submit CSR and save signed certificate ### ##########################################
##############################################
try: try:
certificate_path = clients.get(authority_name, "certificate path") certificate_path = clients.get(authority_name, "certificate path")
@ -314,7 +305,7 @@ def certidude_enroll(fork, no_wait, kerberos):
except ImportError: except ImportError:
click.echo("Kerberos bindings not available, please install requests-kerberos") click.echo("Kerberos bindings not available, please install requests-kerberos")
else: else:
os.environ["KRB5CCNAME"]="/tmp/ca.ticket" os.environ["KRB5CCNAME"] = "/tmp/ca.ticket"
# Mac OS X has keytab with lowercase hostname # Mac OS X has keytab with lowercase hostname
cmd = "kinit -S HTTP/%s -k %s$" % (authority_name, const.HOSTNAME.lower()) cmd = "kinit -S HTTP/%s -k %s$" % (authority_name, const.HOSTNAME.lower())
@ -348,14 +339,15 @@ def certidude_enroll(fork, no_wait, kerberos):
if submission.status_code == requests.codes.ok: if submission.status_code == requests.codes.ok:
pass pass
if submission.status_code == requests.codes.accepted: if submission.status_code == requests.codes.accepted:
click.echo("Server accepted the request, but refused to sign immediately (%s). Waiting was not requested, hence quitting for now" % submission.text) click.echo("Server accepted the request, but refused to sign immediately (%s). "
"Waiting was not requested, hence quitting for now" % submission.text)
os.unlink(pid_path) os.unlink(pid_path)
continue continue
if submission.status_code == requests.codes.conflict: if submission.status_code == requests.codes.conflict:
raise ValueError("Different signing request with same CN is already present on server, server refuses to overwrite") raise ValueError("Different signing request with same CN is already present on server, server refuses to overwrite")
elif submission.status_code == requests.codes.gone: elif submission.status_code == requests.codes.gone:
# Should the client retry or disable request submission? # Should the client retry or disable request submission?
raise ValueError("Server refused to sign the request") # TODO: Raise proper exception raise ValueError("Server refused to sign the request") # TODO: Raise proper exception
elif submission.status_code == requests.codes.bad_request: elif submission.status_code == requests.codes.bad_request:
raise ValueError("Server said following, likely current certificate expired/revoked? %s" % submission.text) raise ValueError("Server said following, likely current certificate expired/revoked? %s" % submission.text)
else: else:
@ -363,8 +355,8 @@ def certidude_enroll(fork, no_wait, kerberos):
try: try:
header, _, certificate_der_bytes = pem.unarmor(submission.content) header, _, certificate_der_bytes = pem.unarmor(submission.content)
cert = x509.Certificate.load(certificate_der_bytes) x509.Certificate.load(certificate_der_bytes)
except: # TODO: catch correct exceptions except ValueError:
raise ValueError("Failed to parse PEM: %s" % submission.text) raise ValueError("Failed to parse PEM: %s" % submission.text)
os.umask(0o022) os.umask(0o022)
@ -380,10 +372,9 @@ def certidude_enroll(fork, no_wait, kerberos):
else: else:
click.echo("Certificate found at %s and no renewal requested" % certificate_path) click.echo("Certificate found at %s and no renewal requested" % certificate_path)
##############################
################################## # Configure related services #
### Configure related services ### ##############################
##################################
endpoint = authority_name endpoint = authority_name
@ -429,12 +420,7 @@ def certidude_enroll(fork, no_wait, kerberos):
if os.path.exists("/bin/systemctl"): if os.path.exists("/bin/systemctl"):
click.echo("Re-running systemd generators for OpenVPN...") click.echo("Re-running systemd generators for OpenVPN...")
os.system("systemctl daemon-reload") os.system("systemctl daemon-reload")
# if not os.path.exists("/etc/systemd/system/openvpn-reconnect.service"): # TODO: Restore openvpn-reconnect.service here
# with open("/etc/systemd/system/openvpn-reconnect.service.part", "w") as fh:
# fh.write(env.get_template("client/openvpn-reconnect.service").render(context))
# os.rename("/etc/systemd/system/openvpn-reconnect.service.part",
# "/etc/systemd/system/openvpn-reconnect.service")
# click.echo("Created /etc/systemd/system/openvpn-reconnect.service")
os.system("systemctl restart openvpn") os.system("systemctl restart openvpn")
continue continue
@ -458,14 +444,12 @@ def certidude_enroll(fork, no_wait, kerberos):
config["conn", endpoint]["esp"] = "%s!" % bootstrap["strongswan"]["esp"] config["conn", endpoint]["esp"] = "%s!" % bootstrap["strongswan"]["esp"]
config["conn", endpoint]["leftsourceip"] = "%config,%config6" config["conn", endpoint]["leftsourceip"] = "%config,%config6"
config["conn", endpoint]["leftcert"] = certificate_path config["conn", endpoint]["leftcert"] = certificate_path
# leftca="$AUTHORITY_CERTIFICATE_DISTINGUISHED_NAME" # TODO: Assert DN values here?
# rightca="$AUTHORITY_CERTIFICATE_DISTINGUISHED_NAME"
with open(strongswan_secrets_path + ".part", "w") as fh: with open(strongswan_secrets_path + ".part", "w") as fh:
fh.write(": %s %s\n" % ( fh.write(": %s %s\n" % (
"ECDSA" if authority_public_key.algorithm == "ec" else "RSA", "ECDSA" if authority_public_key.algorithm == "ec" else "RSA",
key_path key_path
)) ))
with open(strongswan_config_path + ".part", "w") as fh: with open(strongswan_config_path + ".part", "w") as fh:
@ -481,7 +465,7 @@ def certidude_enroll(fork, no_wait, kerberos):
fh.write(certificate_path + " r,\n") fh.write(certificate_path + " r,\n")
# Attempt to reload config or start if it's not running # Attempt to reload config or start if it's not running
if os.path.exists("/usr/sbin/strongswan"): # wtf fedora if os.path.exists("/usr/sbin/strongswan"): # wtf fedora
if os.system("strongswan update"): if os.system("strongswan update"):
os.system("strongswan start") os.system("strongswan start")
else: else:
@ -509,7 +493,7 @@ def certidude_enroll(fork, no_wait, kerberos):
nm_config.set("vpn", "comp-lzo", "no") nm_config.set("vpn", "comp-lzo", "no")
nm_config.set("vpn", "cert-pass-flags", "0") nm_config.set("vpn", "cert-pass-flags", "0")
nm_config.set("vpn", "tap-dev", "no") nm_config.set("vpn", "tap-dev", "no")
nm_config.set("vpn", "remote-cert-tls", "server") # Assert TLS Server flag of X.509 certificate nm_config.set("vpn", "remote-cert-tls", "server") # Assert TLS Server flag of X.509 certificate
nm_config.set("vpn", "remote", endpoint) nm_config.set("vpn", "remote", endpoint)
nm_config.set("vpn", "key", key_path) nm_config.set("vpn", "key", key_path)
nm_config.set("vpn", "cert", certificate_path) nm_config.set("vpn", "cert", certificate_path)
@ -537,10 +521,8 @@ def certidude_enroll(fork, no_wait, kerberos):
os.system("nmcli con up %s" % uuid) os.system("nmcli con up %s" % uuid)
continue continue
# IPSec set up with NetworkManager # IPSec set up with NetworkManager
if method == "network-manager/strongswan": if method == "network-manager/strongswan":
client_config = ConfigParser()
nm_config = ConfigParser() nm_config = ConfigParser()
nm_config.add_section("connection") nm_config.add_section("connection")
nm_config.set("connection", "certidude managed", "true") nm_config.set("connection", "certidude managed", "true")
@ -557,7 +539,7 @@ def certidude_enroll(fork, no_wait, kerberos):
nm_config.set("vpn", "userkey", key_path) nm_config.set("vpn", "userkey", key_path)
nm_config.set("vpn", "usercert", certificate_path) nm_config.set("vpn", "usercert", certificate_path)
nm_config.set("vpn", "certificate", authority_path) nm_config.set("vpn", "certificate", authority_path)
nm_config.set("vpn", "ike", bootstrap["strongswan"]["ike"]) nm_config.set("vpn", "ike", bootstrap["strongswan"]["ike"]) # TODO: Check if the ! syntax is used
nm_config.set("vpn", "esp", bootstrap["strongswan"]["esp"]) nm_config.set("vpn", "esp", bootstrap["strongswan"]["esp"])
nm_config.set("vpn", "proposal", "yes") nm_config.set("vpn", "proposal", "yes")
@ -576,7 +558,7 @@ def certidude_enroll(fork, no_wait, kerberos):
os.system("nmcli con up %s" % uuid) os.system("nmcli con up %s" % uuid)
continue continue
click.echo("Unknown service: %s" % service_config.get(endpoint, "service")) click.echo("Unknown provisioning method: %s" % method)
os.unlink(pid_path) os.unlink(pid_path)

View File

@ -4,20 +4,16 @@ import socket
RUN_DIR = "/run/certidude" RUN_DIR = "/run/certidude"
CONFIG_DIR = "/etc/certidude" CONFIG_DIR = "/etc/certidude"
CLIENT_CONFIG_PATH = os.path.join(CONFIG_DIR, "client.conf") CLIENT_CONFIG_PATH = os.path.join(CONFIG_DIR, "client.conf")
SERVICES_CONFIG_PATH = os.path.join(CONFIG_DIR, "services.conf")
RE_FQDN = "^(([a-z0-9]|[a-z0-9][a-z0-9\-_]*[a-z0-9])\.)+([a-z0-9]|[a-z0-9][a-z0-9\-_]*[a-z0-9])?$" RE_FQDN = r"^(([a-z0-9]|[a-z0-9][a-z0-9\-_]*[a-z0-9])\.)+([a-z0-9]|[a-z0-9][a-z0-9\-_]*[a-z0-9])?$"
RE_HOSTNAME = "^[a-z0-9]([a-z0-9\-_]{0,61}[a-z0-9])?$" RE_HOSTNAME = r"^[a-z0-9]([a-z0-9\-_]{0,61}[a-z0-9])?$"
RE_COMMON_NAME = "^[A-Za-z0-9\-\.\_@]+$" RE_COMMON_NAME = r"^[A-Za-z0-9\-\.\_@]+$"
try: FQDN = socket.getfqdn()
FQDN = socket.getaddrinfo(socket.gethostname(), 0, socket.AF_INET, 0, 0, socket.AI_CANONNAME)[0][3]
except socket.gaierror:
FQDN = socket.gethostname()
try: try:
HOSTNAME, DOMAIN = FQDN.split(".", 1) HOSTNAME, DOMAIN = FQDN.split(".", 1)
except ValueError: # If FQDN is not configured except ValueError: # If FQDN is not configured
HOSTNAME = FQDN HOSTNAME = FQDN
DOMAIN = None DOMAIN = None

View File

@ -1,17 +1,16 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# coding: utf-8 # coding: utf-8
import os
from setuptools import setup from setuptools import setup
setup( setup(
name = "certidude", name="certidude",
version = "0.2.1", version="0.2.1",
author = u"Pinecrypt Labs", author=u"Pinecrypt Labs",
author_email = "lauri@pinecrypt.com", author_email="lauri@pinecrypt.com",
description = "Certidude provisions VPN connections to Pinecrypt Gateway", description="Certidude provisions VPN connections to Pinecrypt Gateway",
license = "MIT", license="MIT",
keywords = "falcon http jinja2 x509 pkcs11 webcrypto kerberos ldap", keywords="falcon http jinja2 x509 pkcs11 webcrypto kerberos ldap",
url = "https://git.k-space.ee/pinecrypt/certidude", url="https://git.k-space.ee/pinecrypt/certidude",
packages=[ packages=[
"pinecrypt.client", "pinecrypt.client",
], ],
@ -40,4 +39,3 @@ setup(
"Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3 :: Only",
], ],
) )