Add preliminary Python 2.x support

This commit is contained in:
Lauri Võsandi 2016-02-28 22:37:56 +02:00
parent 5eed7cb6d9
commit 4240d55fe4
11 changed files with 44 additions and 72 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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