mirror of
https://github.com/laurivosandi/certidude
synced 2026-01-12 17:06:59 +00:00
Use filesystem extended attribute user.xdg.tags for tags, move leases to user.lease namespace
This commit is contained in:
@@ -59,15 +59,29 @@ class SessionResource(object):
|
||||
|
||||
def serialize_certificates(g):
|
||||
for common_name, path, buf, obj, server in g():
|
||||
# Extract certificate tags from filesystem
|
||||
try:
|
||||
last_seen = datetime.strptime(xattr.getxattr(path, "user.last_seen"), "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
tags = []
|
||||
for tag in xattr.getxattr(path, "user.xdg.tags").split(","):
|
||||
if "=" in tag:
|
||||
k, v = tag.split("=", 1)
|
||||
else:
|
||||
k, v = "other", tag
|
||||
tags.append(dict(id=tag, key=k, value=v))
|
||||
except IOError: # No such attribute(s)
|
||||
tags = None
|
||||
|
||||
# Extract lease information from filesystem
|
||||
try:
|
||||
last_seen = datetime.strptime(xattr.getxattr(path, "user.lease.last_seen"), "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
lease = dict(
|
||||
address = xattr.getxattr(path, "user.address"),
|
||||
address = xattr.getxattr(path, "user.lease.address"),
|
||||
last_seen = last_seen,
|
||||
age = datetime.utcnow() - last_seen
|
||||
)
|
||||
except IOError: # No such attribute(s)
|
||||
lease = None
|
||||
|
||||
yield dict(
|
||||
serial_number = "%x" % obj.serial_number,
|
||||
common_name = common_name,
|
||||
@@ -77,10 +91,7 @@ class SessionResource(object):
|
||||
expires = obj.not_valid_after,
|
||||
sha256sum = hashlib.sha256(buf).hexdigest(),
|
||||
lease = lease,
|
||||
tags = dict([
|
||||
(j[9:], xattr.getxattr(path, j).decode("utf-8"))
|
||||
for j in xattr.listxattr(path)
|
||||
if j.startswith("user.tag.")])
|
||||
tags = tags
|
||||
)
|
||||
|
||||
if req.context.get("user").is_admin():
|
||||
@@ -96,6 +107,7 @@ class SessionResource(object):
|
||||
),
|
||||
request_submission_allowed = config.REQUEST_SUBMISSION_ALLOWED,
|
||||
authority = dict(
|
||||
tagging = [dict(name=t[0], type=t[1], title=t[2]) for t in config.TAG_TYPES],
|
||||
lease = dict(
|
||||
offline = 600, # Seconds from last seen activity to consider lease offline, OpenVPN reneg-sec option
|
||||
dead = 604800 # Seconds from last activity to consider lease dead, X509 chain broken or machine discarded
|
||||
@@ -199,7 +211,7 @@ def certidude_app():
|
||||
app.add_route("/api/signed/{cn}/lease/", LeaseDetailResource())
|
||||
|
||||
# API call used to delete existing tags
|
||||
app.add_route("/api/signed/{cn}/tag/{key}/", TagDetailResource())
|
||||
app.add_route("/api/signed/{cn}/tag/{tag}/", TagDetailResource())
|
||||
|
||||
# Gateways can submit leases via this API call
|
||||
app.add_route("/api/lease/", LeaseResource())
|
||||
|
||||
@@ -16,8 +16,8 @@ class LeaseDetailResource(object):
|
||||
def on_get(self, req, resp, cn):
|
||||
path, buf, cert = authority.get_signed(cn)
|
||||
return dict(
|
||||
last_seen = xattr.getxattr(path, "user.last_seen"),
|
||||
address = xattr.getxattr(path, "user.address").decode("ascii")
|
||||
last_seen = xattr.getxattr(path, "user.lease.last_seen"),
|
||||
address = xattr.getxattr(path, "user.lease.address").decode("ascii")
|
||||
)
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ class LeaseResource(object):
|
||||
if cert.serial != req.get_param_as_int("serial", required=True): # Badum we have OCSP!
|
||||
raise # TODO proper exception
|
||||
if req.get_param("action") == "client-connect":
|
||||
xattr.setxattr(path, "user.address", req.get_param("address", required=True).encode("ascii"))
|
||||
xattr.setxattr(path, "user.last_seen", datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z")
|
||||
xattr.setxattr(path, "user.lease.address", req.get_param("address", required=True).encode("ascii"))
|
||||
xattr.setxattr(path, "user.lease.last_seen", datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z")
|
||||
push.publish("lease-update", common_name)
|
||||
|
||||
# client-disconnect is pretty much unusable:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import falcon
|
||||
import logging
|
||||
import xattr
|
||||
from certidude import authority
|
||||
from xattr import getxattr, removexattr, setxattr
|
||||
from certidude import authority, push
|
||||
from certidude.auth import login_required, authorize_admin
|
||||
from certidude.decorators import serialize, csrf_protection
|
||||
|
||||
@@ -13,19 +13,34 @@ class TagResource(object):
|
||||
@authorize_admin
|
||||
def on_get(self, req, resp, cn):
|
||||
path, buf, cert = authority.get_signed(cn)
|
||||
return dict([
|
||||
(k[9:], xattr.getxattr(path, k))
|
||||
for k in xattr.listxattr(path)
|
||||
if k.startswith("user.tag.")])
|
||||
tags = []
|
||||
try:
|
||||
for tag in getxattr(path, "user.xdg.tags").split(","):
|
||||
if "=" in tag:
|
||||
k, v = tag.split("=", 1)
|
||||
else:
|
||||
k, v = "other", tag
|
||||
tags.append(dict(id=tag, key=k, value=v))
|
||||
except IOError: # No user.xdg.tags attribute
|
||||
pass
|
||||
return tags
|
||||
|
||||
|
||||
@csrf_protection
|
||||
@login_required
|
||||
@authorize_admin
|
||||
def on_post(self, req, resp, cn):
|
||||
from certidude import push
|
||||
path, buf, cert = authority.get_signed(cn)
|
||||
key, value = req.get_param("key", required=True), req.get_param("value", required=True)
|
||||
xattr.setxattr(path, "user.tag.%s" % key, value.encode("utf-8"))
|
||||
try:
|
||||
tags = set(getxattr(path, "user.xdg.tags").decode("utf-8").split(","))
|
||||
except IOError:
|
||||
tags = set()
|
||||
if key == "other":
|
||||
tags.add(value)
|
||||
else:
|
||||
tags.add("%s=%s" % (key,value))
|
||||
setxattr(path, "user.xdg.tags", ",".join(tags).encode("utf-8"))
|
||||
logger.debug(u"Tag %s=%s set for %s" % (key, value, cn))
|
||||
push.publish("tag-update", cn)
|
||||
|
||||
@@ -34,9 +49,32 @@ class TagDetailResource(object):
|
||||
@csrf_protection
|
||||
@login_required
|
||||
@authorize_admin
|
||||
def on_delete(self, req, resp, cn, key):
|
||||
from certidude import push
|
||||
def on_put(self, req, resp, cn, tag):
|
||||
path, buf, cert = authority.get_signed(cn)
|
||||
xattr.removexattr(path, "user.tag.%s" % key)
|
||||
logger.debug(u"Tag %s removed for %s" % (key, cn))
|
||||
value = req.get_param("value", required=True)
|
||||
try:
|
||||
tags = set(getxattr(path, "user.xdg.tags").decode("utf-8").split(","))
|
||||
except IOError:
|
||||
tags = set()
|
||||
tags.remove(tag)
|
||||
if "=" in tag:
|
||||
tags.add("%s=%s" % (tag.split("=")[0], value))
|
||||
else:
|
||||
tags.add(value)
|
||||
setxattr(path, "user.xdg.tags", ",".join(tags).encode("utf-8"))
|
||||
logger.debug(u"Tag %s set to %s for %s" % (tag, value, cn))
|
||||
push.publish("tag-update", cn)
|
||||
|
||||
@csrf_protection
|
||||
@login_required
|
||||
@authorize_admin
|
||||
def on_delete(self, req, resp, cn, tag):
|
||||
path, buf, cert = authority.get_signed(cn)
|
||||
tags = set(getxattr(path, "user.xdg.tags").split(","))
|
||||
tags.remove(tag)
|
||||
if not tags:
|
||||
removexattr(path, "user.xdg.tags")
|
||||
else:
|
||||
setxattr(path, "user.xdg.tags", ",".join(tags))
|
||||
logger.debug(u"Tag %s removed for %s" % (tag, cn))
|
||||
push.publish("tag-update", cn)
|
||||
|
||||
Reference in New Issue
Block a user