mirror of
https://github.com/laurivosandi/certidude
synced 2024-12-23 00:25:18 +00:00
Add preliminary Python 2.x support
This commit is contained in:
parent
5eed7cb6d9
commit
4240d55fe4
@ -123,18 +123,19 @@ def certidude_app():
|
||||
message = record.msg % record.args,
|
||||
severity = record.levelname.lower()))
|
||||
|
||||
sql_handler = MySQLLogHandler(config.DATABASE_POOL)
|
||||
if config.DATABASE_POOL:
|
||||
sql_handler = MySQLLogHandler(config.DATABASE_POOL)
|
||||
push_handler = PushLogHandler()
|
||||
|
||||
for facility in "api", "cli":
|
||||
logger = logging.getLogger(facility)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.addHandler(sql_handler)
|
||||
if config.DATABASE_POOL:
|
||||
logger.addHandler(sql_handler)
|
||||
logger.addHandler(push_handler)
|
||||
|
||||
|
||||
logging.getLogger("cli").debug("Started Certidude at %s",
|
||||
socket.getaddrinfo(socket.gethostname(), 0, flags=socket.AI_CANONNAME)[0][3])
|
||||
logging.getLogger("cli").debug("Started Certidude at %s", config.FQDN)
|
||||
|
||||
import atexit
|
||||
|
||||
|
@ -22,7 +22,7 @@ class RequestListResource(object):
|
||||
Submit certificate signing request (CSR) in PEM format
|
||||
"""
|
||||
# Parse remote IPv4/IPv6 address
|
||||
remote_addr = ipaddress.ip_network(req.env["REMOTE_ADDR"])
|
||||
remote_addr = ipaddress.ip_network(req.env["REMOTE_ADDR"].decode("utf-8"))
|
||||
|
||||
# Check for CSR submission whitelist
|
||||
if config.REQUEST_SUBNETS:
|
||||
@ -30,7 +30,7 @@ class RequestListResource(object):
|
||||
if subnet.overlaps(remote_addr):
|
||||
break
|
||||
else:
|
||||
logger.warning("Attempted to submit signing request from non-whitelisted address %s", req.env["REMOTE_ADDR"])
|
||||
logger.warning("Attempted to submit signing request from non-whitelisted address %s", remote_addr)
|
||||
raise falcon.HTTPForbidden("Forbidden", "IP address %s not whitelisted" % remote_addr)
|
||||
|
||||
if req.get_header("Content-Type") != "application/pkcs10":
|
||||
|
@ -18,7 +18,7 @@ logger = logging.getLogger("api")
|
||||
# address eg via LDAP, hence to keep things simple
|
||||
# we simply use Kerberos to authenticate.
|
||||
|
||||
FQDN = socket.getaddrinfo(socket.gethostname(), 0, flags=socket.AI_CANONNAME)[0][3]
|
||||
FQDN = socket.getaddrinfo(socket.gethostname(), 0, socket.AF_INET, 0, 0, socket.AI_CANONNAME)[0][3]
|
||||
|
||||
if not os.getenv("KRB5_KTNAME"):
|
||||
click.echo("Kerberos keytab not specified, set environment variable 'KRB5_KTNAME'", err=True)
|
||||
@ -89,7 +89,7 @@ def authorize_admin(func):
|
||||
def wrapped(self, req, resp, *args, **kwargs):
|
||||
from certidude import config
|
||||
# Parse remote IPv4/IPv6 address
|
||||
remote_addr = ipaddress.ip_network(req.env["REMOTE_ADDR"])
|
||||
remote_addr = ipaddress.ip_network(req.env["REMOTE_ADDR"].decode("utf-8"))
|
||||
|
||||
# Check for administration subnet whitelist
|
||||
print("Comparing:", config.ADMIN_SUBNETS, "To:", remote_addr)
|
||||
@ -97,12 +97,12 @@ def authorize_admin(func):
|
||||
if subnet.overlaps(remote_addr):
|
||||
break
|
||||
else:
|
||||
logger.info("Rejected access to administrative call %s by %s from %s, source address not whitelisted", req.env["PATH_INFO"], req.context["user"], req.env["REMOTE_ADDR"])
|
||||
logger.info("Rejected access to administrative call %s by %s from %s, source address not whitelisted", req.env["PATH_INFO"], req.context["user"], remote_addr)
|
||||
raise falcon.HTTPForbidden("Forbidden", "Remote address %s not whitelisted" % remote_addr)
|
||||
|
||||
# Check for username whitelist
|
||||
if req.context.get("user") not in config.ADMIN_USERS:
|
||||
logger.info("Rejected access to administrative call %s by %s from %s, user not whitelisted", req.env["PATH_INFO"], req.context["user"], req.env["REMOTE_ADDR"])
|
||||
logger.info("Rejected access to administrative call %s by %s from %s, user not whitelisted", req.env["PATH_INFO"], req.context["user"], remote_addr)
|
||||
raise falcon.HTTPForbidden("Forbidden", "User %s not whitelisted" % req.context.get("user"))
|
||||
|
||||
# Retain username, TODO: Better abstraction with username, e-mail, sn, gn?
|
||||
|
@ -3,7 +3,7 @@ import click
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import urllib.request
|
||||
import requests
|
||||
from OpenSSL import crypto
|
||||
from certidude import config, push
|
||||
from certidude.wrappers import Certificate, Request
|
||||
@ -24,12 +24,9 @@ def publish_certificate(func):
|
||||
|
||||
if config.PUSH_PUBLISH:
|
||||
url = config.PUSH_PUBLISH % csr.fingerprint()
|
||||
notification = urllib.request.Request(url, cert.dump().encode("ascii"))
|
||||
notification.add_header("User-Agent", "Certidude API")
|
||||
notification.add_header("Content-Type", "application/x-x509-user-cert")
|
||||
click.echo("Publishing certificate at %s, waiting for response..." % url)
|
||||
response = urllib.request.urlopen(notification)
|
||||
response.read()
|
||||
click.echo("Publishing certificate at %s ..." % url)
|
||||
requests.post(url, data=cert.dump(),
|
||||
headers={"User-Agent": "Certidude API", "Content-Type": "application/x-x509-user-cert"})
|
||||
push.publish("request-signed", csr.common_name)
|
||||
return cert
|
||||
return wrapped
|
||||
@ -146,22 +143,8 @@ def delete_request(common_name):
|
||||
push.publish("request-deleted", request_sha1sum)
|
||||
|
||||
# Write empty certificate to long-polling URL
|
||||
url = config.PUSH_PUBLISH % request_sha1sum
|
||||
click.echo("POST-ing empty certificate at %s, waiting for response..." % url)
|
||||
publisher = urllib.request.Request(url, b"-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----\n")
|
||||
publisher.add_header("User-Agent", "Certidude API")
|
||||
|
||||
try:
|
||||
response = urllib.request.urlopen(publisher)
|
||||
body = response.read()
|
||||
except urllib.error.HTTPError as err:
|
||||
if err.code == 404:
|
||||
print("No subscribers on the channel")
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
print("Push server returned:", response.code, body)
|
||||
|
||||
requests.delete(config.PUSH_PUBLISH % request_sha1sum,
|
||||
headers={"User-Agent": "Certidude API"})
|
||||
|
||||
@publish_certificate
|
||||
def sign(req, overwrite=False, delete=True):
|
||||
|
@ -23,7 +23,8 @@ from jinja2 import Environment, PackageLoader
|
||||
from time import sleep
|
||||
from setproctitle import setproctitle
|
||||
from OpenSSL import crypto
|
||||
|
||||
from future.standard_library import install_aliases
|
||||
install_aliases()
|
||||
|
||||
env = Environment(loader=PackageLoader("certidude", "templates"), trim_blocks=True)
|
||||
|
||||
@ -44,7 +45,7 @@ assert hasattr(crypto.X509Req(), "get_extensions"), "You're running too old vers
|
||||
|
||||
# Parse command-line argument defaults from environment
|
||||
HOSTNAME = socket.gethostname()
|
||||
FQDN = socket.getaddrinfo(HOSTNAME, 0, flags=socket.AI_CANONNAME)[0][3]
|
||||
FQDN = socket.getaddrinfo(HOSTNAME, 0, socket.AF_INET, 0, 0, socket.AI_CANONNAME)[0][3]
|
||||
USERNAME = os.environ.get("USER")
|
||||
NOW = datetime.utcnow().replace(tzinfo=None)
|
||||
FIRST_NAME = None
|
||||
@ -66,9 +67,8 @@ if os.getuid() >= 1000:
|
||||
@click.option("-f", "--fork", default=False, is_flag=True, help="Fork to background")
|
||||
def certidude_request_spawn(fork):
|
||||
from certidude.helpers import certidude_request_certificate
|
||||
from configparser import ConfigParser
|
||||
|
||||
clients = ConfigParser()
|
||||
clients = configparser.ConfigParser()
|
||||
clients.readfp(open("/etc/certidude/client.conf"))
|
||||
|
||||
services = ConfigParser()
|
||||
|
@ -1,14 +1,18 @@
|
||||
|
||||
import click
|
||||
import codecs
|
||||
import configparser
|
||||
import ipaddress
|
||||
import os
|
||||
import socket
|
||||
import string
|
||||
from random import choice
|
||||
from urllib.parse import urlparse
|
||||
|
||||
FQDN = socket.getaddrinfo(socket.gethostname(), 0, socket.AF_INET, 0, 0, socket.AI_CANONNAME)[0][3]
|
||||
|
||||
cp = configparser.ConfigParser()
|
||||
cp.read("/etc/certidude/server.conf")
|
||||
cp.readfp(codecs.open("/etc/certidude/server.conf", "r", "utf8"))
|
||||
|
||||
ADMIN_USERS = set([j for j in cp.get("authorization", "admin_users").split(" ") if j])
|
||||
ADMIN_SUBNETS = set([ipaddress.ip_network(j) for j in cp.get("authorization", "admin_subnets").split(" ") if j])
|
||||
@ -43,14 +47,16 @@ try:
|
||||
PUSH_LONG_POLL = cp.get("push", "long_poll")
|
||||
PUSH_PUBLISH = cp.get("push", "publish")
|
||||
except configparser.NoOptionError:
|
||||
PUSH_SERVER = cp.get("push", "server")
|
||||
PUSH_SERVER = cp.get("push", "server") or "http://localhost"
|
||||
PUSH_EVENT_SOURCE = PUSH_SERVER + "/ev/%s"
|
||||
PUSH_LONG_POLL = PUSH_SERVER + "/lp/%s"
|
||||
PUSH_PUBLISH = PUSH_SERVER + "/pub?id=%s"
|
||||
|
||||
from urllib.parse import urlparse
|
||||
o = urlparse(cp.get("authority", "database"))
|
||||
if o.scheme == "mysql":
|
||||
o = urlparse(cp.get("authority", "database") if cp.has_option("authority", "database") else "")
|
||||
|
||||
if not o.scheme:
|
||||
DATABASE_POOL = None
|
||||
elif o.scheme == "mysql":
|
||||
import mysql.connector
|
||||
DATABASE_POOL = mysql.connector.pooling.MySQLConnectionPool(
|
||||
pool_size = 32,
|
||||
|
@ -1,3 +1,4 @@
|
||||
# encoding: utf-8
|
||||
|
||||
import falcon
|
||||
import ipaddress
|
||||
@ -41,8 +42,6 @@ class MyEncoder(json.JSONEncoder):
|
||||
return obj.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
|
||||
if isinstance(obj, date):
|
||||
return obj.strftime("%Y-%m-%d")
|
||||
if isinstance(obj, map):
|
||||
return tuple(obj)
|
||||
if isinstance(obj, types.GeneratorType):
|
||||
return tuple(obj)
|
||||
if isinstance(obj, Request):
|
||||
|
@ -2,7 +2,6 @@
|
||||
import click
|
||||
import os
|
||||
import requests
|
||||
import urllib.request
|
||||
from certidude import errors
|
||||
from certidude.wrappers import Certificate, Request
|
||||
from OpenSSL import crypto
|
||||
@ -123,12 +122,11 @@ def certidude_request_certificate(url, key_path, request_path, certificate_path,
|
||||
return
|
||||
if submission.status_code == requests.codes.conflict:
|
||||
raise errors.DuplicateCommonNameError("Different signing request with same CN is already present on server, server refuses to overwrite")
|
||||
else:
|
||||
submission.raise_for_status()
|
||||
|
||||
if submission.text == '-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----\n':
|
||||
elif submission.status_code == requests.codes.gone:
|
||||
# Should the client retry or disable request submission?
|
||||
raise ValueError("Server refused to sign the request") # TODO: Raise proper exception
|
||||
else:
|
||||
submission.raise_for_status()
|
||||
|
||||
try:
|
||||
cert = crypto.load_certificate(crypto.FILETYPE_PEM, submission.text)
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
import click
|
||||
import json
|
||||
import urllib.request
|
||||
import requests
|
||||
from certidude import config
|
||||
|
||||
|
||||
@ -13,25 +13,9 @@ def publish(event_type, event_data):
|
||||
from certidude.decorators import MyEncoder
|
||||
event_data = json.dumps(event_data, cls=MyEncoder)
|
||||
|
||||
url = config.PUSH_PUBLISH % config.PUSH_TOKEN
|
||||
click.echo("Posting event %s %s at %s, waiting for response..." % (repr(event_type), repr(event_data), repr(url)))
|
||||
notification = urllib.request.Request(
|
||||
url,
|
||||
event_data.encode("utf-8"),
|
||||
{"X-EventSource-Event":event_type.encode("ascii")})
|
||||
notification.add_header("User-Agent", "Certidude API")
|
||||
|
||||
try:
|
||||
response = urllib.request.urlopen(notification)
|
||||
body = response.read()
|
||||
except urllib.error.HTTPError as err:
|
||||
if err.code == 404:
|
||||
print("No subscribers on the channel")
|
||||
else:
|
||||
print("Failed to submit event, %s" % err)
|
||||
else:
|
||||
print("Push server returned:", response.code, body)
|
||||
response.close()
|
||||
|
||||
notification = requests.post(
|
||||
config.PUSH_PUBLISH % config.PUSH_TOKEN,
|
||||
data=event_data,
|
||||
headers={"X-EventSource-Event": event_type, "User-Agent": "Certidude API"})
|
||||
|
||||
|
||||
|
@ -199,7 +199,7 @@ class Request(CertificateBase):
|
||||
self.path = NotImplemented
|
||||
self.created = NotImplemented
|
||||
|
||||
if isinstance(mixed, io.TextIOWrapper):
|
||||
if isinstance(mixed, file):
|
||||
self.path = mixed.name
|
||||
_, _, _, _, _, _, _, _, mtime, _ = os.stat(self.path)
|
||||
self.created = datetime.fromtimestamp(mtime)
|
||||
@ -248,7 +248,7 @@ class Certificate(CertificateBase):
|
||||
self.path = NotImplemented
|
||||
self.changed = NotImplemented
|
||||
|
||||
if isinstance(mixed, io.TextIOWrapper):
|
||||
if isinstance(mixed, file):
|
||||
self.path = mixed.name
|
||||
_, _, _, _, _, _, _, _, mtime, _ = os.stat(self.path)
|
||||
self.changed = datetime.fromtimestamp(mtime)
|
||||
|
@ -2,6 +2,7 @@ cffi==1.2.1
|
||||
click==5.1
|
||||
cryptography==1.0
|
||||
falcon==0.3.0
|
||||
future=0.15.2
|
||||
humanize==0.5.1
|
||||
idna==2.0
|
||||
ipsecparse==0.1.0
|
||||
|
Loading…
Reference in New Issue
Block a user