diff --git a/certidude/api/__init__.py b/certidude/api/__init__.py
index f1de85a..9d657d9 100644
--- a/certidude/api/__init__.py
+++ b/certidude/api/__init__.py
@@ -100,6 +100,7 @@ class SessionResource(AuthorityHandler):
except IOError:
signer_username = None
+ # TODO: dedup
yield dict(
serial = "%x" % cert.serial_number,
organizational_unit = cert.subject.native.get("organizational_unit_name"),
diff --git a/certidude/api/signed.py b/certidude/api/signed.py
index 2b42cc6..65bc5cc 100644
--- a/certidude/api/signed.py
+++ b/certidude/api/signed.py
@@ -5,7 +5,7 @@ import json
import hashlib
from certidude.auth import login_required, authorize_admin
from certidude.decorators import csrf_protection
-from xattr import getxattr
+from xattr import listxattr, getxattr
from .utils import AuthorityHandler
logger = logging.getLogger(__name__)
@@ -34,14 +34,28 @@ class SignedCertificateDetailResource(AuthorityHandler):
signer_username = getxattr(path, "user.signature.username").decode("ascii")
except IOError:
signer_username = None
+
+ attributes = {}
+ for key in listxattr(path):
+ if key.startswith(b"user.machine."):
+ attributes[key[13:].decode("ascii")] = getxattr(path, key).decode("ascii")
+
+ # TODO: dedup
resp.body = json.dumps(dict(
common_name = cn,
signer = signer_username,
- serial_number = "%x" % cert.serial_number,
+ serial = "%x" % cert.serial_number,
organizational_unit = cert.subject.native.get("organizational_unit_name"),
signed = cert["tbs_certificate"]["validity"]["not_before"].native.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z",
expires = cert["tbs_certificate"]["validity"]["not_after"].native.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z",
- sha256sum = hashlib.sha256(buf).hexdigest()))
+ sha256sum = hashlib.sha256(buf).hexdigest(),
+ attributes = attributes or None,
+ lease = None,
+ extensions = dict([
+ (e["extn_id"].native, e["extn_value"].native)
+ for e in cert["tbs_certificate"]["extensions"]
+ if e["extn_value"] in ("extended_key_usage",)])
+ ))
logger.debug("Served certificate %s to %s as application/json",
cn, req.context.get("remote_addr"))
else:
diff --git a/certidude/api/token.py b/certidude/api/token.py
index 13ce10a..1cc40f5 100644
--- a/certidude/api/token.py
+++ b/certidude/api/token.py
@@ -41,7 +41,7 @@ class TokenResource(AuthorityHandler):
common_name = csr["certification_request_info"]["subject"].native["common_name"]
assert common_name == username or common_name.startswith(username + "@"), "Invalid common name %s" % common_name
try:
- _, resp.body = self.authority._sign(csr, body)
+ _, resp.body = self.authority._sign(csr, body, profile="default")
resp.set_header("Content-Type", "application/x-pem-file")
logger.info("Autosigned %s as proven by token ownership", common_name)
except FileExistsError:
diff --git a/certidude/authority.py b/certidude/authority.py
index 1769540..52a4859 100644
--- a/certidude/authority.py
+++ b/certidude/authority.py
@@ -68,7 +68,7 @@ def self_enroll():
from certidude import authority
from certidude.common import drop_privileges
drop_privileges()
- authority.sign(common_name, skip_push=True, overwrite=True)
+ authority.sign(common_name, skip_push=True, overwrite=True, profile="srv")
sys.exit(0)
else:
os.waitpid(pid, 0)
@@ -307,7 +307,7 @@ def delete_request(common_name):
config.LONG_POLL_PUBLISH % hashlib.sha256(buf).hexdigest(),
headers={"User-Agent": "Certidude API"})
-def sign(common_name, skip_notify=False, skip_push=False, overwrite=False, profile="default", signer=None):
+def sign(common_name, skip_notify=False, skip_push=False, overwrite=False, profile=None, signer=None):
"""
Sign certificate signing request by it's common name
"""
@@ -325,12 +325,12 @@ def sign(common_name, skip_notify=False, skip_push=False, overwrite=False, profi
os.unlink(req_path)
return cert, buf
-def _sign(csr, buf, skip_notify=False, skip_push=False, overwrite=False, profile="default", signer=None):
+def _sign(csr, buf, skip_notify=False, skip_push=False, overwrite=False, profile=None, signer=None):
# TODO: CRLDistributionPoints, OCSP URL, Certificate URL
if profile not in config.PROFILES:
raise ValueError("Invalid profile supplied '%s'" % profile)
- assert buf.startswith(b"-----BEGIN CERTIFICATE REQUEST-----")
+ assert buf.startswith(b"-----BEGIN ")
assert isinstance(csr, CertificationRequest)
csr_pubkey = asymmetric.load_public_key(csr["certification_request_info"]["subject_pk_info"])
common_name = csr["certification_request_info"]["subject"].native["common_name"]
diff --git a/certidude/cli.py b/certidude/cli.py
index 8f84219..bdfeab0 100755
--- a/certidude/cli.py
+++ b/certidude/cli.py
@@ -956,7 +956,7 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
os.system("apt-get install -qq -y cython3 python3-dev python3-mimeparse \
python3-markdown python3-pyxattr python3-jinja2 python3-cffi \
software-properties-common libsasl2-modules-gssapi-mit npm nodejs \
- libkrb5-dev libldap2-dev libsasl2-dev gawk libncurses5-dev")
+ libkrb5-dev libldap2-dev libsasl2-dev gawk libncurses5-dev rsync")
os.system("pip3 install -q --upgrade gssapi falcon humanize ipaddress simplepam")
os.system("pip3 install -q --pre --upgrade python-ldap")
@@ -1308,11 +1308,12 @@ def certidude_list(verbose, show_key_type, show_extensions, show_path, show_sign
@click.command("sign", help="Sign certificate")
@click.argument("common_name")
+@click.option("--profile", "-p", default=None, help="Profile")
@click.option("--overwrite", "-o", default=False, is_flag=True, help="Revoke valid certificate with same CN")
-def certidude_sign(common_name, overwrite):
+def certidude_sign(common_name, overwrite, profile):
from certidude import authority
drop_privileges()
- cert = authority.sign(common_name, overwrite=overwrite)
+ cert = authority.sign(common_name, overwrite=overwrite, profile=profile)
@click.command("revoke", help="Revoke certificate")
diff --git a/certidude/static/views/lease.html b/certidude/static/views/lease.html
index 2795e77..51348bd 100644
--- a/certidude/static/views/lease.html
+++ b/certidude/static/views/lease.html
@@ -1,4 +1,4 @@
-
+
Last seen
at