Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d648d294ec | ||
|
|
a245ec26cd | ||
|
|
7404ee88c4 |
No files matched your search
@@ -144,6 +144,7 @@ must have the type shown below; an unknown section or setting is an error.
|
||||
| `pki` | `s2s_name` | string | `VPN Router Site-to-Site CA` | Common name of the site-to-site issuing CA |
|
||||
| `pki` | `issuing_cas` | object | | The general-purpose issuing CAs, each a name and `{name, templates, acme}` (see Certificates) |
|
||||
| `pki` | `templates` | object | | Certificate templates, each a name and the options it changes or defines (see Certificates) |
|
||||
| `pki` | `extended_key_usages` | object | | Custom extended key usages templates may give, each a name and its OID in dotted form (see Certificates) |
|
||||
| `pki` | `root_key_warning` | boolean | `true` | `vpn-router-ca status` and `/ca` warn while the root key is on the router; `false` silences that warning |
|
||||
| `metrics` | `enabled` | boolean | `true` | The metrics service: collection, history and `/metrics` |
|
||||
| `metrics` | `listen` | string | `internal` | The interfaces `/metrics` is served on: `internal`, `external` or `both` |
|
||||
@@ -442,7 +443,8 @@ A template says what a certificate holds:
|
||||
| `key` | string | `rsa-3072` | `rsa-2048`, `rsa-3072`, `rsa-4096`, `ec-p256` or `ec-p384`, for a key the router makes |
|
||||
| `days` | integer | `365` | Days the certificate is valid, at most until its CA's certificate expires |
|
||||
| `key_usage` | list | `digital_signature` | Of `digital_signature`, `content_commitment`, `key_encipherment`, `data_encipherment`, `key_agreement` |
|
||||
| `extended_key_usage` | list | | Of `server_auth`, `client_auth`, `code_signing`, `email_protection`, `time_stamping`, `ocsp_signing`, `ipsec_ike`, `ike_intermediate` |
|
||||
| `extended_key_usage` | list | | Of the built-in `server_auth`, `client_auth`, `code_signing`, `email_protection`, `time_stamping`, `ocsp_signing`, `ipsec_ike`, `ike_intermediate`, and the custom usages of `pki.extended_key_usages` |
|
||||
| `policies` | list | | Certificate policy OIDs in dotted form, written into a Certificate Policies extension |
|
||||
| `subject` | string | `name` | The common name: `dns`, a DNS name, which is also a subject alternative name; `name`, any name; `email`, an e-mail address |
|
||||
| `sans` | list | | The subject alternative names allowed: of `dns`, `ip`, `email` |
|
||||
|
||||
@@ -461,15 +463,25 @@ templates of its own, named like the issuing CAs:
|
||||
|
||||
```json
|
||||
"pki": {
|
||||
"issuing_cas": { "servers": { "name": "Example Server CA", "templates": ["server", "web"] } },
|
||||
"issuing_cas": { "servers": { "name": "Example Server CA", "templates": ["server", "web", "smartcard"] } },
|
||||
"extended_key_usages": { "smartcard_logon": "1.3.6.1.4.1.311.20.2.2" },
|
||||
"templates": {
|
||||
"server": { "days": 397 },
|
||||
"web": { "key": "ec-p256", "days": 90, "key_usage": ["digital_signature"],
|
||||
"extended_key_usage": ["server_auth"], "subject": "dns", "sans": ["dns"] }
|
||||
"extended_key_usage": ["server_auth"], "subject": "dns", "sans": ["dns"] },
|
||||
"smartcard": { "key_usage": ["digital_signature", "key_encipherment"],
|
||||
"extended_key_usage": ["client_auth", "smartcard_logon"], "subject": "name",
|
||||
"sans": ["email"], "policies": ["1.3.6.1.4.1.99999.1.1"] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A custom extended key usage is first added to `pki.extended_key_usages`, a name of at most 31
|
||||
lowercase letters, digits, hyphens and underscores that is not a built-in one, and its OID; a
|
||||
template then gives it by its name. A usage a template gives cannot be removed. `policies` puts
|
||||
the OIDs of the issuance policies a certificate is issued under into its Certificate Policies
|
||||
extension, without qualifiers.
|
||||
|
||||
### Issuing and revoking
|
||||
|
||||
```sh
|
||||
@@ -904,6 +916,12 @@ curl -H 'Content-Type: application/json' -d '{"name": "alice", "password": "s3cr
|
||||
curl -H "Authorization: Bearer $TOKEN" http://10.0.0.4:7081/config
|
||||
```
|
||||
|
||||
### The /status/full page
|
||||
|
||||
`/status/full`, with the same token as `/config`, shows the whole status document, as `/status`
|
||||
does with `web.status_content` `full`, whatever that setting says, so the Router Manager shows
|
||||
every service and tunnel while the public status page shows less.
|
||||
|
||||
### The /config page
|
||||
|
||||
`/config` shows what `/` and `/status` leave out, for a client such as manage-ui:
|
||||
@@ -1066,7 +1084,7 @@ A change is made, checked and applied as for `/site-to-site`.
|
||||
|
||||
| Request | Body | Answer |
|
||||
| --- | --- | --- |
|
||||
| `GET /ca` | | `{"settings", "templates", "status", "certificates"}` |
|
||||
| `GET /ca` | | `{"settings", "templates", "extended_key_usages", "status", "certificates"}` |
|
||||
| `PUT /ca` | any of the settings of the `pki` section | `{"settings", "templates"}` |
|
||||
| `POST /ca/certificates` | `{"ca", "template", "csr"}` | 201 and `{"serial", "certificate", "chain"}`, PEM |
|
||||
| `POST /ca/certificates` | `{"ca", "template", "subject", "sans", "password"}` | 201 and `{"serial", "pfx"}`, a PKCS#12 file in base64 |
|
||||
@@ -1080,6 +1098,7 @@ The answer of `GET /ca` holds:
|
||||
|
||||
- **`settings`:** the `pki` section.
|
||||
- **`templates`:** every template, the built-in ones included, with all its options.
|
||||
- **`extended_key_usages`:** every extended key usage templates may give, name -> `{"oid", "builtin"}`.
|
||||
- **`status`:** `root` (`subject`, `expires`, `key`, whether the root key is on the router, `crl_next_update` and `crl_source`, `signed` or `uploaded`), `authorities`, one per issuing CA (`name`, `common_name`, `state`: `active`, `pending` or `missing`, `builtin`, `templates`, `acme`, `subject`, `expires`, `key`, `crl_next_update`, `acme_url`), `retired` and `warnings`.
|
||||
- **`certificates`:** the certificates issued, newest first, each `{"serial", "ca", "kind", "template", "subject", "sans", "created", "expires", "revoked", "reason", "state"}`; `kind` is `ca`, `router`, `user` or `external`, and `state` `valid`, `expired` or `revoked`.
|
||||
|
||||
|
||||
@@ -1,3 +1,36 @@
|
||||
vpn-router (0.22.0-1) unstable; urgency=medium
|
||||
|
||||
feature: pki.extended_key_usages adds custom extended key usages by their OIDs, which templates give by name, and a template's policies put certificate policy OIDs into its certificates.
|
||||
|
||||
* pki.extended_key_usages maps a name to the OID of a custom extended
|
||||
key usage; a template's extended_key_usage takes the built-in and the
|
||||
custom names.
|
||||
* The template option policies lists certificate policy OIDs, written
|
||||
into a Certificate Policies extension.
|
||||
* GET /ca answers extended_key_usages, each usage with its OID and
|
||||
whether it is built in.
|
||||
|
||||
-- Slawomir Koszewski <slawek@koszewscy.waw.pl> Mon, 02 Nov 2026 14:00:00 +0100
|
||||
|
||||
vpn-router (0.21.5-1) unstable; urgency=medium
|
||||
|
||||
fix: /status/full of the status web service gives an authenticated client the whole status, whatever web.status_content says.
|
||||
|
||||
* /status/full takes the token of /config and answers the status
|
||||
document as web.status_content full shows it.
|
||||
|
||||
-- Slawomir Koszewski <slawek@koszewscy.waw.pl> Mon, 02 Nov 2026 12:00:00 +0100
|
||||
|
||||
vpn-router (0.21.4-1) unstable; urgency=medium
|
||||
|
||||
fix: vpn-router-setup, vpn-router-ca and /ca read a CA's key and CRL from the directory each as itself, instead of failing on a CA with a CRL.
|
||||
|
||||
* A CA's CRL was read as its key and its key as its CRL, so every
|
||||
operation on a CA with a CRL failed with "Could not deserialize key
|
||||
data".
|
||||
|
||||
-- Slawomir Koszewski <slawek@koszewscy.waw.pl> Mon, 02 Nov 2026 10:00:00 +0100
|
||||
|
||||
vpn-router (0.21.3-1) unstable; urgency=medium
|
||||
|
||||
fix: the PKI is kept in ou=pki of the router's directory instead of files under /etc/vpn-router/pki, and the router's site-to-site certificate names only the identities its connections present, and /ca/bundle gives the certificate bundle of a CA.
|
||||
|
||||
@@ -57,7 +57,7 @@ SCHEMA = {
|
||||
'pki': {'root_name': 'VPN Router Root CA', 'root_days': 7300, 'ca_days': 3650, 'key': 'rsa-4096',
|
||||
'crl_days': 7, 'root_crl_days': 180, 'publish': ['http'], 'p2s_name': 'VPN Router P2S CA',
|
||||
's2s_name': 'VPN Router Site-to-Site CA', 'issuing_cas': {}, 'templates': {},
|
||||
'root_key_warning': True},
|
||||
'extended_key_usages': {}, 'root_key_warning': True},
|
||||
'metrics': {'enabled': True, 'listen': 'internal', 'port': 9464, 'https_port': 9465, 'service_account': 'metrics',
|
||||
'otlp_endpoint': '', 'otlp_username': '', 'otlp_password': '',
|
||||
'keep_details': '24 hours', 'keep_hourly': '1 month', 'keep_daily': '3 months',
|
||||
@@ -136,9 +136,23 @@ PKI_PUBLISH = ('http', 'http-80', 'https', 'https-default')
|
||||
#: The key usages a template may give, as the arguments of cryptography's KeyUsage.
|
||||
KEY_USAGES = ('digital_signature', 'content_commitment', 'key_encipherment', 'data_encipherment', 'key_agreement')
|
||||
|
||||
#: The extended key usages a template may give.
|
||||
EXTENDED_KEY_USAGES = ('server_auth', 'client_auth', 'code_signing', 'email_protection', 'time_stamping',
|
||||
'ocsp_signing', 'ipsec_ike', 'ike_intermediate')
|
||||
#: The built-in extended key usages a template may give, name -> OID; pki.extended_key_usages adds custom ones.
|
||||
EXTENDED_KEY_USAGES = {
|
||||
'server_auth': '1.3.6.1.5.5.7.3.1',
|
||||
'client_auth': '1.3.6.1.5.5.7.3.2',
|
||||
'code_signing': '1.3.6.1.5.5.7.3.3',
|
||||
'email_protection': '1.3.6.1.5.5.7.3.4',
|
||||
'time_stamping': '1.3.6.1.5.5.7.3.8',
|
||||
'ocsp_signing': '1.3.6.1.5.5.7.3.9',
|
||||
'ipsec_ike': '1.3.6.1.5.5.7.3.17',
|
||||
'ike_intermediate': '1.3.6.1.5.5.8.2.2',
|
||||
}
|
||||
|
||||
#: The name of a custom extended key usage of pki.extended_key_usages.
|
||||
EKU_NAME = re.compile(r'^[a-z][a-z0-9_-]{0,30}$')
|
||||
|
||||
#: An object identifier in dotted form, of a custom extended key usage or a certificate policy.
|
||||
OID = re.compile(r'^[0-2](\.(0|[1-9][0-9]*)){1,63}$')
|
||||
|
||||
#: What the common name of a template's certificates is: a DNS name, any name, or an e-mail address.
|
||||
TEMPLATE_SUBJECTS = ('dns', 'name', 'email')
|
||||
@@ -148,7 +162,7 @@ SAN_TYPES = ('dns', 'ip', 'email')
|
||||
|
||||
#: A certificate template: option -> default, the type of the default being the required type.
|
||||
TEMPLATE = {'key': 'rsa-3072', 'days': 365, 'key_usage': ['digital_signature'], 'extended_key_usage': [],
|
||||
'subject': 'name', 'sans': []}
|
||||
'subject': 'name', 'sans': [], 'policies': []}
|
||||
|
||||
#: The built-in templates; pki.templates changes their options and adds templates of its own.
|
||||
PKI_TEMPLATES = {
|
||||
@@ -502,6 +516,14 @@ def validate(cfg):
|
||||
raise ConfigError('pki.publish: http is served by the web service, which needs web.enabled')
|
||||
if set(publish) - {'http'} and not _get(cfg, 'https', 'enabled'):
|
||||
raise ConfigError('pki.publish: http-80, https and https-default are served by Caddy, which needs https.enabled')
|
||||
for name, oid in cfg['pki']['extended_key_usages'].items():
|
||||
prefix = f'pki.extended_key_usages.{name}'
|
||||
if not EKU_NAME.match(name) or name in EXTENDED_KEY_USAGES:
|
||||
raise ConfigError(f'{prefix}: an extended key usage name starts with a lowercase letter, has at most 31 '
|
||||
'lowercase letters, digits, hyphens and underscores, and is not a built-in one')
|
||||
if not isinstance(oid, str) or not OID.match(oid):
|
||||
raise ConfigError(f'{prefix} must be an OID in dotted form, such as 1.3.6.1.4.1.311.20.2.2')
|
||||
usages = extended_key_usages(cfg)
|
||||
for name, template in cfg['pki']['templates'].items():
|
||||
prefix = f'pki.templates.{name}'
|
||||
if not PKI_NAME.match(name):
|
||||
@@ -517,11 +539,14 @@ def validate(cfg):
|
||||
raise ConfigError(f'{prefix}.key: {template["key"]!r} is not one of {", ".join(PKI_KEYS)}')
|
||||
if not 1 <= template['days'] <= 36500:
|
||||
raise ConfigError(f'{prefix}.days must be 1 to 36500 days')
|
||||
for option, allowed in (('key_usage', KEY_USAGES), ('extended_key_usage', EXTENDED_KEY_USAGES),
|
||||
for option, allowed in (('key_usage', KEY_USAGES), ('extended_key_usage', tuple(usages)),
|
||||
('sans', SAN_TYPES)):
|
||||
for item in template[option]:
|
||||
if item not in allowed:
|
||||
raise ConfigError(f'{prefix}.{option}: {item!r} is not one of {", ".join(allowed)}')
|
||||
for item in template['policies']:
|
||||
if not OID.match(item):
|
||||
raise ConfigError(f'{prefix}.policies: {item!r} is not an OID in dotted form, such as 1.3.6.1.4.1.99999.1')
|
||||
if not template['key_usage']:
|
||||
raise ConfigError(f'{prefix}.key_usage needs at least one usage')
|
||||
if template['subject'] not in TEMPLATE_SUBJECTS:
|
||||
@@ -665,6 +690,13 @@ def pki_templates(cfg):
|
||||
for name in [*PKI_TEMPLATES, *(name for name in custom if name not in PKI_TEMPLATES)]}
|
||||
|
||||
|
||||
def extended_key_usages(cfg):
|
||||
"""The extended key usages templates may give, name -> OID: the built-in ones (EXTENDED_KEY_USAGES) and those of
|
||||
pki.extended_key_usages."""
|
||||
custom = cfg['pki']['extended_key_usages']
|
||||
return {**EXTENDED_KEY_USAGES, **{name: oid for name, oid in custom.items() if isinstance(oid, str)}}
|
||||
|
||||
|
||||
def issuing_cas(cfg):
|
||||
"""The issuing CAs, name -> {name, templates, acme, builtin}: p2s and s2s (BUILTIN_CAS) with their common names from
|
||||
the pki section, then those of pki.issuing_cas, each filled from ISSUING_CA."""
|
||||
|
||||
@@ -44,7 +44,7 @@ from cryptography.exceptions import InvalidSignature
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import ec, rsa
|
||||
from cryptography.hazmat.primitives.serialization import pkcs12
|
||||
from cryptography.x509.oid import AuthorityInformationAccessOID, ExtendedKeyUsageOID, NameOID
|
||||
from cryptography.x509.oid import AuthorityInformationAccessOID, NameOID
|
||||
|
||||
import vpnrouter
|
||||
import vpnrouter_ldap
|
||||
@@ -89,18 +89,6 @@ CERTIFICATE_ATTRIBUTES = ['serialNumber', 'cn', 'vpnRouterKind', 'vpnRouterTempl
|
||||
#: The suffix of a retired CA's name: -<time of the retirement>.
|
||||
RETIRED_SUFFIX = re.compile(r'-\d{14}$')
|
||||
|
||||
#: The extended key usages of vpnrouter.EXTENDED_KEY_USAGES.
|
||||
EKU_OIDS = {
|
||||
'server_auth': ExtendedKeyUsageOID.SERVER_AUTH,
|
||||
'client_auth': ExtendedKeyUsageOID.CLIENT_AUTH,
|
||||
'code_signing': ExtendedKeyUsageOID.CODE_SIGNING,
|
||||
'email_protection': ExtendedKeyUsageOID.EMAIL_PROTECTION,
|
||||
'time_stamping': ExtendedKeyUsageOID.TIME_STAMPING,
|
||||
'ocsp_signing': ExtendedKeyUsageOID.OCSP_SIGNING,
|
||||
'ipsec_ike': ExtendedKeyUsageOID.IPSEC_IKE,
|
||||
'ike_intermediate': x509.ObjectIdentifier('1.3.6.1.5.5.8.2.2'),
|
||||
}
|
||||
|
||||
#: The revocation reasons revoke takes.
|
||||
REASONS = ('unspecified', 'key_compromise', 'affiliation_changed', 'superseded', 'cessation_of_operation',
|
||||
'privilege_withdrawn')
|
||||
@@ -229,7 +217,7 @@ def authority(connection, name):
|
||||
attributes = connection.search_s(authority_dn(name), ldap.SCOPE_BASE, attrlist=AUTHORITY_ATTRIBUTES)[0][1]
|
||||
except ldap.NO_SUCH_OBJECT:
|
||||
return None
|
||||
cert, key, crl = (first(attributes, item) for item in AUTHORITY_ATTRIBUTES[:3])
|
||||
cert, crl, key = (first(attributes, item) for item in AUTHORITY_ATTRIBUTES[:3])
|
||||
return {
|
||||
'name': name,
|
||||
'cert': x509.load_der_x509_certificate(cert) if cert else None,
|
||||
@@ -457,13 +445,19 @@ def general_names(values, allowed):
|
||||
return names
|
||||
|
||||
|
||||
def leaf_extensions(template, names):
|
||||
"""The extensions of a certificate of the template with the subject alternative names."""
|
||||
def leaf_extensions(cfg, template, names):
|
||||
"""The extensions of a certificate of the template with the subject alternative names: the extended key usages by
|
||||
their OIDs (vpnrouter.extended_key_usages), and the template's policies as certificate policies."""
|
||||
usage = x509.KeyUsage(**{option: option in template['key_usage'] for option in vpnrouter.KEY_USAGES},
|
||||
key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False)
|
||||
extensions = [(x509.BasicConstraints(ca=False, path_length=None), True), (usage, True)]
|
||||
if template['extended_key_usage']:
|
||||
extensions.append((x509.ExtendedKeyUsage([EKU_OIDS[item] for item in template['extended_key_usage']]), False))
|
||||
oids = vpnrouter.extended_key_usages(cfg)
|
||||
extensions.append((x509.ExtendedKeyUsage([x509.ObjectIdentifier(oids[item])
|
||||
for item in template['extended_key_usage']]), False))
|
||||
if template['policies']:
|
||||
extensions.append((x509.CertificatePolicies([x509.PolicyInformation(x509.ObjectIdentifier(oid), None)
|
||||
for oid in template['policies']]), False))
|
||||
if names:
|
||||
extensions.append((x509.SubjectAlternativeName(names), False))
|
||||
return extensions
|
||||
@@ -589,7 +583,7 @@ def ensure_router_cert(connection, cfg, ctx, name, urls):
|
||||
template = vpnrouter.pki_templates(cfg)[template_name]
|
||||
key = new_key(template['key'])
|
||||
cert = sign(subject, key.public_key(), issuer['cert'], issuer['key'], template['days'],
|
||||
leaf_extensions(template, general_names(sans, vpnrouter.SAN_TYPES)), urls, ca)
|
||||
leaf_extensions(cfg, template, general_names(sans, vpnrouter.SAN_TYPES)), urls, ca)
|
||||
record(connection, cert, ca, 'router', template_name, key)
|
||||
for dn, _, _ in current:
|
||||
drop_key(connection, dn)
|
||||
@@ -852,7 +846,7 @@ def issue(cfg, ca, template, csr=None, subject=None, sans=None, password=None):
|
||||
subject = check_subject(values['subject'], subject)
|
||||
names = general_names(([subject] if values['subject'] == 'dns' else []) + list(sans),
|
||||
set(values['sans']) | ({'dns'} if values['subject'] == 'dns' else set()))
|
||||
cert = sign(subject, public_key, issuer['cert'], issuer['key'], values['days'], leaf_extensions(values, names),
|
||||
cert = sign(subject, public_key, issuer['cert'], issuer['key'], values['days'], leaf_extensions(cfg, values, names),
|
||||
vpnrouter.pki_base_urls(cfg), ca)
|
||||
record(connection, cert, ca, 'external', template)
|
||||
root = authority(connection, 'root')['cert']
|
||||
@@ -880,7 +874,7 @@ def issue_user(cfg, name, email):
|
||||
issuer, values = issuer_of(connection, cfg, 'p2s', 'vpn-user')
|
||||
key = new_key(values['key'])
|
||||
cert = sign(name, key.public_key(), issuer['cert'], issuer['key'], values['days'],
|
||||
leaf_extensions(values, general_names([name, email], vpnrouter.SAN_TYPES)),
|
||||
leaf_extensions(cfg, values, general_names([name, email], vpnrouter.SAN_TYPES)),
|
||||
vpnrouter.pki_base_urls(cfg), 'p2s')
|
||||
for dn, _, _ in holders(connection, 'p2s', user_condition(name)):
|
||||
drop_key(connection, dn)
|
||||
|
||||
@@ -8,6 +8,8 @@ Pages:
|
||||
/ the status for people: plain text for curl and wget, a styled page
|
||||
(Tailwind CSS from a CDN) for a browser
|
||||
/status the status as JSON
|
||||
/status/full
|
||||
the whole status as JSON, whatever web.status_content says, for the Router Manager
|
||||
/config the configuration without its secrets, and what the router resolved and
|
||||
runs, with the installed Caddy and where it comes from, as JSON
|
||||
/users the users in the router's directory, whether or not a P2S system is enabled: list,
|
||||
@@ -40,7 +42,7 @@ Pages:
|
||||
the databases of each PostgreSQL cluster on the router with their sizes, as JSON (see
|
||||
postgresql_databases()); /config has the clusters' settings
|
||||
|
||||
/config, /metrics/history, /postgresql, /users, /service-accounts, /groups, /site-to-site, /backup, /ca and /account need an HS256 JWT in an Authorization: Bearer header signed
|
||||
/status/full, /config, /metrics/history, /postgresql, /users, /service-accounts, /groups, /site-to-site, /backup, /ca and /account need an HS256 JWT in an Authorization: Bearer header signed
|
||||
with vpnrouter_auth.WEB_KEY (see vpnrouter_auth.verify). A token of /auth names a user in sub;
|
||||
/account serves that user, and the other paths need a member of vpnrouter_users.ADMIN_GROUP. A
|
||||
token of vpn-router-token has the admin claim and names no user; it may call every path but
|
||||
@@ -779,10 +781,11 @@ def backup(method, path, body):
|
||||
def ca(method, path, body):
|
||||
"""Answer a /ca request with vpnrouter_pki: the HTTP status and the JSON document.
|
||||
|
||||
GET /ca {"settings", "templates", "status", "certificates"}: the pki section,
|
||||
the templates with the built-in ones, the root, the issuing CAs, the
|
||||
retired CAs and the warnings (vpnrouter_pki.status), and the
|
||||
certificates issued, newest first
|
||||
GET /ca {"settings", "templates", "extended_key_usages", "status",
|
||||
"certificates"}: the pki section, the templates with the built-in
|
||||
ones, the extended key usages, each {oid, builtin}, the root, the
|
||||
issuing CAs, the retired CAs and the warnings (vpnrouter_pki.status),
|
||||
and the certificates issued, newest first
|
||||
PUT /ca change any of the settings of the pki section
|
||||
POST /ca/certificates issue a certificate: {"ca", "template", "csr"} answers {"serial",
|
||||
"certificate", "chain"}; {"ca", "template", "subject", "sans",
|
||||
@@ -809,6 +812,8 @@ def ca(method, path, body):
|
||||
return 200, vpnrouter_pki.bundle(route[2])
|
||||
if route == ('GET',):
|
||||
return 200, {'settings': cfg['pki'], 'templates': vpnrouter.pki_templates(cfg),
|
||||
'extended_key_usages': {name: {'oid': oid, 'builtin': name in vpnrouter.EXTENDED_KEY_USAGES}
|
||||
for name, oid in vpnrouter.extended_key_usages(cfg).items()},
|
||||
'status': vpnrouter_pki.status(cfg), 'certificates': vpnrouter_pki.certificates()}
|
||||
if route == ('PUT',):
|
||||
settings = {option: body[option] for option in vpnrouter.SCHEMA['pki'] if option in body}
|
||||
@@ -1042,7 +1047,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
directory = next((name for name in ('users', 'service-accounts', 'groups', 'account', 'site-to-site', 'backup',
|
||||
'ca')
|
||||
if path == f'/{name}' or path.startswith(f'/{name}/')), None)
|
||||
protected = path in ('/config', '/metrics/history', '/postgresql') or directory is not None
|
||||
protected = path in ('/status/full', '/config', '/metrics/history', '/postgresql') or directory is not None
|
||||
signing = path in ('/auth', '/auth/refresh', '/auth/logout')
|
||||
state = current() if protected or signing else {}
|
||||
claims = self.claims(state['key']) if protected and state['key'] else None
|
||||
@@ -1067,6 +1072,9 @@ class Handler(BaseHTTPRequestHandler):
|
||||
elif protected and (refused := self.refusal(claims, path, payload)):
|
||||
code, kind = refused[0], 'application/json'
|
||||
body = json.dumps({'error': refused[1]}, indent=2).encode() + b'\n'
|
||||
elif path == '/status/full' and self.command in ('GET', 'HEAD'):
|
||||
document = shown(self.status(), 'full', self.client())
|
||||
body, kind, code = json.dumps(document, indent=2).encode() + b'\n', 'application/json', 200
|
||||
elif path == '/config' and self.command in ('GET', 'HEAD'):
|
||||
body, kind, code = json.dumps(details(state), indent=2).encode() + b'\n', 'application/json', 200
|
||||
elif path == '/postgresql' and self.command in ('GET', 'HEAD'):
|
||||
|
||||
@@ -92,6 +92,9 @@
|
||||
"days": 397
|
||||
}
|
||||
},
|
||||
"extended_key_usages": {
|
||||
"smartcard_logon": "1.3.6.1.4.1.311.20.2.2"
|
||||
},
|
||||
"root_key_warning": true
|
||||
},
|
||||
"metrics": {
|
||||
|
||||
Reference in new issue
Block a user