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.
Release / release (push) Successful in 20s
Release / release (push) Successful in 20s
This commit is contained in:
1 parent
a245ec26cd
commit
d648d294ec
6 files changed
+92
-33
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` | `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` | `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` | `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 |
|
| `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` | `enabled` | boolean | `true` | The metrics service: collection, history and `/metrics` |
|
||||||
| `metrics` | `listen` | string | `internal` | The interfaces `/metrics` is served on: `internal`, `external` or `both` |
|
| `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 |
|
| `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 |
|
| `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` |
|
| `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 |
|
| `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` |
|
| `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
|
```json
|
||||||
"pki": {
|
"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": {
|
"templates": {
|
||||||
"server": { "days": 397 },
|
"server": { "days": 397 },
|
||||||
"web": { "key": "ec-p256", "days": 90, "key_usage": ["digital_signature"],
|
"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
|
### Issuing and revoking
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -1072,7 +1084,7 @@ A change is made, checked and applied as for `/site-to-site`.
|
|||||||
|
|
||||||
| Request | Body | Answer |
|
| 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"}` |
|
| `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", "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 |
|
| `POST /ca/certificates` | `{"ca", "template", "subject", "sans", "password"}` | 201 and `{"serial", "pfx"}`, a PKCS#12 file in base64 |
|
||||||
@@ -1086,6 +1098,7 @@ The answer of `GET /ca` holds:
|
|||||||
|
|
||||||
- **`settings`:** the `pki` section.
|
- **`settings`:** the `pki` section.
|
||||||
- **`templates`:** every template, the built-in ones included, with all its options.
|
- **`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`.
|
- **`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`.
|
- **`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,17 @@
|
|||||||
|
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
|
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.
|
fix: /status/full of the status web service gives an authenticated client the whole status, whatever web.status_content says.
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ SCHEMA = {
|
|||||||
'pki': {'root_name': 'VPN Router Root CA', 'root_days': 7300, 'ca_days': 3650, 'key': 'rsa-4096',
|
'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',
|
'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': {},
|
'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',
|
'metrics': {'enabled': True, 'listen': 'internal', 'port': 9464, 'https_port': 9465, 'service_account': 'metrics',
|
||||||
'otlp_endpoint': '', 'otlp_username': '', 'otlp_password': '',
|
'otlp_endpoint': '', 'otlp_username': '', 'otlp_password': '',
|
||||||
'keep_details': '24 hours', 'keep_hourly': '1 month', 'keep_daily': '3 months',
|
'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.
|
#: 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')
|
KEY_USAGES = ('digital_signature', 'content_commitment', 'key_encipherment', 'data_encipherment', 'key_agreement')
|
||||||
|
|
||||||
#: The extended key usages a template may give.
|
#: The built-in extended key usages a template may give, name -> OID; pki.extended_key_usages adds custom ones.
|
||||||
EXTENDED_KEY_USAGES = ('server_auth', 'client_auth', 'code_signing', 'email_protection', 'time_stamping',
|
EXTENDED_KEY_USAGES = {
|
||||||
'ocsp_signing', 'ipsec_ike', 'ike_intermediate')
|
'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.
|
#: 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')
|
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.
|
#: 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': [],
|
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.
|
#: The built-in templates; pki.templates changes their options and adds templates of its own.
|
||||||
PKI_TEMPLATES = {
|
PKI_TEMPLATES = {
|
||||||
@@ -502,6 +516,14 @@ def validate(cfg):
|
|||||||
raise ConfigError('pki.publish: http is served by the web service, which needs web.enabled')
|
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'):
|
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')
|
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():
|
for name, template in cfg['pki']['templates'].items():
|
||||||
prefix = f'pki.templates.{name}'
|
prefix = f'pki.templates.{name}'
|
||||||
if not PKI_NAME.match(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)}')
|
raise ConfigError(f'{prefix}.key: {template["key"]!r} is not one of {", ".join(PKI_KEYS)}')
|
||||||
if not 1 <= template['days'] <= 36500:
|
if not 1 <= template['days'] <= 36500:
|
||||||
raise ConfigError(f'{prefix}.days must be 1 to 36500 days')
|
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)):
|
('sans', SAN_TYPES)):
|
||||||
for item in template[option]:
|
for item in template[option]:
|
||||||
if item not in allowed:
|
if item not in allowed:
|
||||||
raise ConfigError(f'{prefix}.{option}: {item!r} is not one of {", ".join(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']:
|
if not template['key_usage']:
|
||||||
raise ConfigError(f'{prefix}.key_usage needs at least one usage')
|
raise ConfigError(f'{prefix}.key_usage needs at least one usage')
|
||||||
if template['subject'] not in TEMPLATE_SUBJECTS:
|
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)]}
|
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):
|
def issuing_cas(cfg):
|
||||||
"""The issuing CAs, name -> {name, templates, acme, builtin}: p2s and s2s (BUILTIN_CAS) with their common names from
|
"""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."""
|
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 import hashes, serialization
|
||||||
from cryptography.hazmat.primitives.asymmetric import ec, rsa
|
from cryptography.hazmat.primitives.asymmetric import ec, rsa
|
||||||
from cryptography.hazmat.primitives.serialization import pkcs12
|
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
|
||||||
import vpnrouter_ldap
|
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>.
|
#: The suffix of a retired CA's name: -<time of the retirement>.
|
||||||
RETIRED_SUFFIX = re.compile(r'-\d{14}$')
|
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.
|
#: The revocation reasons revoke takes.
|
||||||
REASONS = ('unspecified', 'key_compromise', 'affiliation_changed', 'superseded', 'cessation_of_operation',
|
REASONS = ('unspecified', 'key_compromise', 'affiliation_changed', 'superseded', 'cessation_of_operation',
|
||||||
'privilege_withdrawn')
|
'privilege_withdrawn')
|
||||||
@@ -457,13 +445,19 @@ def general_names(values, allowed):
|
|||||||
return names
|
return names
|
||||||
|
|
||||||
|
|
||||||
def leaf_extensions(template, names):
|
def leaf_extensions(cfg, template, names):
|
||||||
"""The extensions of a certificate of the template with the subject alternative 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},
|
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)
|
key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False)
|
||||||
extensions = [(x509.BasicConstraints(ca=False, path_length=None), True), (usage, True)]
|
extensions = [(x509.BasicConstraints(ca=False, path_length=None), True), (usage, True)]
|
||||||
if template['extended_key_usage']:
|
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:
|
if names:
|
||||||
extensions.append((x509.SubjectAlternativeName(names), False))
|
extensions.append((x509.SubjectAlternativeName(names), False))
|
||||||
return extensions
|
return extensions
|
||||||
@@ -589,7 +583,7 @@ def ensure_router_cert(connection, cfg, ctx, name, urls):
|
|||||||
template = vpnrouter.pki_templates(cfg)[template_name]
|
template = vpnrouter.pki_templates(cfg)[template_name]
|
||||||
key = new_key(template['key'])
|
key = new_key(template['key'])
|
||||||
cert = sign(subject, key.public_key(), issuer['cert'], issuer['key'], template['days'],
|
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)
|
record(connection, cert, ca, 'router', template_name, key)
|
||||||
for dn, _, _ in current:
|
for dn, _, _ in current:
|
||||||
drop_key(connection, dn)
|
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)
|
subject = check_subject(values['subject'], subject)
|
||||||
names = general_names(([subject] if values['subject'] == 'dns' else []) + list(sans),
|
names = general_names(([subject] if values['subject'] == 'dns' else []) + list(sans),
|
||||||
set(values['sans']) | ({'dns'} if values['subject'] == 'dns' else set()))
|
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)
|
vpnrouter.pki_base_urls(cfg), ca)
|
||||||
record(connection, cert, ca, 'external', template)
|
record(connection, cert, ca, 'external', template)
|
||||||
root = authority(connection, 'root')['cert']
|
root = authority(connection, 'root')['cert']
|
||||||
@@ -880,7 +874,7 @@ def issue_user(cfg, name, email):
|
|||||||
issuer, values = issuer_of(connection, cfg, 'p2s', 'vpn-user')
|
issuer, values = issuer_of(connection, cfg, 'p2s', 'vpn-user')
|
||||||
key = new_key(values['key'])
|
key = new_key(values['key'])
|
||||||
cert = sign(name, key.public_key(), issuer['cert'], issuer['key'], values['days'],
|
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')
|
vpnrouter.pki_base_urls(cfg), 'p2s')
|
||||||
for dn, _, _ in holders(connection, 'p2s', user_condition(name)):
|
for dn, _, _ in holders(connection, 'p2s', user_condition(name)):
|
||||||
drop_key(connection, dn)
|
drop_key(connection, dn)
|
||||||
|
|||||||
@@ -781,10 +781,11 @@ def backup(method, path, body):
|
|||||||
def ca(method, path, body):
|
def ca(method, path, body):
|
||||||
"""Answer a /ca request with vpnrouter_pki: the HTTP status and the JSON document.
|
"""Answer a /ca request with vpnrouter_pki: the HTTP status and the JSON document.
|
||||||
|
|
||||||
GET /ca {"settings", "templates", "status", "certificates"}: the pki section,
|
GET /ca {"settings", "templates", "extended_key_usages", "status",
|
||||||
the templates with the built-in ones, the root, the issuing CAs, the
|
"certificates"}: the pki section, the templates with the built-in
|
||||||
retired CAs and the warnings (vpnrouter_pki.status), and the
|
ones, the extended key usages, each {oid, builtin}, the root, the
|
||||||
certificates issued, newest first
|
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
|
PUT /ca change any of the settings of the pki section
|
||||||
POST /ca/certificates issue a certificate: {"ca", "template", "csr"} answers {"serial",
|
POST /ca/certificates issue a certificate: {"ca", "template", "csr"} answers {"serial",
|
||||||
"certificate", "chain"}; {"ca", "template", "subject", "sans",
|
"certificate", "chain"}; {"ca", "template", "subject", "sans",
|
||||||
@@ -811,6 +812,8 @@ def ca(method, path, body):
|
|||||||
return 200, vpnrouter_pki.bundle(route[2])
|
return 200, vpnrouter_pki.bundle(route[2])
|
||||||
if route == ('GET',):
|
if route == ('GET',):
|
||||||
return 200, {'settings': cfg['pki'], 'templates': vpnrouter.pki_templates(cfg),
|
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()}
|
'status': vpnrouter_pki.status(cfg), 'certificates': vpnrouter_pki.certificates()}
|
||||||
if route == ('PUT',):
|
if route == ('PUT',):
|
||||||
settings = {option: body[option] for option in vpnrouter.SCHEMA['pki'] if option in body}
|
settings = {option: body[option] for option in vpnrouter.SCHEMA['pki'] if option in body}
|
||||||
|
|||||||
@@ -92,6 +92,9 @@
|
|||||||
"days": 397
|
"days": 397
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"extended_key_usages": {
|
||||||
|
"smartcard_logon": "1.3.6.1.4.1.311.20.2.2"
|
||||||
|
},
|
||||||
"root_key_warning": true
|
"root_key_warning": true
|
||||||
},
|
},
|
||||||
"metrics": {
|
"metrics": {
|
||||||
|
|||||||
Reference in new issue
Block a user