1
0
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:
Lauri Võsandi 2017-04-14 01:50:33 +03:00
commit 8bf9ebfebb
14 changed files with 139 additions and 55 deletions

View File

@ -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

View File

@ -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,

View File

@ -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__)

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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 %}
} }

View File

@ -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

View File

@ -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

View File

@ -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"