From f793ae07a12e8dbae59ae57f4721b251ea359069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20V=C3=B5sandi?= Date: Wed, 14 Apr 2021 14:26:50 +0300 Subject: [PATCH] Fix up StrongSwan support --- Dockerfile | 6 ++-- entrypoint-openvpn.sh | 12 ++++++++ entrypoint-strongswan.sh | 12 ++++++++ entrypoint.sh | 5 ---- pinecrypt/client/cli.py | 63 +++++++++++++++++++++++++-------------- pinecrypt/client/const.py | 5 ++++ 6 files changed, 74 insertions(+), 29 deletions(-) create mode 100755 entrypoint-openvpn.sh create mode 100755 entrypoint-strongswan.sh delete mode 100755 entrypoint.sh diff --git a/Dockerfile b/Dockerfile index a37a0af..5225468 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,9 +10,11 @@ RUN echo "Dpkg::Use-Pty=0;" > /etc/apt/apt.conf.d/99quieter RUN apt-get update -qq RUN apt-get install -y -qq \ python3-pip \ - openvpn + openvpn \ + strongswan -COPY entrypoint.sh /entrypoint.sh +COPY entrypoint-openvpn.sh /entrypoint-openvpn.sh +COPY entrypoint-strongswan.sh /entrypoint-strongswan.sh COPY pinecrypt/client/. /src/pinecrypt/client COPY setup.py /src/ COPY README.md /src/ diff --git a/entrypoint-openvpn.sh b/entrypoint-openvpn.sh new file mode 100755 index 0000000..9036062 --- /dev/null +++ b/entrypoint-openvpn.sh @@ -0,0 +1,12 @@ +#!/bin/sh +sleep 10 +set -e +$@ +AUTHORITY=$3 +echo "Client config:" +cat /etc/certidude/client.conf +echo +echo "Generated VPN config:" +cat /etc/openvpn/$AUTHORITY.conf +echo +openvpn --config /etc/openvpn/$AUTHORITY.conf diff --git a/entrypoint-strongswan.sh b/entrypoint-strongswan.sh new file mode 100755 index 0000000..5fa945e --- /dev/null +++ b/entrypoint-strongswan.sh @@ -0,0 +1,12 @@ +#!/bin/sh +sleep 10 +set -e +$@ +AUTHORITY=$3 +echo "Client config:" +cat /etc/certidude/client.conf +echo +echo "Generated VPN config:" +cat /etc/ipsec.conf +echo +/usr/sbin/ipsec start --nofork diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100755 index fe7255b..0000000 --- a/entrypoint.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh -$@ -AUTHORITY=$3 -cat /etc/openvpn/$AUTHORITY.conf -openvpn --config /etc/openvpn/$AUTHORITY.conf diff --git a/pinecrypt/client/cli.py b/pinecrypt/client/cli.py index 861cbb3..eec2fc4 100644 --- a/pinecrypt/client/cli.py +++ b/pinecrypt/client/cli.py @@ -3,6 +3,7 @@ import click import hashlib import logging +import ipsecparse import json import os import random @@ -13,7 +14,6 @@ import socket import subprocess import sys import requests -from ipsecparse import loads from asn1crypto import pem, x509 from asn1crypto.csr import CertificationRequest from certbuilder import CertificateBuilder, pem_armor_certificate @@ -51,7 +51,8 @@ class ConfigTreeParser(ConfigParser): @click.command("provision", help="Add endpoint to Certidude client config") @click.argument("authority") -def certidude_provision(authority): +@click.option("-m", "--method", help="Force connection method") +def certidude_provision(authority, method): client_config = ConfigParser() try: os.makedirs(os.path.dirname(const.CLIENT_CONFIG_PATH)) @@ -71,6 +72,8 @@ def certidude_provision(authority): 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, "authority path", os.path.join(b, "ca_cert.pem")) + if method: + client_config.set(authority, "method", method) with open(const.CLIENT_CONFIG_PATH + ".part", 'w') as fh: client_config.write(fh) os.rename(const.CLIENT_CONFIG_PATH + ".part", const.CLIENT_CONFIG_PATH) @@ -80,6 +83,7 @@ def certidude_provision(authority): @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("-nw", "--no-wait", default=False, is_flag=True, help="Return immediately if server doesn't autosign") + def certidude_enroll(fork, no_wait, kerberos): try: os.makedirs(const.RUN_DIR) @@ -359,7 +363,11 @@ def certidude_enroll(fork, no_wait, kerberos): ################################## endpoint = authority_name - method = "init/openvpn" + + try: + method = clients.get(authority_name, "method") + except NoOptionError: + method = "init/openvpn" click.echo("Configuring '%s'" % endpoint) csummer = hashlib.sha1() @@ -375,6 +383,7 @@ def certidude_enroll(fork, no_wait, kerberos): fh.write("client\n") fh.write("nobind\n") fh.write("remote %s 1194 udp\n" % endpoint) + fh.write("remote %s 443 tcp\n" % endpoint) fh.write("tls-version-min 1.2\n") fh.write("tls-cipher %s\n" % bootstrap["openvpn"]["tls_cipher"]) fh.write("cipher %s\n" % bootstrap["openvpn"]["cipher"]) @@ -403,27 +412,37 @@ def certidude_enroll(fork, no_wait, kerberos): # IPSec set up with initscripts if method == "init/strongswan": - config = loads(open("%s/ipsec.conf" % const.STRONGSWAN_PREFIX).read()) - for section_type, section_name in config: - # Identify correct ipsec.conf section by leftcert - if section_type != "conn": - continue - if config[section_type,section_name]["leftcert"] != certificate_path: - continue + strongswan_config_path = os.path.join(const.STRONGSWAN_PREFIX, "ipsec.conf") + strongswan_secrets_path = os.path.join(const.STRONGSWAN_PREFIX, "ipsec.secrets") + with open(strongswan_config_path) as fh: + config = ipsecparse.loads(fh.read()) + config["ca", endpoint] = {} + config["ca", endpoint]["cacert"] = authority_path + config["ca", endpoint]["auto"] = "add" + config["conn", endpoint] = {} + config["conn", endpoint]["auto"] = "start" + config["conn", endpoint]["right"] = endpoint + config["conn", endpoint]["keyingtries"] = "%forever" + config["conn", endpoint]["dpdaction"] = "restart" + config["conn", endpoint]["closeaction"] = "restart" + config["conn", endpoint]["ike"] = "%s!" % bootstrap["strongswan"]["ike"] + config["conn", endpoint]["esp"] = "%s!" % bootstrap["strongswan"]["esp"] + config["conn", endpoint]["left"] = "%defaultroute" + config["conn", endpoint]["leftcert"] = certificate_path +# leftca="$AUTHORITY_CERTIFICATE_DISTINGUISHED_NAME" +# rightca="$AUTHORITY_CERTIFICATE_DISTINGUISHED_NAME" - if config[section_type,section_name].get("left", "") == "%defaultroute": - config[section_type,section_name]["auto"] = "start" # This is client - elif config[section_type,section_name].get("leftsourceip", ""): - config[section_type,section_name]["auto"] = "add" # This is server - else: - config[section_type,section_name]["auto"] = "route" # This is site-to-site tunnel - with open("%s/ipsec.conf.part" % const.STRONGSWAN_PREFIX, "w") as fh: - fh.write(config.dumps()) - os.rename( - "%s/ipsec.conf.part" % const.STRONGSWAN_PREFIX, - "%s/ipsec.conf" % const.STRONGSWAN_PREFIX) - break + with open(strongswan_secrets_path + ".part", "w") as fh: + fh.write(": %s %s`n" % ( + "ECDSA" if authority_public_key.algorithm == "ec" else "RSA", + key_path + )) + + with open(strongswan_config_path + ".part", "w") as fh: + fh.write(config.dumps()) + os.rename(strongswan_secrets_path + ".part", strongswan_secrets_path) + os.rename(strongswan_config_path + ".part", strongswan_config_path) # Tune AppArmor profile, TODO: retain contents if os.path.exists("/etc/apparmor.d/local"): diff --git a/pinecrypt/client/const.py b/pinecrypt/client/const.py index 3194c9e..a05a46a 100644 --- a/pinecrypt/client/const.py +++ b/pinecrypt/client/const.py @@ -20,3 +20,8 @@ try: except ValueError: # If FQDN is not configured HOSTNAME = FQDN DOMAIN = None + +if os.path.exists("/etc/strongswan/ipsec.conf"): + STRONGSWAN_PREFIX = "/etc/strongswan" +else: + STRONGSWAN_PREFIX = "/etc"