1
0
mirror of https://github.com/laurivosandi/certidude synced 2025-10-31 01:19:11 +00:00

Add preliminary support for logging

Current logging mechanism makes use of Python's logging module.
MySQL logging handler inserts log entries to MySQL server and
another logging handler is used to stream events to web interface
via nginx streaming push.
This commit is contained in:
2015-12-13 15:11:22 +00:00
parent b788d701eb
commit fbbf7a320d
16 changed files with 280 additions and 16 deletions

View File

@@ -35,7 +35,7 @@ class SessionResource(object):
@event_source
def on_get(self, req, resp):
return dict(
username=req.context.get("user")[0],
username=req.context.get("user"),
event_channel = config.PUSH_EVENT_SOURCE % config.PUSH_TOKEN,
autosign_subnets = config.AUTOSIGN_SUBNETS,
request_subnets = config.REQUEST_SUBNETS,
@@ -78,6 +78,7 @@ def certidude_app():
from .request import RequestListResource, RequestDetailResource
from .lease import LeaseResource
from .whois import WhoisResource
from .log import LogResource
app = falcon.API()
@@ -89,10 +90,51 @@ def certidude_app():
app.add_route("/api/signed/", SignedCertificateListResource())
app.add_route("/api/request/{cn}/", RequestDetailResource())
app.add_route("/api/request/", RequestListResource())
app.add_route("/api/log/", LogResource())
app.add_route("/api/", SessionResource())
# Gateway API calls, should this be moved to separate project?
app.add_route("/api/lease/", LeaseResource())
app.add_route("/api/whois/", WhoisResource())
"""
Set up logging
"""
from certidude import config
from certidude.mysqllog import MySQLLogHandler
from datetime import datetime
import logging
import socket
import json
class PushLogHandler(logging.Handler):
def emit(self, record):
from certidude.push import publish
print("EVENT HAPPENED:", record.created)
publish("log-entry", dict(
created = datetime.fromtimestamp(record.created),
message = record.msg % record.args,
severity = record.levelname.lower()))
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)
logger.addHandler(push_handler)
logging.getLogger("cli").info("Started Certidude at %s", socket.getaddrinfo(socket.gethostname(), 0, flags=socket.AI_CANONNAME)[0][3])
import atexit
def exit_handler():
logging.getLogger("cli").info("Shutting down Certidude")
atexit.register(exit_handler)
return app

39
certidude/api/log.py Normal file
View File

@@ -0,0 +1,39 @@
from certidude import config
from certidude.auth import login_required, authorize_admin
from certidude.decorators import serialize
class LogResource(object):
@serialize
@login_required
@authorize_admin
def on_get(self, req, resp):
"""
Translate currently online client's IP-address to distinguished name
"""
SQL_LOG_ENTRIES = """
SELECT
*
FROM
log
ORDER BY created DESC
"""
conn = config.DATABASE_POOL.get_connection()
cursor = conn.cursor(dictionary=True)
cursor.execute(SQL_LOG_ENTRIES)
def g():
for row in cursor:
yield row
cursor.close()
conn.close()
return tuple(g())
# for acquired, released, identity in cursor:
# return {
# "acquired": datetime.utcfromtimestamp(acquired),
# "identity": parse_dn(bytes(identity))
# }
# return None

View File

@@ -1,6 +1,7 @@
import click
import falcon
import logging
import ipaddress
import os
from certidude import config, authority, helpers, push
@@ -8,6 +9,8 @@ from certidude.auth import login_required, authorize_admin
from certidude.decorators import serialize
from certidude.wrappers import Request, Certificate
logger = logging.getLogger("api")
class RequestListResource(object):
@serialize
@authorize_admin
@@ -27,13 +30,14 @@ 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"])
raise falcon.HTTPForbidden("Forbidden", "IP address %s not whitelisted" % remote_addr)
if req.get_header("Content-Type") != "application/pkcs10":
raise falcon.HTTPUnsupportedMediaType(
"This API call accepts only application/pkcs10 content type")
body = req.stream.read(req.content_length)
body = req.stream.read(req.content_length).decode("ascii")
csr = Request(body)
# Check if this request has been already signed and return corresponding certificte if it has been signed
@@ -65,6 +69,7 @@ class RequestListResource(object):
try:
csr = authority.store_request(body)
except FileExistsError:
logger.warning("Rejected signing request with overlapping common name from %s", req.env["REMOTE_ADDR"])
raise falcon.HTTPConflict(
"CSR with such CN already exists",
"Will not overwrite existing certificate signing request, explicitly delete CSR and try again")
@@ -77,9 +82,11 @@ class RequestListResource(object):
click.echo("Redirecting to: %s" % url)
resp.status = falcon.HTTP_SEE_OTHER
resp.set_header("Location", url)
logger.warning("Redirecting signing request from %s to %s", req.env["REMOTE_ADDR"], url)
else:
# Request was accepted, but not processed
resp.status = falcon.HTTP_202
logger.info("Signing request from %s stored", req.env["REMOTE_ADDR"])
class RequestDetailResource(object):
@@ -108,6 +115,7 @@ class RequestDetailResource(object):
resp.body = "Certificate successfully signed"
resp.status = falcon.HTTP_201
resp.location = os.path.join(req.relative_uri, "..", "..", "signed", cn)
logger.info("Signing request %s signed by %s from %s", csr.common_name, req.context["user"], req.env["REMOTE_ADDR"])
@login_required
@authorize_admin
@@ -116,4 +124,5 @@ class RequestDetailResource(object):
authority.delete_request(cn)
except FileNotFoundError:
resp.body = "No certificate CN=%s found" % cn
logger.warning("User %s attempted to delete non-existant signing request %s from %s", req.context["user"], cn, req.env["REMOTE_ADDR"])
raise falcon.HTTPNotFound()