64 lines
3.1 KiB
Python
64 lines
3.1 KiB
Python
import falcon
|
|
import logging
|
|
import os
|
|
import re
|
|
import xattr
|
|
from datetime import datetime
|
|
from certidude import config, push
|
|
from certidude.decorators import serialize
|
|
from .utils import AuthorityHandler
|
|
from .utils.firewall import login_required, authorize_admin, authorize_server
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# TODO: lease namespacing (?)
|
|
|
|
class LeaseDetailResource(AuthorityHandler):
|
|
@serialize
|
|
@login_required
|
|
@authorize_admin
|
|
def on_get(self, req, resp, cn):
|
|
try:
|
|
path, buf, cert, signed, expires = self.authority.get_signed(cn)
|
|
return dict(
|
|
last_seen = xattr.getxattr(path, "user.lease.last_seen").decode("ascii"),
|
|
inner_address = xattr.getxattr(path, "user.lease.inner_address").decode("ascii"),
|
|
outer_address = xattr.getxattr(path, "user.lease.outer_address").decode("ascii")
|
|
)
|
|
except EnvironmentError: # Certificate or attribute not found
|
|
raise falcon.HTTPNotFound()
|
|
|
|
|
|
class LeaseResource(AuthorityHandler):
|
|
@authorize_server
|
|
def on_post(self, req, resp):
|
|
client_common_name = req.get_param("client", required=True)
|
|
m = re.match("^(.*, )*CN=(.+?)(, .*)*$", client_common_name) # It's actually DN, resolve it to CN
|
|
if m:
|
|
_, client_common_name, _ = m.groups()
|
|
|
|
path, buf, cert, signed, expires = self.authority.get_signed(client_common_name) # TODO: catch exceptions
|
|
if req.get_param("serial") and cert.serial_number != req.get_param_as_int("serial"): # OCSP-ish solution for OpenVPN, not exposed for StrongSwan
|
|
logger.info("Gateway %s attempted to submit lease information for %s with expired/unknown serial %x, expected %x" % (
|
|
req.context["machine"], client_common_name,
|
|
req.get_param_as_int("serial"), cert.serial_number))
|
|
raise falcon.HTTPForbidden("Forbidden", "Invalid serial number supplied")
|
|
now = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
|
|
|
|
xattr.setxattr(path, "user.lease.outer_address", req.get_param("outer_address", required=True).encode("ascii"))
|
|
xattr.setxattr(path, "user.lease.inner_address", req.get_param("inner_address", required=True).encode("ascii"))
|
|
xattr.setxattr(path, "user.lease.last_seen", now)
|
|
push.publish("lease-update", client_common_name)
|
|
|
|
server_common_name = req.context.get("machine")
|
|
path = os.path.join(config.SIGNED_DIR, server_common_name + ".pem")
|
|
xattr.setxattr(path, "user.lease.outer_address", "")
|
|
xattr.setxattr(path, "user.lease.inner_address", "%s" % req.context.get("remote_addr"))
|
|
xattr.setxattr(path, "user.lease.last_seen", now)
|
|
push.publish("lease-update", server_common_name)
|
|
|
|
# client-disconnect is pretty much unusable:
|
|
# - Android Connect Client results "IP packet with unknown IP version=2" on gateway
|
|
# - NetworkManager just kills OpenVPN client, disconnect is never reported
|
|
# - Disconnect is also not reported when uplink connection dies or laptop goes to sleep
|