mirror of
https://github.com/laurivosandi/certidude
synced 2024-12-22 16:25:17 +00:00
Merge branch 'master' of github.com:laurivosandi/certidude
This commit is contained in:
commit
8bf9ebfebb
@ -10,7 +10,7 @@ virtualenv:
|
|||||||
install:
|
install:
|
||||||
- pip install -r requirements.txt
|
- pip install -r requirements.txt
|
||||||
- pip install --editable .
|
- pip install --editable .
|
||||||
- pip install codecov pytest-cov
|
- pip install codecov pytest-cov click ipaddress humanize falcon
|
||||||
script:
|
script:
|
||||||
- py.test --cov-report xml --cov=certidude tests/
|
- py.test --cov-report xml --cov=certidude tests/
|
||||||
cache:
|
cache:
|
||||||
@ -19,5 +19,10 @@ cache:
|
|||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
packages:
|
packages:
|
||||||
- python-ldap
|
|
||||||
- python-xattr
|
- python-xattr
|
||||||
|
- python-setproctitle
|
||||||
|
- python-markdown
|
||||||
|
- python-jinja2
|
||||||
|
- python-configparser
|
||||||
|
- python-pyasn1
|
||||||
|
- python-openssl
|
||||||
|
@ -6,7 +6,6 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import click
|
import click
|
||||||
import hashlib
|
import hashlib
|
||||||
import xattr
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from certidude import authority, mailer
|
from certidude import authority, mailer
|
||||||
@ -46,6 +45,8 @@ class SessionResource(object):
|
|||||||
@login_required
|
@login_required
|
||||||
@event_source
|
@event_source
|
||||||
def on_get(self, req, resp):
|
def on_get(self, req, resp):
|
||||||
|
import xattr
|
||||||
|
|
||||||
def serialize_requests(g):
|
def serialize_requests(g):
|
||||||
for common_name, path, buf, obj, server in g():
|
for common_name, path, buf, obj, server in g():
|
||||||
yield dict(
|
yield dict(
|
||||||
@ -126,7 +127,6 @@ class SessionResource(object):
|
|||||||
requests=serialize_requests(authority.list_requests),
|
requests=serialize_requests(authority.list_requests),
|
||||||
signed=serialize_certificates(authority.list_signed),
|
signed=serialize_certificates(authority.list_signed),
|
||||||
revoked=serialize_certificates(authority.list_revoked),
|
revoked=serialize_certificates(authority.list_revoked),
|
||||||
users=User.objects.all(),
|
|
||||||
admin_users = User.objects.filter_admins(),
|
admin_users = User.objects.filter_admins(),
|
||||||
user_subnets = config.USER_SUBNETS,
|
user_subnets = config.USER_SUBNETS,
|
||||||
autosign_subnets = config.AUTOSIGN_SUBNETS,
|
autosign_subnets = config.AUTOSIGN_SUBNETS,
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
|
|
||||||
import falcon
|
import falcon
|
||||||
import logging
|
import logging
|
||||||
from ipaddress import ip_address
|
from ipaddress import ip_address
|
||||||
from xattr import getxattr, listxattr
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from certidude import config, authority
|
from certidude import config, authority
|
||||||
from certidude.decorators import serialize
|
from certidude.decorators import serialize
|
||||||
|
from xattr import getxattr, listxattr
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
import click
|
import click
|
||||||
import falcon
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@ -22,6 +21,7 @@ if "kerberos" in config.AUTHENTICATION_BACKENDS:
|
|||||||
|
|
||||||
|
|
||||||
def authenticate(optional=False):
|
def authenticate(optional=False):
|
||||||
|
import falcon
|
||||||
def wrapper(func):
|
def wrapper(func):
|
||||||
def kerberos_authenticate(resource, req, resp, *args, **kwargs):
|
def kerberos_authenticate(resource, req, resp, *args, **kwargs):
|
||||||
# If LDAP enabled and device is not Kerberos capable fall
|
# If LDAP enabled and device is not Kerberos capable fall
|
||||||
|
@ -15,7 +15,6 @@ from cryptography.hazmat.primitives import hashes, serialization
|
|||||||
from certidude import config, push, mailer, const
|
from certidude import config, push, mailer, const
|
||||||
from certidude import errors
|
from certidude import errors
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
from xattr import getxattr, listxattr, setxattr
|
|
||||||
|
|
||||||
RE_HOSTNAME = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(@(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9]))?$"
|
RE_HOSTNAME = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(@(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9]))?$"
|
||||||
|
|
||||||
@ -259,7 +258,6 @@ def generate_pkcs12_bundle(common_name, key_size=4096, owner=None):
|
|||||||
try:
|
try:
|
||||||
from OpenSSL import crypto
|
from OpenSSL import crypto
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.error("For P12 bundles please install pyOpenSSL: pip install pyOpenSSL")
|
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
p12 = crypto.PKCS12()
|
p12 = crypto.PKCS12()
|
||||||
@ -297,6 +295,8 @@ def sign(common_name, overwrite=False):
|
|||||||
def _sign(csr, buf, overwrite=False):
|
def _sign(csr, buf, overwrite=False):
|
||||||
assert buf.startswith("-----BEGIN CERTIFICATE REQUEST-----\n")
|
assert buf.startswith("-----BEGIN CERTIFICATE REQUEST-----\n")
|
||||||
assert isinstance(csr, x509.CertificateSigningRequest)
|
assert isinstance(csr, x509.CertificateSigningRequest)
|
||||||
|
from xattr import getxattr, listxattr, setxattr
|
||||||
|
|
||||||
common_name, = csr.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
|
common_name, = csr.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
|
||||||
cert_path = os.path.join(config.SIGNED_DIR, common_name.value + ".pem")
|
cert_path = os.path.join(config.SIGNED_DIR, common_name.value + ".pem")
|
||||||
renew = False
|
renew = False
|
||||||
@ -375,5 +375,3 @@ def _sign(csr, buf, overwrite=False):
|
|||||||
push.publish("request-signed", common_name.value)
|
push.publish("request-signed", common_name.value)
|
||||||
|
|
||||||
return cert, cert_buf
|
return cert, cert_buf
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import os
|
|||||||
import pwd
|
import pwd
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import requests
|
|
||||||
import signal
|
import signal
|
||||||
import socket
|
import socket
|
||||||
import string
|
import string
|
||||||
@ -17,6 +16,7 @@ import sys
|
|||||||
from configparser import ConfigParser, NoOptionError, NoSectionError
|
from configparser import ConfigParser, NoOptionError, NoSectionError
|
||||||
from certidude.helpers import certidude_request_certificate
|
from certidude.helpers import certidude_request_certificate
|
||||||
from certidude.common import expand_paths, ip_address, ip_network
|
from certidude.common import expand_paths, ip_address, ip_network
|
||||||
|
from certidude.decorators import apt, rpm, pip
|
||||||
from cryptography import x509
|
from cryptography import x509
|
||||||
from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
|
from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
@ -24,9 +24,7 @@ from cryptography.hazmat.primitives import serialization
|
|||||||
from cryptography.hazmat.primitives import hashes, serialization
|
from cryptography.hazmat.primitives import hashes, serialization
|
||||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from humanize import naturaltime
|
|
||||||
from jinja2 import Environment, PackageLoader
|
from jinja2 import Environment, PackageLoader
|
||||||
from setproctitle import setproctitle
|
|
||||||
import const
|
import const
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -67,6 +65,8 @@ ExecStart=%s request
|
|||||||
@click.option("-r", "--renew", default=False, is_flag=True, help="Renew now")
|
@click.option("-r", "--renew", default=False, is_flag=True, help="Renew now")
|
||||||
@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")
|
||||||
def certidude_request(fork, renew):
|
def certidude_request(fork, renew):
|
||||||
|
import requests
|
||||||
|
|
||||||
if not os.path.exists(const.CLIENT_CONFIG_PATH):
|
if not os.path.exists(const.CLIENT_CONFIG_PATH):
|
||||||
click.echo("No %s!" % const.CLIENT_CONFIG_PATH)
|
click.echo("No %s!" % const.CLIENT_CONFIG_PATH)
|
||||||
return 1
|
return 1
|
||||||
@ -485,6 +485,8 @@ def certidude_setup_nginx(authority, site_config, tls_config, common_name, direc
|
|||||||
default="/etc/openvpn/client-to-site.conf",
|
default="/etc/openvpn/client-to-site.conf",
|
||||||
type=click.File(mode="w", atomic=True, lazy=True),
|
type=click.File(mode="w", atomic=True, lazy=True),
|
||||||
help="OpenVPN configuration file")
|
help="OpenVPN configuration file")
|
||||||
|
@apt("openvpn python-requests-kerberos")
|
||||||
|
@rpm("openvpn python2-requests-kerberos")
|
||||||
def certidude_setup_openvpn_client(authority, remote, config, proto):
|
def certidude_setup_openvpn_client(authority, remote, config, proto):
|
||||||
|
|
||||||
# Create corresponding section in Certidude client configuration file
|
# Create corresponding section in Certidude client configuration file
|
||||||
@ -602,6 +604,9 @@ def certidude_setup_strongswan_server(authority, config, secrets, subnet, route,
|
|||||||
@click.command("client", help="Set up strongSwan client")
|
@click.command("client", help="Set up strongSwan client")
|
||||||
@click.argument("authority")
|
@click.argument("authority")
|
||||||
@click.argument("remote")
|
@click.argument("remote")
|
||||||
|
@apt("network-manager-openvpn-gnome python-requests-kerberos")
|
||||||
|
@rpm("NetworkManager-openvpn-gnome python2-requests-kerberos")
|
||||||
|
@pip("ipsecparse")
|
||||||
def certidude_setup_strongswan_client(authority, config, remote, dpdaction):
|
def certidude_setup_strongswan_client(authority, config, remote, dpdaction):
|
||||||
# Create corresponding section in /etc/certidude/client.conf
|
# Create corresponding section in /etc/certidude/client.conf
|
||||||
client_config = ConfigParser()
|
client_config = ConfigParser()
|
||||||
@ -649,6 +654,8 @@ def certidude_setup_strongswan_client(authority, config, remote, dpdaction):
|
|||||||
@click.command("networkmanager", help="Set up strongSwan client via NetworkManager")
|
@click.command("networkmanager", help="Set up strongSwan client via NetworkManager")
|
||||||
@click.argument("authority") # Certidude server
|
@click.argument("authority") # Certidude server
|
||||||
@click.argument("remote") # StrongSwan gateway
|
@click.argument("remote") # StrongSwan gateway
|
||||||
|
@apt("strongswan-nm")
|
||||||
|
@rpm("NetworkManager-strongswan-gnome")
|
||||||
def certidude_setup_strongswan_networkmanager(authority, remote):
|
def certidude_setup_strongswan_networkmanager(authority, remote):
|
||||||
endpoint = "IPSec to %s" % remote
|
endpoint = "IPSec to %s" % remote
|
||||||
|
|
||||||
@ -744,10 +751,12 @@ def certidude_setup_openvpn_networkmanager(authority, remote):
|
|||||||
@click.option("--authority-lifetime", default=20*365, help="Authority certificate lifetime in days, 20 years by default")
|
@click.option("--authority-lifetime", default=20*365, help="Authority certificate lifetime in days, 20 years by default")
|
||||||
@click.option("--organization", "-o", default=None, help="Company or organization name")
|
@click.option("--organization", "-o", default=None, help="Company or organization name")
|
||||||
@click.option("--organizational-unit", "-ou", default=None)
|
@click.option("--organizational-unit", "-ou", default=None)
|
||||||
@click.option("--push-server", default="http://" + const.FQDN, help="Push server, by default http://%s" % const.FQDN)
|
@click.option("--push-server", help="Push server, by default http://%s" % const.FQDN)
|
||||||
@click.option("--directory", help="Directory for authority files")
|
@click.option("--directory", help="Directory for authority files")
|
||||||
@click.option("--server-flags", is_flag=True, help="Add TLS Server and IKE Intermediate extended key usage flags")
|
@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("--outbox", default="smtp://smtp.%s" % const.DOMAIN, help="SMTP server, smtp://smtp.%s by default" % const.DOMAIN)
|
||||||
|
@apt("python-setproctitle python-openssl python-falcon python-humanize python-markdown python-xattr")
|
||||||
|
@rpm("python-setproctitle pyOpenSSL python-falcon python-humanize python-markdown pyxattr")
|
||||||
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):
|
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):
|
||||||
openvpn_profile_template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates", "openvpn-client.conf")
|
openvpn_profile_template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates", "openvpn-client.conf")
|
||||||
bootstrap_template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates", "bootstrap.conf")
|
bootstrap_template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates", "bootstrap.conf")
|
||||||
@ -802,8 +811,7 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
|
|||||||
else:
|
else:
|
||||||
click.echo("Warning: /etc/krb5.keytab or /etc/samba/smb.conf not found, Kerberos unconfigured")
|
click.echo("Warning: /etc/krb5.keytab or /etc/samba/smb.conf not found, Kerberos unconfigured")
|
||||||
|
|
||||||
|
static_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), "static")
|
||||||
working_directory = os.path.realpath(os.path.dirname(__file__))
|
|
||||||
certidude_path = sys.argv[0]
|
certidude_path = sys.argv[0]
|
||||||
|
|
||||||
# Push server config generation
|
# Push server config generation
|
||||||
@ -812,15 +820,16 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
|
|||||||
listen = "0.0.0.0"
|
listen = "0.0.0.0"
|
||||||
port = "80"
|
port = "80"
|
||||||
else:
|
else:
|
||||||
nginx_client_config.write(env.get_template("nginx.conf").render(vars()))
|
port = "8080"
|
||||||
click.echo("Generated: %s" % nginx_client_config.name)
|
nginx_config.write(env.get_template("nginx.conf").render(vars()))
|
||||||
|
click.echo("Generated: %s" % nginx_config.name)
|
||||||
if not os.path.exists("/etc/nginx/sites-enabled/certidude.conf"):
|
if not os.path.exists("/etc/nginx/sites-enabled/certidude.conf"):
|
||||||
os.symlink("../sites-available/certidude.conf", "/etc/nginx/sites-enabled/certidude.conf")
|
os.symlink("../sites-available/certidude.conf", "/etc/nginx/sites-enabled/certidude.conf")
|
||||||
click.echo("Symlinked %s -> /etc/nginx/sites-enabled/" % nginx_client_config.name)
|
click.echo("Symlinked %s -> /etc/nginx/sites-enabled/" % nginx_config.name)
|
||||||
if os.path.exists("/etc/nginx/sites-enabled/default"):
|
if os.path.exists("/etc/nginx/sites-enabled/default"):
|
||||||
os.unlink("/etc/nginx/sites-enabled/default")
|
os.unlink("/etc/nginx/sites-enabled/default")
|
||||||
if not push_server:
|
if not push_server:
|
||||||
click.echo("Remember to install nchan instead of regular nginx!")
|
click.echo("Remember to install nchan capable nginx instead of regular nginx!")
|
||||||
|
|
||||||
if os.path.exists("/etc/systemd"):
|
if os.path.exists("/etc/systemd"):
|
||||||
if os.path.exists("/etc/systemd/system/certidude.service"):
|
if os.path.exists("/etc/systemd/system/certidude.service"):
|
||||||
@ -974,7 +983,7 @@ def certidude_list(verbose, show_key_type, show_extensions, show_path, show_sign
|
|||||||
# e - expired
|
# e - expired
|
||||||
# y - not valid yet
|
# y - not valid yet
|
||||||
# r - revoked
|
# r - revoked
|
||||||
|
from humanize import naturaltime
|
||||||
from certidude import authority
|
from certidude import authority
|
||||||
|
|
||||||
def dump_common(common_name, path, cert):
|
def dump_common(common_name, path, cert):
|
||||||
@ -1076,6 +1085,7 @@ def certidude_cron():
|
|||||||
@click.option("-l", "--listen", default="0.0.0.0", help="Listen address")
|
@click.option("-l", "--listen", default="0.0.0.0", help="Listen address")
|
||||||
@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")
|
||||||
def certidude_serve(port, listen, fork):
|
def certidude_serve(port, listen, fork):
|
||||||
|
from setproctitle import setproctitle
|
||||||
from certidude.signer import SignServer
|
from certidude.signer import SignServer
|
||||||
from certidude import const
|
from certidude import const
|
||||||
click.echo("Using configuration from: %s" % const.CONFIG_PATH)
|
click.echo("Using configuration from: %s" % const.CONFIG_PATH)
|
||||||
@ -1234,6 +1244,7 @@ def certidude_serve(port, listen, fork):
|
|||||||
@click.option("-s", "--slot", default="9a", help="Yubikey slot to use, 9a by default")
|
@click.option("-s", "--slot", default="9a", help="Yubikey slot to use, 9a by default")
|
||||||
@click.option("-u", "--username", default=os.getenv("USER"), help="Username to use, %s by default" % os.getenv("USER"))
|
@click.option("-u", "--username", default=os.getenv("USER"), help="Username to use, %s by default" % os.getenv("USER"))
|
||||||
def certidude_setup_yubikey(authority, slot, username, pin):
|
def certidude_setup_yubikey(authority, slot, username, pin):
|
||||||
|
import requests
|
||||||
cmd = "ykinfo", "-q", "-s"
|
cmd = "ykinfo", "-q", "-s"
|
||||||
click.echo("Executing: %s" % " ".join(cmd))
|
click.echo("Executing: %s" % " ".join(cmd))
|
||||||
serial = subprocess.check_output(cmd).strip()
|
serial = subprocess.check_output(cmd).strip()
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import click
|
import click
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
|
import sys
|
||||||
|
|
||||||
CONFIG_DIR = os.path.expanduser("~/.certidude") if os.getuid() else "/etc/certidude"
|
CONFIG_DIR = os.path.expanduser("~/.certidude") if os.getuid() else "/etc/certidude"
|
||||||
CONFIG_PATH = os.path.join(CONFIG_DIR, "server.conf")
|
CONFIG_PATH = os.path.join(CONFIG_DIR, "server.conf")
|
||||||
@ -18,7 +19,11 @@ SIGNER_LOG_PATH = os.path.join(CONFIG_DIR, "signer.log") if os.getuid() else "/v
|
|||||||
if os.getenv("TRAVIS"):
|
if os.getenv("TRAVIS"):
|
||||||
FQDN = "buildbot"
|
FQDN = "buildbot"
|
||||||
else:
|
else:
|
||||||
FQDN = socket.getaddrinfo(socket.gethostname(), 0, socket.AF_INET, 0, 0, socket.AI_CANONNAME)[0][3]
|
try:
|
||||||
|
FQDN = socket.getaddrinfo(socket.gethostname(), 0, socket.AF_INET, 0, 0, socket.AI_CANONNAME)[0][3]
|
||||||
|
except socket.gaierror:
|
||||||
|
click.echo("Failed to resolve fully qualified hostname of this machine, make sure hostname -f works")
|
||||||
|
sys.exit(254)
|
||||||
|
|
||||||
if "." in FQDN:
|
if "." in FQDN:
|
||||||
HOSTNAME, DOMAIN = FQDN.split(".", 1)
|
HOSTNAME, DOMAIN = FQDN.split(".", 1)
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import falcon
|
import click
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
import types
|
import types
|
||||||
from datetime import date, time, datetime, timedelta
|
from datetime import date, time, datetime, timedelta
|
||||||
from certidude.auth import User
|
|
||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
|
|
||||||
logger = logging.getLogger("api")
|
logger = logging.getLogger("api")
|
||||||
@ -13,6 +14,7 @@ def csrf_protection(func):
|
|||||||
"""
|
"""
|
||||||
Protect resource from common CSRF attacks by checking user agent and referrer
|
Protect resource from common CSRF attacks by checking user agent and referrer
|
||||||
"""
|
"""
|
||||||
|
import falcon
|
||||||
def wrapped(self, req, resp, *args, **kwargs):
|
def wrapped(self, req, resp, *args, **kwargs):
|
||||||
# Assume curl and python-requests are used intentionally
|
# Assume curl and python-requests are used intentionally
|
||||||
if req.user_agent.startswith("curl/") or req.user_agent.startswith("python-requests/"):
|
if req.user_agent.startswith("curl/") or req.user_agent.startswith("python-requests/"):
|
||||||
@ -40,6 +42,7 @@ def csrf_protection(func):
|
|||||||
|
|
||||||
|
|
||||||
def event_source(func):
|
def event_source(func):
|
||||||
|
import falcon
|
||||||
def wrapped(self, req, resp, *args, **kwargs):
|
def wrapped(self, req, resp, *args, **kwargs):
|
||||||
if req.get_header("Accept") == "text/event-stream":
|
if req.get_header("Accept") == "text/event-stream":
|
||||||
resp.status = falcon.HTTP_SEE_OTHER
|
resp.status = falcon.HTTP_SEE_OTHER
|
||||||
@ -50,6 +53,7 @@ def event_source(func):
|
|||||||
|
|
||||||
class MyEncoder(json.JSONEncoder):
|
class MyEncoder(json.JSONEncoder):
|
||||||
def default(self, obj):
|
def default(self, obj):
|
||||||
|
from certidude.auth import User
|
||||||
if isinstance(obj, ipaddress._IPAddressBase):
|
if isinstance(obj, ipaddress._IPAddressBase):
|
||||||
return str(obj)
|
return str(obj)
|
||||||
if isinstance(obj, set):
|
if isinstance(obj, set):
|
||||||
@ -72,6 +76,7 @@ def serialize(func):
|
|||||||
"""
|
"""
|
||||||
Falcon response serialization
|
Falcon response serialization
|
||||||
"""
|
"""
|
||||||
|
import falcon
|
||||||
def wrapped(instance, req, resp, **kwargs):
|
def wrapped(instance, req, resp, **kwargs):
|
||||||
if not req.client_accepts("application/json"):
|
if not req.client_accepts("application/json"):
|
||||||
logger.debug("Client did not accept application/json")
|
logger.debug("Client did not accept application/json")
|
||||||
@ -83,3 +88,43 @@ def serialize(func):
|
|||||||
resp.body = json.dumps(func(instance, req, resp, **kwargs), cls=MyEncoder)
|
resp.body = json.dumps(func(instance, req, resp, **kwargs), cls=MyEncoder)
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
|
def apt(packages):
|
||||||
|
"""
|
||||||
|
Install packages for Debian and Ubuntu
|
||||||
|
"""
|
||||||
|
def wrapper(func):
|
||||||
|
def wrapped(*args, **kwargs):
|
||||||
|
if os.path.exists("/usr/bin/apt-get"):
|
||||||
|
cmd = ["/usr/bin/apt-get", "install", "-yqq"] + packages.split(" ")
|
||||||
|
click.echo("Running: %s" % " ".join(cmd))
|
||||||
|
subprocess.call(cmd)
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return wrapped
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def rpm(packages):
|
||||||
|
"""
|
||||||
|
Install packages for Fedora and CentOS
|
||||||
|
"""
|
||||||
|
def wrapper(func):
|
||||||
|
def wrapped(*args, **kwargs):
|
||||||
|
if os.path.exists("/usr/bin/dnf"):
|
||||||
|
cmd = ["/usr/bin/dnf", "install", "-y"] + packages.split(" ")
|
||||||
|
click.echo("Running: %s" % " ".join(cmd))
|
||||||
|
subprocess.call(cmd)
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return wrapped
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def pip(packages):
|
||||||
|
def wrapper(func):
|
||||||
|
def wrapped(*args, **kwargs):
|
||||||
|
click.echo("Running: pip install %s" % packages)
|
||||||
|
import pip
|
||||||
|
pip.main(['install'] + packages.split(" "))
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return wrapped
|
||||||
|
return wrapper
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
|
|
||||||
import falcon
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger("api")
|
logger = logging.getLogger("api")
|
||||||
@ -8,6 +7,8 @@ def whitelist_subnets(subnets):
|
|||||||
"""
|
"""
|
||||||
Validate source IP address of API call against subnet list
|
Validate source IP address of API call against subnet list
|
||||||
"""
|
"""
|
||||||
|
import falcon
|
||||||
|
|
||||||
def wrapper(func):
|
def wrapper(func):
|
||||||
def wrapped(self, req, resp, *args, **kwargs):
|
def wrapped(self, req, resp, *args, **kwargs):
|
||||||
# Check for administration subnet whitelist
|
# Check for administration subnet whitelist
|
||||||
@ -26,6 +27,8 @@ def whitelist_subnets(subnets):
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
def whitelist_content_types(*content_types):
|
def whitelist_content_types(*content_types):
|
||||||
|
import falcon
|
||||||
|
|
||||||
def wrapper(func):
|
def wrapper(func):
|
||||||
def wrapped(self, req, resp, *args, **kwargs):
|
def wrapped(self, req, resp, *args, **kwargs):
|
||||||
for content_type in content_types:
|
for content_type in content_types:
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
import click
|
import click
|
||||||
import os
|
import os
|
||||||
import requests
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
@ -30,6 +29,7 @@ def certidude_request_certificate(server, system_keytab_required, key_path, requ
|
|||||||
"""
|
"""
|
||||||
Exchange CSR for certificate using Certidude HTTP API server
|
Exchange CSR for certificate using Certidude HTTP API server
|
||||||
"""
|
"""
|
||||||
|
import requests
|
||||||
|
|
||||||
# Create directories
|
# Create directories
|
||||||
for path in key_path, request_path, certificate_path, authority_path, revocations_path:
|
for path in key_path, request_path, certificate_path, authority_path, revocations_path:
|
||||||
|
@ -1,13 +1,29 @@
|
|||||||
|
# To set up SSL certificates using Let's Encrypt run:
|
||||||
|
#
|
||||||
|
# apt install letsencrypt
|
||||||
|
# certbot certonly -d {{common_name}} --webroot /var/www/html/
|
||||||
|
#
|
||||||
|
# Also uncomment URL rewriting and SSL configuration below
|
||||||
|
|
||||||
server {
|
server {
|
||||||
server_name {{ common_name }};
|
server_name {{ common_name }};
|
||||||
listen 80 default_server;
|
listen 80 default_server;
|
||||||
|
# rewrite ^ https://$server_name$request_uri? permanent;
|
||||||
|
#}
|
||||||
|
|
||||||
|
#server {
|
||||||
|
# server_name {{ common_name }};
|
||||||
|
# listen 443 ssl http2 default_server;
|
||||||
|
# add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
|
||||||
|
# ssl_certificate /etc/letsencrypt/live/{{common_name}}/fullchain.pem;
|
||||||
|
# ssl_certificate_key /etc/letsencrypt/live/{{common_name}}/privkey.pem;
|
||||||
|
|
||||||
error_page 500 502 503 504 /50x.html;
|
error_page 500 502 503 504 /50x.html;
|
||||||
|
|
||||||
root {{static_path}};
|
root {{static_path}};
|
||||||
|
|
||||||
location /api/ {
|
location /api/ {
|
||||||
proxy_pass http://127.0.0.1{% if listen != 80 }:{{ listen }}{% endif %}/api/;
|
proxy_pass http://127.0.0.1{% if port != 80 %}:{{ port }}{% endif %}/api/;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_connect_timeout 600;
|
proxy_connect_timeout 600;
|
||||||
@ -16,7 +32,15 @@ server {
|
|||||||
send_timeout 600;
|
send_timeout 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
{% if not push_server %}
|
# This is for Let's Encrypt
|
||||||
|
location /.well-known/ {
|
||||||
|
alias /var/www/html/.well-known/;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{% if not push_server %}
|
||||||
|
# This only works with nchan, for Debian 9 just apt install libnginx-mod-nchan
|
||||||
|
# For Ubuntu and older Debian releases install nchan from https://nchan.io/
|
||||||
location ~ "^/lp/pub/(.*)" {
|
location ~ "^/lp/pub/(.*)" {
|
||||||
allow 127.0.0.1;
|
allow 127.0.0.1;
|
||||||
nchan_publisher;
|
nchan_publisher;
|
||||||
@ -40,7 +64,7 @@ server {
|
|||||||
nchan_channel_id $1;
|
nchan_channel_id $1;
|
||||||
nchan_subscriber eventsource;
|
nchan_subscriber eventsource;
|
||||||
}
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
|
|
||||||
import click
|
import click
|
||||||
import grp
|
import grp
|
||||||
import ldap
|
|
||||||
import ldap.sasl
|
|
||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
from certidude import const, config
|
from certidude import const, config
|
||||||
@ -65,6 +63,9 @@ class PosixUserManager(object):
|
|||||||
|
|
||||||
class DirectoryConnection(object):
|
class DirectoryConnection(object):
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
|
import ldap
|
||||||
|
import ldap.sasl
|
||||||
|
|
||||||
# TODO: Implement simple bind
|
# TODO: Implement simple bind
|
||||||
if not os.path.exists(config.LDAP_GSSAPI_CRED_CACHE):
|
if not os.path.exists(config.LDAP_GSSAPI_CRED_CACHE):
|
||||||
raise ValueError("Ticket cache at %s not initialized, unable to "
|
raise ValueError("Ticket cache at %s not initialized, unable to "
|
||||||
@ -87,8 +88,7 @@ class ActiveDirectoryUserManager(object):
|
|||||||
with DirectoryConnection() as conn:
|
with DirectoryConnection() as conn:
|
||||||
ft = config.LDAP_USER_FILTER % username
|
ft = config.LDAP_USER_FILTER % username
|
||||||
attribs = "cn", "givenName", "sn", "mail", "userPrincipalName"
|
attribs = "cn", "givenName", "sn", "mail", "userPrincipalName"
|
||||||
r = conn.search_s(config.LDAP_BASE, ldap.SCOPE_SUBTREE,
|
r = conn.search_s(config.LDAP_BASE, 2, ft.encode("utf-8"), attribs)
|
||||||
ft.encode("utf-8"), attribs)
|
|
||||||
for dn, entry in r:
|
for dn, entry in r:
|
||||||
if not dn:
|
if not dn:
|
||||||
continue
|
continue
|
||||||
@ -110,8 +110,7 @@ class ActiveDirectoryUserManager(object):
|
|||||||
def filter(self, ft):
|
def filter(self, ft):
|
||||||
with DirectoryConnection() as conn:
|
with DirectoryConnection() as conn:
|
||||||
attribs = "givenName", "surname", "samaccountname", "cn", "mail", "userPrincipalName"
|
attribs = "givenName", "surname", "samaccountname", "cn", "mail", "userPrincipalName"
|
||||||
r = conn.search_s(config.LDAP_BASE, ldap.SCOPE_SUBTREE,
|
r = conn.search_s(config.LDAP_BASE, 2, ft.encode("utf-8"), attribs)
|
||||||
ft.encode("utf-8"), attribs)
|
|
||||||
for dn,entry in r:
|
for dn,entry in r:
|
||||||
if not dn:
|
if not dn:
|
||||||
continue
|
continue
|
||||||
@ -145,8 +144,7 @@ class ActiveDirectoryUserManager(object):
|
|||||||
def is_admin(self, user):
|
def is_admin(self, user):
|
||||||
with DirectoryConnection() as conn:
|
with DirectoryConnection() as conn:
|
||||||
ft = config.LDAP_ADMIN_FILTER % user.name
|
ft = config.LDAP_ADMIN_FILTER % user.name
|
||||||
r = conn.search_s(config.LDAP_BASE, ldap.SCOPE_SUBTREE,
|
r = conn.search_s(config.LDAP_BASE, 2, ft.encode("utf-8"), ["cn"])
|
||||||
ft.encode("utf-8"), ["cn"])
|
|
||||||
for dn, entry in r:
|
for dn, entry in r:
|
||||||
if not dn:
|
if not dn:
|
||||||
continue
|
continue
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
click==6.7
|
click>=6.7
|
||||||
configparser==3.5.0
|
configparser>=3.5.0
|
||||||
cryptography==1.7.2
|
cryptography>=1.7.1
|
||||||
falcon==1.1.0
|
Jinja2>=2.8
|
||||||
humanize==0.5.1
|
pyasn1>=0.1.9
|
||||||
ipaddress==1.0.18
|
requests>=2.12.4
|
||||||
Jinja2==2.9.5
|
requests-kerberos>=0.7.0
|
||||||
Markdown==2.6.8
|
|
||||||
pyldap==2.4.28
|
|
||||||
python-dateutil==2.6.0
|
|
||||||
requests==2.10.0
|
|
||||||
|
12
setup.py
12
setup.py
@ -17,15 +17,15 @@ setup(
|
|||||||
"certidude.api"
|
"certidude.api"
|
||||||
],
|
],
|
||||||
long_description=open("README.rst").read(),
|
long_description=open("README.rst").read(),
|
||||||
|
# Include here only stuff required to run certidude client
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"setproctitle",
|
|
||||||
"click",
|
"click",
|
||||||
"falcon",
|
|
||||||
"jinja2",
|
|
||||||
"pyopenssl",
|
|
||||||
"humanize",
|
|
||||||
"cryptography",
|
"cryptography",
|
||||||
"markupsafe",
|
"configparser",
|
||||||
|
"jinja2",
|
||||||
|
"pyasn1",
|
||||||
|
"requests",
|
||||||
|
"requests-kerberos"
|
||||||
],
|
],
|
||||||
scripts=[
|
scripts=[
|
||||||
"misc/certidude"
|
"misc/certidude"
|
||||||
|
Loading…
Reference in New Issue
Block a user