Skip to content

Commit

Permalink
Merge branch 'ypid-ca-hardening-and-status-uri-change'
Browse files Browse the repository at this point in the history
  • Loading branch information
drybjed committed Apr 25, 2017
2 parents 185e8e8 + afa9b3c commit 50bc478
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 15 deletions.
13 changes: 13 additions & 0 deletions CHANGES.rst
Expand Up @@ -22,6 +22,19 @@ Added
- Add custom pre and post task hooks to allow more flexibility with PKI
management. [muelli_]

- Support to change or disable CRL and OCSP for PKI authorities using
``item.crl`` and ``item.ocsp``. [ypid_]

- Use X.509 Name Constraints to limit PKI authorities to ``item.domain`` by default.
This greatly reduces the damage that a compromised PKI authority could do
(which is trusted by the cluster by default).
Previously, any CA managed by ``debops.pki`` could happily issue certificates
for any domain and clients would accept them which is probably not what you want.
Use ``item.name_constraints`` if you want to change the default.
Note that this new default is only effective for newly created CAs.
Refer to `A Web PKI x509 certificate primer <https://developer.mozilla.org/en-US/docs/Mozilla/Security/x509_Certificates>`_
for details. [ypid_]


`debops.pki v0.2.14`_ - 2016-11-21
----------------------------------
Expand Down
2 changes: 1 addition & 1 deletion docs/acme-integration.rst
Expand Up @@ -196,7 +196,7 @@ Names`_), which does not include the ``example.com`` apex domain.
acme_default_subdomains: []
# Can also include different domains like 'mail.example.org'
# in the same realm.
acme_domains: [ 'logs.example.com', 'mon.example.com' ]
acme_domains: [ 'mon.example.com' ]
# acme_ca: 'le-staging'
ACME configuration variables
Expand Down
31 changes: 31 additions & 0 deletions docs/defaults-detailed.rst
Expand Up @@ -230,3 +230,34 @@ respectively:
- 'uri:https://{{ ansible_domain }}/'
- 'dns:*.{{ ansible_domain }}'
- 'dns:{{ ansible_domain }}'
pki_authorities
---------------

The set of :envvar:`pki_authorities` lists can be used to define internal
Certificate Authorities managed on an Ansible Controller.

List of supported parameters (incomplete):

``crl``
The CRL URL to include in certificates which can be used for certificate
status checking. The default is ``True`` which will result in ``http://\$name.\$domain_suffix/crl/``.
It can be set to ``False`` to not include a CRL URL in certificates.
Any other value (not matching :regexp:`^(?:[Tt]rue|[Ff]alse)$`) will be included as is as CRL URL.

``ocsp``
The OCSP URL to include in certificates which can be used for certificate
status checking. The default is ``True`` which will result in ``http://\$name.\$domain_suffix/ocsp/``.
It can be set to ``False`` to not include a OCSP URL in certificates.
Any other value (not matching :regexp:`^(?:[Tt]rue|[Ff]alse)$`) will be included as is as OCSP URL.

``name_constraints``
The X.509 Name Constraints certificate extension to include in certificates
which will be used during certificate verification to ensure that the CA is
authorized to issue a certificate for the name in question.
The extension is set to critical which REQUIRES X.509 libraries to support it or to return an error.
This is done following common recommendations
(ref: `Which properties of a X.509 certificate should be critical and which not? <https://security.stackexchange.com/questions/30974/which-properties-of-a-x-509-certificate-should-be-critical-and-which-not>`_).
The default is ``True`` which will result in ``critical, permitted;DNS:${config_domain}``.
It can be set to ``False`` to not include X.509 Name Constraints in certificates.
Any other value (not matching :regexp:`^(?:[Tt]rue|[Ff]alse)$`) will be included as is as X.509 Name Constraint.
108 changes: 98 additions & 10 deletions files/secret/pki/lib/pki-authority
@@ -1,7 +1,9 @@
#!/usr/bin/env bash
# vim: foldmarker={{{,}}}:foldmethod=marker

# pki-authority: CA-side PKI management
# Copyright (C) 2016 Maciej Delmanowski <drybjed@gmail.com>
# Copyright (C) 2016-2017 Robin Schneider <ypid@riseup.net>
# Homepage: https://debops.org/

set -o nounset -o pipefail -o errexit
Expand Down Expand Up @@ -106,6 +108,57 @@ chgrp_idempotent () {
}
# }}}

get_openssl_ocsp_uri_directive () {
local ocsp_uri
case "${config['ocsp']}" in
true|True)
ocsp_uri="OCSP;URI.0 = \$ocsp_url"
;;
false|False)
ocsp_uri=""
;;
*)
ocsp_uri="OCSP;URI.0 = ${config['ocsp']}"
;;
esac
echo "$ocsp_uri"
}

get_openssl_name_constraints_directive () {
local config_domain="${1}"
local name_constraints
case "${config['name_constraints']}" in
true|True)
name_constraints="nameConstraints = critical, permitted;DNS:${config_domain}"
;;
false|False)
name_constraints=""
;;
*)
name_constraints="nameConstraints = ${config['name_constraints']}"
;;
esac

echo "$name_constraints"
}

get_openssl_crl_distribution_points_directive () {
local crl_distribution_points=''
case "${config['crl']}" in
true|True)
crl_distribution_points="crlDistributionPoints = @crl_info"
;;
false|False)
crl_distribution_points=""
;;
*)
crl_distribution_points="crlDistributionPoints = ${config['crl']}"
;;
esac

echo "$crl_distribution_points"
}

initialize_environment () {

declare -gA config
Expand All @@ -126,6 +179,7 @@ initialize_environment () {
config["ca_type"]=""
config["issuer_name"]=""
config["alt_authority"]=""
config["name_constraints"]=""

config["pki_default_sign_base"]="365"
config["pki_default_root_sign_multiplier"]="12"
Expand All @@ -136,6 +190,8 @@ initialize_environment () {
config["ca_sign_days"]=""
config["cert_sign_days"]=""
config["key_size"]="4096"
config["crl"]="true"
config["ocsp"]="true"

config["public_dir_group"]="$(id -g)"
config["public_file_group"]="$(id -g)"
Expand Down Expand Up @@ -165,8 +221,7 @@ enter_authority () {

if [ -r "${config_file}" ] ; then

# FIXME: Add a code that checks if the config file has no dangerous code inside
# shellcheck disable=SC1090
# shellcheck source=/dev/null
. "${config_file}"
fi

Expand Down Expand Up @@ -259,6 +314,8 @@ create_openssl_selfsign_config () {
local config_ca_type="${4}"

if [ -n "${config_file}" ] && [ ! -r "${config_file}" ] ; then
local ocsp_uri
ocsp_uri="$(get_openssl_ocsp_uri_directive)"

cat << EOF > "${config_file}"
# Configuration file generated by pki-authority
Expand Down Expand Up @@ -294,7 +351,7 @@ URI.0 = \$crl_url
[ issuer_info ]
caIssuers;URI.0 = \$aia_url
OCSP;URI.0 = \$ocsp_url
${ocsp_uri}
[ extension_ocsp ]
authorityKeyIdentifier = keyid:always
Expand All @@ -314,20 +371,26 @@ emailAddress = optional
[ extension_default ]
EOF

local name_constraints=''
name_constraints="$(get_openssl_name_constraints_directive "$config_domain")"
local crl_distribution_points=''
crl_distribution_points="$(get_openssl_crl_distribution_points_directive)"
if [ -z "${config_ca_type}" ] || [ "${config_ca_type}" = "root" ] ; then
cat << EOF >> "${config_file}"
basicConstraints = critical, CA:TRUE
keyUsage = critical, keyCertSign, cRLSign
subjectKeyIdentifier = hash
${name_constraints}
EOF
elif [ "${config_ca_type}" = "service" ] ; then
cat << EOF >> "${config_file}"
authorityInfoAccess = @issuer_info
basicConstraints = critical, CA:TRUE, pathlen:0
crlDistributionPoints = @crl_info
${crl_distribution_points}
keyUsage = critical, keyCertSign, cRLSign
subjectKeyIdentifier = hash
${name_constraints}
EOF
fi
Expand Down Expand Up @@ -384,6 +447,8 @@ create_openssl_sign_config () {
local config_issuer="${5}"

if [ -n "${config_file}" ] && [ ! -r "${config_file}" ] ; then
local ocsp_uri
ocsp_uri="$(get_openssl_ocsp_uri_directive)"

cat << EOF > "${config_file}"
# Configuration file generated by pki-authority
Expand Down Expand Up @@ -443,7 +508,7 @@ URI.0 = \$crl_url
[ issuer_info ]
caIssuers;URI.0 = \$aia_url
OCSP;URI.0 = \$ocsp_url
${ocsp_uri}
[ extension_ocsp ]
authorityKeyIdentifier = keyid:always
Expand Down Expand Up @@ -489,15 +554,20 @@ emailAddress = optional
EOF
fi

local name_constraints=''
name_constraints="$(get_openssl_name_constraints_directive "$config_domain")"
local crl_distribution_points=''
crl_distribution_points="$(get_openssl_crl_distribution_points_directive)"
if [ -z "${config_issuer}" ] && [ -z "${config_ca_type}" ] || [ "${config_ca_type}" = "root" ] ; then
cat << EOF >> "${config_file}"
[ extension_default ]
authorityInfoAccess = @issuer_info
authorityKeyIdentifier = keyid:always
basicConstraints = critical, CA:TRUE, pathlen:0
crlDistributionPoints = @crl_info
${crl_distribution_points}
keyUsage = critical, keyCertSign, cRLSign
subjectKeyIdentifier = hash
${name_constraints}
EOF
elif [ -z "${config_issuer}" ] && [ "${config_ca_type}" = "service" ] ; then
Expand All @@ -506,7 +576,7 @@ EOF
authorityInfoAccess = @issuer_info
authorityKeyIdentifier = keyid:always, issuer:always
basicConstraints = critical, CA:FALSE
crlDistributionPoints = @crl_info
${crl_distribution_points}
extendedKeyUsage = clientAuth, serverAuth
keyUsage = critical, digitalSignature, keyEncipherment
subjectKeyIdentifier = hash
Expand All @@ -518,7 +588,7 @@ EOF
authorityInfoAccess = @issuer_info
authorityKeyIdentifier = keyid:always, issuer:always
basicConstraints = critical, CA:FALSE
crlDistributionPoints = @crl_info
${crl_distribution_points}
extendedKeyUsage = clientAuth, serverAuth
keyUsage = critical, digitalSignature, keyEncipherment
subjectKeyIdentifier = hash
Expand Down Expand Up @@ -1157,7 +1227,7 @@ sub_sign () {

if key_exists args "name" ; then

enter_authority ${args['name']}
enter_authority "${args['name']}"

local library="${config['pki_library']}"
local input
Expand Down Expand Up @@ -1322,6 +1392,24 @@ sub_new-ca () {
key-size=*)
args["key_size"]=${OPTARG#*=}
;;
crl)
args["crl"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
;;
crl=*)
args["crl"]=${OPTARG#*=}
;;
ocsp)
args["ocsp"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
;;
ocsp=*)
args["ocsp"]=${OPTARG#*=}
;;
name-constraints)
args["name_constraints"]="${!OPTIND}"; OPTIND=$(( OPTIND + 1 ))
;;
name-constraints=*)
args["name_constraints"]=${OPTARG#*=}
;;
*)
if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
echo "Unknown option --${OPTARG}" >&2
Expand All @@ -1346,7 +1434,7 @@ sub_new-ca () {

if key_exists args "name" ; then

enter_authority ${args['name']}
enter_authority "${args['name']}"

local config_changed="false"

Expand Down
5 changes: 3 additions & 2 deletions files/usr/local/lib/pki/pki-realm
@@ -1,7 +1,9 @@
#!/usr/bin/env bash
# vim: foldmarker={{{,}}}:foldmethod=marker

# pki-realm: client-side PKI management
# Copyright (C) 2016 Maciej Delmanowski <drybjed@gmail.com>
# Copyright (C) 2016-2017 Robin Schneider <ypid@riseup.net>
# Homepage: https://debops.org/

set -o nounset -o pipefail -o errexit
Expand Down Expand Up @@ -388,8 +390,7 @@ enter_realm () {

if [ -r "${config_file}" ] ; then

# FIXME: Add a code that checks if the config file has no dangerous code inside
# shellcheck disable=SC1090
# shellcheck source=/dev/null
. "${config_file}"
fi

Expand Down
6 changes: 4 additions & 2 deletions tasks/main.yml
Expand Up @@ -305,8 +305,7 @@
environment:
PKI_ROOT: '{{ secret + "/pki" }}'
PKI_LIBRARY: '{{ item.pki_ca_library | d(pki_ca_library) }}'
PKI_CA_CERTIFICATES: '{{ secret + "/pki/ca-certificates/" +
(item.ca_certificates_path | d(pki_ca_certificates_path)) }}'
PKI_CA_CERTIFICATES: '{{ secret + "/pki/ca-certificates/" + (item.ca_certificates_path | d(pki_ca_certificates_path)) }}'
command: ./lib/pki-authority init --name "{{ item.name }}"
--default-sign-base "{{ pki_default_sign_base }}"
--root-sign-multiplier "{{ pki_default_root_sign_multiplier }}"
Expand Down Expand Up @@ -340,6 +339,9 @@
--system-ca "{{ (item.system_ca | d(True)) | bool | lower }}"
--alt-authority "{{ item.alt_authority | d('') }}"
--key-size "{{ item.key_size | d('') }}"
--crl "{{ item.crl | d(True) }}"
--ocsp "{{ item.ocsp | d(True) }}"
--name-constraints "{{ item.name_constraints | d(True) }}"
args:
chdir: '{{ secret + "/pki" }}'
creates: '{{ secret + "/pki/authorities/" + item.name + "/subject/cert.pem" }}'
Expand Down

0 comments on commit 50bc478

Please sign in to comment.