mirror of
https://github.com/laurivosandi/certidude
synced 2024-12-22 08:15:18 +00:00
Several improvements
* Add EC support * Make token form toggleable * Make client certificates compatible with iOS native IKEv2 * Fix OU for self-enroll * Improved sample scripts in web UI
This commit is contained in:
parent
9c6872a949
commit
577962e09b
@ -10,7 +10,7 @@ from xattr import listxattr, getxattr
|
||||
from certidude.auth import login_required
|
||||
from certidude.user import User
|
||||
from certidude.decorators import serialize, csrf_protection
|
||||
from certidude import const, config
|
||||
from certidude import const, config, authority
|
||||
from .utils import AuthorityHandler
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -140,8 +140,11 @@ class SessionResource(AuthorityHandler):
|
||||
offline = 600, # Seconds from last seen activity to consider lease offline, OpenVPN reneg-sec option
|
||||
dead = 604800 # Seconds from last activity to consider lease dead, X509 chain broken or machine discarded
|
||||
),
|
||||
common_name = const.FQDN,
|
||||
title = self.authority.certificate.subject.native["common_name"],
|
||||
certificate = dict(
|
||||
algorithm = authority.public_key.algorithm,
|
||||
common_name = self.authority.certificate.subject.native["common_name"],
|
||||
blob = self.authority.certificate_buf.decode("ascii"),
|
||||
),
|
||||
mailer = dict(
|
||||
name = config.MAILER_NAME,
|
||||
address = config.MAILER_ADDRESS
|
||||
@ -164,6 +167,7 @@ class SessionResource(AuthorityHandler):
|
||||
)
|
||||
) if req.context.get("user").is_admin() else None,
|
||||
features=dict(
|
||||
token=bool(config.TOKEN_URL),
|
||||
tagging=True,
|
||||
leases=True,
|
||||
logging=config.LOGGING_BACKEND))
|
||||
|
@ -41,7 +41,8 @@ class TokenResource(AuthorityHandler):
|
||||
common_name = csr["certification_request_info"]["subject"].native["common_name"]
|
||||
assert common_name == username or common_name.startswith(username + "@"), "Invalid common name %s" % common_name
|
||||
try:
|
||||
_, resp.body = self.authority._sign(csr, body, profile="default")
|
||||
_, resp.body = self.authority._sign(csr, body, profile="default",
|
||||
overwrite=config.TOKEN_OVERWRITE_PERMITTED)
|
||||
resp.set_header("Content-Type", "application/x-pem-file")
|
||||
logger.info("Autosigned %s as proven by token ownership", common_name)
|
||||
except FileExistsError:
|
||||
|
@ -47,11 +47,16 @@ def self_enroll():
|
||||
|
||||
try:
|
||||
path, buf, cert, signed, expires = get_signed(common_name)
|
||||
public_key = asymmetric.load_public_key(path)
|
||||
self_public_key = asymmetric.load_public_key(path)
|
||||
private_key = asymmetric.load_private_key(self_key_path)
|
||||
except FileNotFoundError: # certificate or private key not found
|
||||
with open(self_key_path, 'wb') as fh:
|
||||
public_key, private_key = asymmetric.generate_pair('rsa', bit_size=2048)
|
||||
if public_key.algorithm == "ec":
|
||||
self_public_key, private_key = asymmetric.generate_pair("ec", curve=public_key.curve)
|
||||
elif public_key.algorithm == "rsa":
|
||||
self_public_key, private_key = asymmetric.generate_pair("rsa", bit_size=public_key.bit_size)
|
||||
else:
|
||||
NotImplemented
|
||||
fh.write(asymmetric.dump_private_key(private_key, None))
|
||||
else:
|
||||
now = datetime.utcnow()
|
||||
@ -59,7 +64,7 @@ def self_enroll():
|
||||
click.echo("Certificate %s still valid, delete to self-enroll again" % path)
|
||||
return
|
||||
|
||||
builder = CSRBuilder({"common_name": common_name}, public_key)
|
||||
builder = CSRBuilder({"common_name": common_name}, self_public_key)
|
||||
request = builder.build(private_key)
|
||||
with open(os.path.join(directory, "requests", common_name + ".pem"), "wb") as fh:
|
||||
fh.write(pem_armor_csr(request))
|
||||
@ -389,6 +394,7 @@ def _sign(csr, buf, skip_notify=False, skip_push=False, overwrite=False, profile
|
||||
builder.subject_alt_domains = [common_name] # OpenVPN uses CN while StrongSwan uses SAN to match hostname of the server
|
||||
builder.extended_key_usage = set(["server_auth", "1.3.6.1.5.5.8.2.2", "client_auth"])
|
||||
else:
|
||||
builder.subject_alt_domains = [common_name] # iOS demands SAN also for clients
|
||||
builder.extended_key_usage = set(["client_auth"])
|
||||
|
||||
end_entity_cert = builder.build(private_key)
|
||||
|
@ -287,7 +287,17 @@ def certidude_enroll(fork, renew, no_wait, kerberos, skip_self):
|
||||
if not os.path.exists(request_path):
|
||||
key_partial = key_path + ".part"
|
||||
request_partial = request_path + ".part"
|
||||
public_key, private_key = asymmetric.generate_pair('rsa', bit_size=2048)
|
||||
|
||||
certificate = x509.Certificate.load(certificate_der_bytes)
|
||||
public_key = asymmetric.load_public_key(certificate["tbs_certificate"]["subject_public_key_info"])
|
||||
|
||||
if public_key.algorithm == "ec":
|
||||
self_public_key, private_key = asymmetric.generate_pair("ec", curve=public_key.curve)
|
||||
elif public_key.algorithm == "rsa":
|
||||
self_public_key, private_key = asymmetric.generate_pair("rsa", bit_size=public_key.bit_size)
|
||||
else:
|
||||
NotImplemented
|
||||
|
||||
builder = CSRBuilder({"common_name": common_name}, public_key)
|
||||
request = builder.build(private_key)
|
||||
with open(key_partial, 'wb') as f:
|
||||
@ -945,8 +955,9 @@ def certidude_setup_openvpn_networkmanager(authority, remote, common_name, **pat
|
||||
@click.option("--server-flags", is_flag=True, help="Add TLS Server and IKE Intermediate extended key usage flags")
|
||||
@click.option("--outbox", default="smtp://smtp.%s" % const.DOMAIN, help="SMTP server, smtp://smtp.%s by default" % const.DOMAIN)
|
||||
@click.option("--skip-packages", is_flag=True, help="Don't attempt to install apt/pip/npm packages")
|
||||
@click.option("--elliptic-curve", "-e", is_flag=True, help="Generate EC instead of RSA keypair")
|
||||
@fqdn_required
|
||||
def certidude_setup_authority(username, kerberos_keytab, nginx_config, country, state, locality, organization, organizational_unit, common_name, directory, authority_lifetime, push_server, outbox, server_flags, title, skip_packages):
|
||||
def certidude_setup_authority(username, kerberos_keytab, nginx_config, country, state, locality, organization, organizational_unit, common_name, directory, authority_lifetime, push_server, outbox, server_flags, title, skip_packages, elliptic_curve):
|
||||
assert os.getuid() == 0 and os.getgid() == 0, "Authority can be set up only by root"
|
||||
|
||||
import pwd
|
||||
@ -1160,9 +1171,12 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
|
||||
|
||||
# Generate and sign CA key
|
||||
if not os.path.exists(ca_key):
|
||||
click.echo("Generating %d-bit RSA key for CA ..." % const.KEY_SIZE)
|
||||
|
||||
public_key, private_key = asymmetric.generate_pair('rsa', bit_size=const.KEY_SIZE)
|
||||
if elliptic_curve:
|
||||
click.echo("Generating %s EC key for CA ..." % const.CURVE_NAME)
|
||||
public_key, private_key = asymmetric.generate_pair("ec", curve=const.CURVE_NAME)
|
||||
else:
|
||||
click.echo("Generating %d-bit RSA key for CA ..." % const.KEY_SIZE)
|
||||
public_key, private_key = asymmetric.generate_pair("rsa", bit_size=const.KEY_SIZE)
|
||||
|
||||
names = (
|
||||
("country_name", country),
|
||||
|
@ -99,3 +99,6 @@ PROFILES = OrderedDict([[i, [j.strip() for j in cp.get("profile", i).split(",")]
|
||||
cp2 = configparser.RawConfigParser()
|
||||
cp2.readfp(open(const.BUILDER_CONFIG_PATH, "r"))
|
||||
IMAGE_BUILDER_PROFILES = [(j, cp2.get(j, "title"), cp2.get(j, "rename")) for j in cp2.sections()]
|
||||
|
||||
|
||||
TOKEN_OVERWRITE_PERMITTED=True
|
||||
|
@ -5,6 +5,8 @@ import socket
|
||||
import sys
|
||||
|
||||
KEY_SIZE = 1024 if os.getenv("TRAVIS") else 4096
|
||||
CURVE_NAME = "secp384r1"
|
||||
|
||||
RUN_DIR = "/run/certidude"
|
||||
CONFIG_DIR = "/etc/certidude"
|
||||
SERVER_CONFIG_PATH = os.path.join(CONFIG_DIR, "server.conf")
|
||||
|
@ -13,7 +13,7 @@
|
||||
<div class="highlight">
|
||||
<pre><code>easy_install pip;
|
||||
pip3 install certidude;
|
||||
certidude bootstrap {{session.authority.common_name}}
|
||||
certidude bootstrap {{ window.location.hostname }}
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
@ -30,14 +30,16 @@ Signature="$Windows NT$
|
||||
[NewRequest]
|
||||
Subject = "CN=$hostname"
|
||||
Exportable = FALSE
|
||||
KeyLength = 2048
|
||||
KeySpec = 1
|
||||
KeyUsage = 0xA0
|
||||
MachineKeySet = True
|
||||
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
|
||||
ProviderType = 12
|
||||
RequestType = PKCS10
|
||||
"@
|
||||
{% if session.authority.certificate.algorithm == "ec" %}ProviderName = "Microsoft Software Key Storage Provider"
|
||||
KeyAlgorithm = ECDSA_P384
|
||||
{% else %}ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
|
||||
KeyLength = 2048
|
||||
{% endif %}"@
|
||||
|
||||
$templ | Out-File req.inf
|
||||
|
||||
@ -54,8 +56,23 @@ Import-Certificate -FilePath client_cert.pem -CertStoreLocation Cert:\LocalMachi
|
||||
|
||||
# Set up IPSec VPN tunnel
|
||||
Remove-VpnConnection -AllUserConnection -Force k-space
|
||||
Add-VpnConnection -Name k-space -ServerAddress guests.k-space.ee -SplitTunneling -PassThru -TunnelType ikev2 -AllUserConnection -AuthenticationMethod MachineCertificate
|
||||
Set-VpnConnectionIPsecConfiguration -ConnectionName k-space -AuthenticationTransformConstants SHA256128 -CipherTransformConstants AES256 -EncryptionMethod AES256 -IntegrityCheckMethod SHA384 -PfsGroup PFS24 -DHGroup Group24 -PassThru -AllUserConnection -Force</code></pre>
|
||||
Add-VpnConnection `
|
||||
-Name k-space `
|
||||
-ServerAddress guests.k-space.ee `
|
||||
-AuthenticationMethod MachineCertificate `
|
||||
-SplitTunneling `
|
||||
-TunnelType ikev2 `
|
||||
-PassThru -AllUserConnection
|
||||
|
||||
# Security hardening
|
||||
Set-VpnConnectionIPsecConfiguration `
|
||||
-ConnectionName k-space `
|
||||
-AuthenticationTransformConstants GCMAES128 `
|
||||
-CipherTransformConstants GCMAES128 `
|
||||
-EncryptionMethod AES128 `
|
||||
-IntegrityCheckMethod none `
|
||||
-PfsGroup {% if session.authority.certificate.algorithm == "ec" %}ECP384 -DHGroup ECP384{% else %}PFS2048 -DHGroup Group14{% endif %} `
|
||||
-PassThru -AllUserConnection -Force</code></pre>
|
||||
</div>
|
||||
|
||||
<h5>UNIX & UNIX-like</h5>
|
||||
@ -63,58 +80,110 @@ Set-VpnConnectionIPsecConfiguration -ConnectionName k-space -AuthenticationTrans
|
||||
<p>On other UNIX-like machines generate key pair and submit the signing request using OpenSSL and cURL:</p>
|
||||
<div class="highlight">
|
||||
<pre class="code"><code>NAME=$(hostname);
|
||||
openssl genrsa -out client_key.pem 2048;
|
||||
openssl req -new -sha256 -key client_key.pem -out client_req.pem -subj "/CN=$NAME";
|
||||
{% if session.authority.certificate.algorithm == "ec" %}openssl ecparam -name secp384r1 -genkey -noout -out client_key.pem{% else %}openssl genrsa -out client_key.pem 2048;{% endif %}
|
||||
openssl req -new -sha384 -key client_key.pem -out client_req.pem -subj "/CN=$NAME";
|
||||
curl -f -L -H "Content-type: application/pkcs10" --data-binary @client_req.pem \
|
||||
http://{{ window.location.hostname }}/api/request/?wait=yes > client_cert.pem</code></pre>
|
||||
</div>
|
||||
|
||||
<h5>OpenVPN gateway on OpenWrt/LEDE router</h5>
|
||||
<h5>StrongSwan as client</h5>
|
||||
|
||||
<p>First enroll certificates:</p>
|
||||
<div class="highlight">
|
||||
<pre class="code"><code>
|
||||
FQDN=$(cat /etc/hostname)
|
||||
curl -f http://{{ window.location.hostname }}/api/certificate/ -o /etc/ipsec.d/cacerts/ca.pem; \
|
||||
test -e /etc/ipsec.d/private/client.pem \
|
||||
|| openssl ecparam -name secp384r1 -genkey -noout -out /etc/ipsec.d/private/client.pem; \
|
||||
test -e /etc/ipsec.d/reqs/client.pem \
|
||||
|| openssl req -new -sha384 \
|
||||
-key /etc/ipsec.d/private/client.pem \
|
||||
-out /etc/ipsec.d/reqs/client.pem -subj "/CN=$FQDN"; \
|
||||
curl -f -L -H "Content-type: application/pkcs10" \
|
||||
--data-binary @/etc/ipsec.d/reqs/client.pem \
|
||||
-o /etc/ipsec.d/certs/client.pem \
|
||||
http://{{ window.location.hostname }}/api/request/?wait=yes</code></pre>
|
||||
</div>
|
||||
|
||||
<p>Then configure StrongSwan</p>
|
||||
<div class="highlight">
|
||||
<pre class="code"><code>
|
||||
cat > /etc/ipsec.conf << EOF
|
||||
conn c2s
|
||||
auto=start
|
||||
right=router.k-space.ee
|
||||
dpdaction=restart
|
||||
closeaction=restart
|
||||
left=%defaultroute
|
||||
rightsubnet=0.0.0.0/0
|
||||
keyingtries=%forever
|
||||
rightid=%any
|
||||
leftsourceip=%config
|
||||
leftcert=client.pem
|
||||
ike=aes128-sha384-{% if session.authority.certificate.algorithm == "ec" %}ecp384{% else %}modp2048{% endif %}!
|
||||
esp=aes128gcm16!
|
||||
|
||||
EOF
|
||||
|
||||
echo ": {% if session.authority.certificate.algorithm == "ec" %}ECDSA{% else %}RSA{% endif %} client.pem" > /etc/ipsec.secrets
|
||||
|
||||
ipsec restart</code></pre>
|
||||
</div>
|
||||
|
||||
<h5>OpenWrt/LEDE as VPN gateway</h5>
|
||||
|
||||
<p>First enroll certificates:</p>
|
||||
<div class="highlight">
|
||||
<pre class="code"><code># Derive FQDN from WAN interface's reverse DNS record
|
||||
FQDN=$(nslookup $(uci get network.wan.ipaddr) | grep "name =" | head -n1 | cut -d "=" -f 2 | xargs)
|
||||
|
||||
mkdir -p /etc/certidude/authority/{{ window.location.hostname }}; \
|
||||
grep -c certidude /etc/sysupgrade.conf || echo /etc/certidude >> /etc/sysupgrade.conf; \
|
||||
curl -f http://{{ window.location.hostname }}/api/certificate/ -o /etc/certidude/authority/{{ window.location.hostname }}/ca_cert.pem; \
|
||||
mkdir -p /etc/certidude/authority/{{ window.location.hostname }}
|
||||
grep -c certidude /etc/sysupgrade.conf || echo /etc/certidude >> /etc/sysupgrade.conf
|
||||
cat << EOF > /etc/certidude/authority/{{ window.location.hostname }}/ca_cert.pem
|
||||
{{ session.authority.certificate.blob }}
|
||||
EOF
|
||||
test -e /etc/certidude/authority/{{ window.location.hostname }}/server_key.pem \
|
||||
|| openssl genrsa -out /etc/certidude/authority/{{ window.location.hostname }}/server_key.pem 2048; \
|
||||
|| openssl {% if session.authority.certificate.algorithm == "ec" %}ecparam -name secp384r1 -genkey -noout -out /etc/certidude/authority/{{ window.location.hostname }}/server_key.pem{% else %}genrsa -out /etc/certidude/authority/{{ window.location.hostname }}/server_key.pem 2048{% endif %}
|
||||
test -e /etc/certidude/authority/{{ window.location.hostname }}/server_req.pem \
|
||||
|| openssl req -new -sha256 \
|
||||
|| openssl req -new -sha384 \
|
||||
-key /etc/certidude/authority/{{ window.location.hostname }}/server_key.pem \
|
||||
-out /etc/certidude/authority/{{ window.location.hostname }}/server_req.pem -subj "/CN=$FQDN"; \
|
||||
-out /etc/certidude/authority/{{ window.location.hostname }}/server_req.pem -subj "/CN=$FQDN"
|
||||
cat /etc/certidude/authority/{{ window.location.hostname }}/server_req.pem
|
||||
curl -f -L -H "Content-type: application/pkcs10" \
|
||||
--data-binary @/etc/certidude/authority/{{ window.location.hostname }}/server_req.pem \
|
||||
-o /etc/certidude/authority/{{ window.location.hostname }}/server_cert.pem \
|
||||
http://{{ window.location.hostname }}/api/request/?wait=yes</code></pre>
|
||||
</div>
|
||||
http://{{ window.location.hostname }}/api/request/?wait=yes
|
||||
|
||||
<p>Then set up service:</p>
|
||||
<div class="highlight">
|
||||
<pre class="code"><code># Create VPN gateway up/down script for reporting client IP addresses to CA
|
||||
cat <<\EOF > /etc/certidude/updown
|
||||
# Create VPN gateway up/down script for reporting client IP addresses to CA
|
||||
cat <<\EOF > /etc/certidude/authority/{{ window.location.hostname }}/updown
|
||||
#!/bin/sh
|
||||
CURL="curl -f --key /etc/certidude/authority/{{ window.location.hostname }}/server_key.pem --cert /etc/certidude/authority/{{ window.location.hostname }}/server_cert.pem --cacert /etc/certidude/authority/{{ window.location.hostname }}/ca_cert.pem https://{{ window.location.hostname }}:8443/api/lease/"
|
||||
|
||||
CURL="curl -m 3 -f --key /etc/certidude/authority/{{ window.location.hostname }}/server_key.pem --cert /etc/certidude/authority/{{ window.location.hostname }}/server_cert.pem --cacert /etc/certidude/authority/{{ window.location.hostname }}/ca_cert.pem https://{{ window.location.hostname }}:8443/api/lease/"
|
||||
|
||||
case $PLUTO_VERB in
|
||||
up-client|down-client) $CURL --data "outer_address=$PLUTO_PEER&inner_address=$PLUTO_PEER_SOURCEIP&client=$PLUTO_PEER_ID" ;;
|
||||
*) $CURL --data "client=$X509_0_CN&outer_address=$untrusted_ip&inner_address=$ifconfig_pool_remote_ip&serial=$tls_serial_0" ;;
|
||||
up-client) $CURL --data-urlencode "outer_address=$PLUTO_PEER" --data-urlencode "inner_address=$PLUTO_PEER_SOURCEIP" --data-urlencode "client=$PLUTO_PEER_ID" ;;
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
case $script_type in
|
||||
client-connect) $CURL --data-urlencode client=$X509_0_CN --data-urlencode serial=$tls_serial_0 --data-urlencode outer_address=$untrusted_ip --data-urlencode inner_address=$ifconfig_pool_remote_ip ;;
|
||||
*) ;;
|
||||
esac
|
||||
EOF
|
||||
|
||||
chmod +x /etc/certidude/updown
|
||||
chmod +x /etc/certidude/authority/{{ window.location.hostname }}/updown</code></pre>
|
||||
</div>
|
||||
|
||||
|
||||
# Generate Diffie-Hellman parameters file for OpenVPN
|
||||
<p>Then either set up OpenVPN service:</p>
|
||||
<div class="highlight">
|
||||
<pre class="code"><code># Generate Diffie-Hellman parameters file for OpenVPN
|
||||
test -e /etc/certidude/dh.pem \
|
||||
|| openssl dhparam 2048 -out /etc/certidude/dh.pem
|
||||
|
||||
# Create interface definition for tunnel
|
||||
uci set network.vpn=interface
|
||||
uci set network.vpn.name='vpn'
|
||||
uci set network.vpn.ifname=tun_s2c
|
||||
uci set network.vpn.ifname=tun_s2c_udp tun_s2c_tcp
|
||||
uci set network.vpn.proto='none'
|
||||
|
||||
# Create zone definition for VPN interface
|
||||
@ -133,6 +202,14 @@ uci set firewall.openvpn.dest_port=1194
|
||||
uci set firewall.openvpn.proto='udp'
|
||||
uci set firewall.openvpn.target='ACCEPT'
|
||||
|
||||
# Allow TCP 443 on WAN interface
|
||||
uci set firewall.openvpn=rule
|
||||
uci set firewall.openvpn.name='Allow OpenVPN over TCP'
|
||||
uci set firewall.openvpn.src='wan'
|
||||
uci set firewall.openvpn.dest_port=443
|
||||
uci set firewall.openvpn.proto='tcp'
|
||||
uci set firewall.openvpn.target='ACCEPT'
|
||||
|
||||
# Forward traffic from VPN to LAN
|
||||
uci set firewall.c2s=forwarding
|
||||
uci set firewall.c2s.src='vpn'
|
||||
@ -142,31 +219,83 @@ uci set firewall.c2s.dest='lan'
|
||||
uci set dhcp.@dnsmasq[0].localservice='0'
|
||||
|
||||
touch /etc/config/openvpn
|
||||
uci set openvpn.s2c=openvpn
|
||||
uci set openvpn.s2c.local=$(uci get network.wan.ipaddr)
|
||||
uci set openvpn.s2c.script_security=2
|
||||
uci set openvpn.s2c.client_connect='/etc/certidude/updown'
|
||||
uci set openvpn.s2c.tls_version_min='1.2'
|
||||
uci set openvpn.s2c.tls_cipher='TLS-DHE-RSA-WITH-AES-256-GCM-SHA384'
|
||||
uci set openvpn.s2c.cipher='AES-256-CBC'
|
||||
uci set openvpn.s2c.auth='SHA384'
|
||||
uci set openvpn.s2c.dev=tun_s2c
|
||||
uci set openvpn.s2c.server='10.179.43.0 255.255.255.0'
|
||||
uci set openvpn.s2c.key='/etc/certidude/authority/{{ window.location.hostname }}/server_key.pem'
|
||||
uci set openvpn.s2c.cert='/etc/certidude/authority/{{ window.location.hostname }}/server_cert.pem'
|
||||
uci set openvpn.s2c.ca='/etc/certidude/authority/{{ window.location.hostname }}/ca_cert.pem'
|
||||
uci set openvpn.s2c.dh='/etc/certidude/dh.pem'
|
||||
uci set openvpn.s2c.enabled=1
|
||||
uci set openvpn.s2c.comp_lzo=yes
|
||||
uci add_list openvpn.s2c.push="route-metric 1000"
|
||||
uci add_list openvpn.s2c.push="route $(uci get network.lan.ipaddr) $(uci get network.lan.netmask)"
|
||||
uci add_list openvpn.s2c.push="dhcp-option DNS $(uci get network.lan.ipaddr)"
|
||||
uci add_list openvpn.s2c.push="dhcp-option DOMAIN $(uci get dhcp.@dnsmasq[0].domain)"
|
||||
|
||||
# Configure OpenVPN over TCP
|
||||
uci set openvpn.s2c_tcp=openvpn
|
||||
uci set openvpn.s2c_tcp.local=$(uci get network.wan.ipaddr)
|
||||
uci set openvpn.s2c_tcp.server='10.179.43.0 255.255.255.128'
|
||||
uci set openvpn.s2c_tcp.proto='tcp-server'
|
||||
uci set openvpn.s2c_tcp.port='443'
|
||||
uci set openvpn.s2c_tcp.dev=tun_s2c_tcp
|
||||
|
||||
# Configure OpenVPN over UDP
|
||||
uci set openvpn.s2c_udp=openvpn
|
||||
uci set openvpn.s2c_udp.local=$(uci get network.wan.ipaddr)
|
||||
uci set openvpn.s2c_udp.server='10.179.43.128 255.255.255.128'
|
||||
uci set openvpn.s2c_tcp.dev=tun_s2c_udp
|
||||
|
||||
for section in s2c_tcp s2c_udp; do
|
||||
|
||||
# Common paths
|
||||
uci set openvpn.$section.script_security=2
|
||||
uci set openvpn.$section.client_connect='/etc/certidude/updown'
|
||||
uci set openvpn.$section.key='/etc/certidude/authority/{{ window.location.hostname }}/server_key.pem'
|
||||
uci set openvpn.$section.cert='/etc/certidude/authority/{{ window.location.hostname }}/server_cert.pem'
|
||||
uci set openvpn.$section.ca='/etc/certidude/authority/{{ window.location.hostname }}/ca_cert.pem'
|
||||
uci set openvpn.$section.dh='/etc/certidude/dh.pem'
|
||||
uci set openvpn.$section.enabled=1
|
||||
|
||||
# DNS and routes
|
||||
uci add_list openvpn.$section.push="route-metric 1000"
|
||||
uci add_list openvpn.$section.push="route $(uci get network.lan.ipaddr) $(uci get network.lan.netmask)"
|
||||
uci add_list openvpn.$section.push="dhcp-option DNS $(uci get network.lan.ipaddr)"
|
||||
uci add_list openvpn.$section.push="dhcp-option DOMAIN $(uci get dhcp.@dnsmasq[0].domain)"
|
||||
|
||||
# Security hardening
|
||||
uci set openvpn.$section.tls_version_min='1.2'
|
||||
uci set openvpn.$section.tls_cipher='TLS-{% if session.authority.certificate.algorithm == "ec" %}ECDHE-ECDSA{% else %}DHE-RSA{% endif %}-WITH-AES-128-GCM-SHA384'
|
||||
uci set openvpn.$section.cipher='AES-128-GCM'
|
||||
uci set openvpn.$section.auth='SHA384'
|
||||
|
||||
done
|
||||
|
||||
/etc/init.d/openvpn restart
|
||||
/etc/init.d/firewall restart</code></pre>
|
||||
</div>
|
||||
|
||||
<p>Alternatively or additionally set up StrongSwan:</p>
|
||||
<div class="highlight">
|
||||
<pre class="code"><code># Generate StrongSwan config
|
||||
cat > /etc/ipsec.conf << EOF
|
||||
config setup
|
||||
strictcrlpolicy=yes
|
||||
uniqueids = yes
|
||||
|
||||
ca {{ window.location.hostname }}
|
||||
auto=add
|
||||
cacert = /etc/certidude/authority/{{ window.location.hostname }}/ca_cert.pem
|
||||
crluri = http://{{ window.location.hostname }}/api/revoked
|
||||
ocspuri = http://{{ window.location.hostname }}/api/ocsp/
|
||||
|
||||
conn s2c
|
||||
auto=add
|
||||
dpdaction=clear
|
||||
closeaction=clear
|
||||
leftdns=$(uci get network.lan.ipaddr)
|
||||
rightsourceip=172.21.0.0/24
|
||||
left=$(uci get network.wan.ipaddr)
|
||||
leftsubnet=$(uci get network.lan.ipaddr | cut -d . -f 1-3).0/24
|
||||
leftcert=/etc/certidude/authority/{{ window.location.hostname }}/server_cert.pem
|
||||
ike=aes128-sha384-{% if session.authority.certificate.algorithm == "ec" %}ecp384{% else %}modp2048{% endif %}!
|
||||
esp=aes128gcm16!
|
||||
leftupdown=/etc/certidude/{{ window.location.hostname }}/updown
|
||||
|
||||
EOF
|
||||
|
||||
echo ": {% if session.authority.certificate.algorithm == "ec" %}ECDSA{% else %}RSA{% endif %} /etc/certidude/authority/{{ window.location.hostname }}/server_key.pem" > /etc/ipsec.secrets
|
||||
|
||||
ipsec restart</code></pre>
|
||||
</div>
|
||||
|
||||
{% if session.authority.builder %}
|
||||
<h5>OpenWrt/LEDE image builder</h5>
|
||||
@ -337,28 +466,30 @@ forbidden
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
|
||||
<h1>Tokens</h1>
|
||||
|
||||
<p>Tokens allow enrolling smartphones and third party devices.</p>
|
||||
<ul>
|
||||
<li>You can issue yourself a token to be used on a mobile device</li>
|
||||
<li>Enter username to issue a token to issue a token for another user</li>
|
||||
<li>Enter e-mail address to issue a token to guest users outside domain</li>
|
||||
</ul>
|
||||
<p>
|
||||
<div class="input-group">
|
||||
<input id="token_username" name="username" type="text" class="form-control" placeholder="Username" aria-describedby="sizing-addon2">
|
||||
<input id="token_mail" name="mail" type="mail" class="form-control" placeholder="Optional e-mail" aria-describedby="sizing-addon2">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-secondary" type="button" onClick="onSendToken();"><i class="fa fa-send"></i> Send token</button>
|
||||
</span>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<div id="token_qrcode"></div>
|
||||
|
||||
|
||||
{% if session.authority %}
|
||||
{% if session.features.token %}
|
||||
<h1>Tokens</h1>
|
||||
|
||||
<p>Tokens allow enrolling smartphones and third party devices.</p>
|
||||
<ul>
|
||||
<li>You can issue yourself a token to be used on a mobile device</li>
|
||||
<li>Enter username to issue a token to issue a token for another user</li>
|
||||
<li>Enter e-mail address to issue a token to guest users outside domain</li>
|
||||
</ul>
|
||||
<p>
|
||||
<div class="input-group">
|
||||
<input id="token_username" name="username" type="text" class="form-control" placeholder="Username" aria-describedby="sizing-addon2">
|
||||
<input id="token_mail" name="mail" type="mail" class="form-control" placeholder="Optional e-mail" aria-describedby="sizing-addon2">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-secondary" type="button" onClick="onSendToken();"><i class="fa fa-send"></i> Send token</button>
|
||||
</span>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<div id="token_qrcode"></div>
|
||||
{% endif %}
|
||||
|
||||
<h1>Pending requests</h1>
|
||||
|
||||
<p>Use Certidude client to apply for a certificate.
|
||||
|
@ -39,7 +39,7 @@ class PosixUserManager(object):
|
||||
_, _, _, _, gecos, _, _ = pwd.getpwnam(username)
|
||||
gecos = gecos.split(",")
|
||||
full_name = gecos[0]
|
||||
mail = "%s@%s" % (username, const.DOMAIN)
|
||||
mail = "%s@%s" % ("tteearu", "k-space.ee") # username, "k-space.ee") # const.DOMAIN)
|
||||
if full_name and " " in full_name:
|
||||
given_name, surname = full_name.split(" ", 1)
|
||||
return User(username, mail, given_name, surname)
|
||||
|
37
doc/overlay/etc/hotplug.d/iface/50-certidude
Normal file → Executable file
37
doc/overlay/etc/hotplug.d/iface/50-certidude
Normal file → Executable file
@ -5,12 +5,13 @@
|
||||
|
||||
# TODO: renewal
|
||||
|
||||
AUTHORITY=certidude.@authority[0]
|
||||
|
||||
[ $ACTION == "ifup" ] || exit 0
|
||||
[ $INTERFACE == "wan" ] || exit 0
|
||||
[ $INTERFACE == "$(uci get $AUTHORITY.trigger)" ] || exit 0
|
||||
|
||||
# TODO: iterate over all authorities
|
||||
|
||||
AUTHORITY=certidude.@authority[0]
|
||||
URL=$(uci get $AUTHORITY.url)
|
||||
GATEWAY=$(uci get $AUTHORITY.gateway)
|
||||
|
||||
@ -40,8 +41,9 @@ logger -t certidude -s "Time is now: $(date)"
|
||||
# If certificate file is there assume everything's set up
|
||||
if [ -f $CERTIFICATE_PATH ]; then
|
||||
SERIAL=$(openssl x509 -in $CERTIFICATE_PATH -noout -serial | cut -d "=" -f 2 | tr [A-F] [a-f])
|
||||
logger -t certidude -s "Certificate with serial $SERIAL already exists, attempting to bring up IPsec tunnel..."
|
||||
ipsec up client-to-site
|
||||
logger -t certidude -s "Certificate with serial $SERIAL already exists in $CERTIFICATE_PATH, attempting to bring up VPN tunnel..."
|
||||
/etc/init.d/openvpn start
|
||||
/etc/init.d/ipsec start
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@ -60,7 +62,7 @@ fi
|
||||
if [ ! -f $KEY_PATH ]; then
|
||||
KEY_TEMP=$(mktemp -u)
|
||||
|
||||
logger -t certidude -s "Generating RSA key for IPsec..."
|
||||
logger -t certidude -s "Generating RSA key for VPN..."
|
||||
if [ -d $GREEN_LED ]; then
|
||||
echo 250 | tee $GREEN_LED/delay_*
|
||||
fi
|
||||
@ -167,31 +169,10 @@ fi
|
||||
|
||||
logger -t certidude -s "Certificate md5sum: $(md5sum -b $CERTIFICATE_TEMP)"
|
||||
|
||||
|
||||
|
||||
|
||||
###################################
|
||||
### Generate /etc/ipsec.secrets ###
|
||||
###################################
|
||||
|
||||
SECRETS_TEMP=$(mktemp -u)
|
||||
|
||||
for filename in /etc/ipsec.d/private/*.pem; do
|
||||
echo ": RSA $filename" >> $SECRETS_TEMP
|
||||
done
|
||||
|
||||
uci commit
|
||||
|
||||
mv $SECRETS_TEMP /etc/ipsec.secrets
|
||||
mv $IPSEC_TEMP /etc/ipsec.conf
|
||||
mv $CERTIFICATE_TEMP $CERTIFICATE_PATH
|
||||
|
||||
# Enable services
|
||||
/etc/init.d/ipsec enable
|
||||
|
||||
# Restart services
|
||||
/etc/init.d/ipsec restart
|
||||
|
||||
sleep 2
|
||||
|
||||
ipsec up client-to-site
|
||||
/etc/init.d/ipsec start
|
||||
/etc/init.d/openvpn start
|
||||
|
@ -14,18 +14,11 @@ export PS1='\u@\h:\w\$ '
|
||||
[ -x /usr/bin/ldd ] || ldd() { LD_TRACE_LOADED_OBJECTS=1 $*; }
|
||||
|
||||
HOSTNAME=$(uci get system.@system[0].hostname)
|
||||
DOMAIN=$(uci -q get dhcp.@dnsmasq[0].domain)
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
FQDN=$HOSTNAME.$DOMAIN
|
||||
else
|
||||
FQDN=$HOSTNAME
|
||||
fi
|
||||
|
||||
export PS1='\[\033[01;31m\]$FQDN\[\033[01;34m\] \W #\[\033[00m\] '
|
||||
export PS1='\[\033[01;31m\]$HOSTNAME\[\033[01;34m\] \W #\[\033[00m\] '
|
||||
case "$TERM" in
|
||||
xterm*|rxvt*)
|
||||
echo -ne "\033]0;${USER}@${FQDN}:${PWD}\007"
|
||||
echo -ne "\033]0;${USER}@${HOSTNAME}:${PWD}\007"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
|
@ -1,66 +0,0 @@
|
||||
# Disable DHCP servers
|
||||
/etc/init.d/odhcpd disable
|
||||
/etc/init.d/dnsmasq disable
|
||||
|
||||
# Remove firewall rules since AP bridges ethernet to wireless anyway
|
||||
uci delete firewall.@zone[1]
|
||||
uci delete firewall.@zone[0]
|
||||
uci delete firewall.@forwarding[0]
|
||||
for j in $(seq 0 10); do uci delete firewall.@rule[0]; done
|
||||
|
||||
# Remove WAN interface
|
||||
uci delete network.wan
|
||||
uci delete network.wan6
|
||||
|
||||
# Reconfigure DHCP client for bridge over LAN and WAN ports
|
||||
uci delete network.lan.ipaddr
|
||||
uci delete network.lan.netmask
|
||||
uci delete network.lan.ip6assign
|
||||
uci delete network.globals.ula_prefix
|
||||
uci delete network.@switch_vlan[1]
|
||||
uci delete dhcp.@dnsmasq[0].domain
|
||||
uci set network.lan.proto=dhcp
|
||||
uci set network.lan.ipv6=0
|
||||
uci set network.lan.ifname='eth0'
|
||||
uci set network.lan.stp=1
|
||||
|
||||
# Radio ordering differs among models
|
||||
case $(uci get wireless.radio0.hwmode) in
|
||||
11a) uci rename wireless.radio0=radio5ghz;;
|
||||
11g) uci rename wireless.radio0=radio2ghz;;
|
||||
esac
|
||||
case $(uci get wireless.radio1.hwmode) in
|
||||
11a) uci rename wireless.radio1=radio5ghz;;
|
||||
11g) uci rename wireless.radio1=radio2ghz;;
|
||||
esac
|
||||
|
||||
# Reset virtual SSID-s
|
||||
uci delete wireless.@wifi-iface[1]
|
||||
uci delete wireless.@wifi-iface[0]
|
||||
|
||||
# Pseudorandomize channel selection, should work with 80MHz on 5GHz band
|
||||
case $(uci get system.@system[0].hostname | md5sum) in
|
||||
1*|2*|3*|4*) uci set wireless.radio2ghz.channel=1; uci set wireless.radio5ghz.channel=36 ;;
|
||||
5*|6*|7*|8*) uci set wireless.radio2ghz.channel=5; uci set wireless.radio5ghz.channel=52 ;;
|
||||
9*|0*|a*|b*) uci set wireless.radio2ghz.channel=9; uci set wireless.radio5ghz.channel=100 ;;
|
||||
c*|d*|e*|f*) uci set wireless.radio2ghz.channel=13; uci set wireless.radio5ghz.channel=132 ;;
|
||||
esac
|
||||
|
||||
# Create bridge for guests
|
||||
uci set network.guest=interface
|
||||
uci set network.guest.proto='static'
|
||||
uci set network.guest.address='0.0.0.0'
|
||||
uci set network.guest.type='bridge'
|
||||
uci set network.guest.ifname='eth0.156' # tag id 156 for guest network
|
||||
uci set network.guest.ipaddr='0.0.0.0'
|
||||
uci set network.guest.ipv6=0
|
||||
uci set network.guest.stp=1
|
||||
|
||||
# Disable switch tagging and bridge all ports on TP-Link WDR3600/WDR4300
|
||||
case $(cat /etc/board.json | jsonfilter -e '@["model"]["id"]') in
|
||||
tl-wdr*)
|
||||
uci set network.@switch[0].enable_vlan=0
|
||||
uci set network.@switch_vlan[0].ports='0 1 2 3 4 5 6'
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
Loading…
Reference in New Issue
Block a user