diff --git a/REFERENCE.md b/REFERENCE.md index d137c74821975a3aa726fdb54c4cd8aa029851a2..73a133d7c0a0ebc038f5fb05db017b6af1bebcc7 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -9,8 +9,10 @@ #### Public Classes * [`cfssl`](#cfssl): Install and configure CFSSL, serve process and CRL generation. +* [`cfssl::ca::gencrls`](#cfsslcagencrls): Creates services to generate CRL for a list of CA * [`cfssl::ca::intermediates`](#cfsslcaintermediates): Creates `cfssl::ca::intermediate` defined types. * [`cfssl::ca::root`](#cfsslcaroot): Init a selfsigned root authority +* [`cfssl::params`](#cfsslparams): A short summary of the purpose of this class #### Private Classes @@ -18,8 +20,16 @@ ### Defined types +#### Public Defined types + * [`cfssl::ca::intermediate`](#cfsslcaintermediate): Creates an intermediate authority signed by root authority +#### Private Defined types + +* `cfssl::ca::gencrl`: Creates a service to generate CRL for a CA + +Creates a service to generate CRL for a CA + ### Data types * [`Cfssl::Authkey`](#cfsslauthkey): Struct representing authentication key used by CFSSL serve during sign requests @@ -274,6 +284,37 @@ The Certificate authority served with CFSSL serve Default value: ``undef`` +### <a name="cfsslcagencrls"></a>`cfssl::ca::gencrls` + +Creates services to generate CRL for a list of CA + +#### Examples + +##### + +```puppet +class { 'cfssl::ca::gencrls': + authorities => [ + 'MYEXEMPLE ROOT CA', + 'MYEXEMPLE INTERMDIATE CA', + ], +} +``` + +#### Parameters + +The following parameters are available in the `cfssl::ca::gencrls` class: + +* [`authorities`](#authorities) + +##### <a name="authorities"></a>`authorities` + +Data type: `Array[String]` + +A list of authority CN's, already defined. + +Default value: `[]` + ### <a name="cfsslcaintermediates"></a>`cfssl::ca::intermediates` Creates `cfssl::ca::intermediate` defined types. @@ -369,6 +410,18 @@ Cryptographic algorithm used for creating key pairs. Default value: `{ algo => 'rsa', size => 2048 }` +### <a name="cfsslparams"></a>`cfssl::params` + +A description of what this class does + +#### Examples + +##### + +```puppet +include cfssl::params +``` + ## Defined types ### <a name="cfsslcaintermediate"></a>`cfssl::ca::intermediate` diff --git a/manifests/ca/gencrl.pp b/manifests/ca/gencrl.pp new file mode 100644 index 0000000000000000000000000000000000000000..cdafa92b2b8b400614171ed31308f4d6041007bf --- /dev/null +++ b/manifests/ca/gencrl.pp @@ -0,0 +1,60 @@ +# @summary Creates a service to generate CRL for a CA +# +# Creates a service to generate CRL for a CA +# +# @example +# cfssl::ca::gencrl { 'MYEXEMPLE INTERMDIATE CA': } +# +# @api private +# +# @param db_config_json Name of JSON config file for database usaed by CFSSL +# @param systemd_unitdir Directory where systemd units are created +define cfssl::ca::gencrl ( + String $db_config_json = $cfssl::params::db_config_json, + Stdlib::Absolutepath $systemd_unitdir = $cfssl::params::systemd_unitdir, +) { + assert_private() + + require cfssl::ca::root + + $_ca = regsubst($name, '\s', '', 'G') + + if $cfssl::crl_manage { + ensure_packages('coreutils', { ensure => 'present' }) + + file { "${systemd_unitdir}/cfssl-gencrl-${_ca}.service": + ensure => file, + mode => '0644', + owner => 0, + group => 0, + content => epp('cfssl/cfssl-gencrl.service.epp', { 'ca' => $_ca }), + } + ~> service { "cfssl-gencrl-${_ca}.service": + ensure => 'running', + enable => true, + provider => 'systemd', + } + + file { "${systemd_unitdir}/cfssl-gencrl-${_ca}.timer": + ensure => file, + mode => '0644', + owner => 0, + group => 0, + content => epp('cfssl/cfssl-gencrl.timer.epp', { 'ca' => $_ca }), + } + ~> service { "cfssl-gencrl-${_ca}.timer": + ensure => 'running', + enable => true, + provider => 'systemd', + } + } else { + service { ["cfssl-gencrl-${_ca}.service" ,"cfssl-gencrl-${_ca}.timer"]: + ensure => 'stopped', + enable => false, + provider => 'systemd', + } + -> file { ["${systemd_unitdir}/cfssl-gencrl-${_ca}.service" ,"${systemd_unitdir}/cfssl-gencrl-${_ca}.timer"]: + ensure => absent, + } + } +} diff --git a/manifests/ca/gencrls.pp b/manifests/ca/gencrls.pp new file mode 100644 index 0000000000000000000000000000000000000000..88d29ee27e90be54eef8e243e20ba852b156dc06 --- /dev/null +++ b/manifests/ca/gencrls.pp @@ -0,0 +1,22 @@ +# @summary Creates services to generate CRL for a list of CA +# +# Creates services to generate CRL for a list of CA +# +# @example +# class { 'cfssl::ca::gencrls': +# authorities => [ +# 'MYEXEMPLE ROOT CA', +# 'MYEXEMPLE INTERMDIATE CA', +# ], +# } +# +# @param authorities A list of authority CN's, already defined. +# +class cfssl::ca::gencrls ( + Array[String] $authorities = [], +) { + $authorities.each | String[1] $_authority | { + cfssl::ca::gencrl { $_authority: + } + } +} diff --git a/manifests/goose.pp b/manifests/goose.pp index 363e106f8638c7688bffd63d5544fe5c081eb24d..133ec78a5061caa98d4225b27e27c6646fd3d7b1 100644 --- a/manifests/goose.pp +++ b/manifests/goose.pp @@ -10,12 +10,12 @@ class cfssl::goose { assert_private() # stuffs for goose : a database migration tool used by CFSSL - package { 'golang-1.16': + package { $cfssl::params::go_package: ensure => present, } -> file { '/usr/local/bin/go': ensure => link, - target => '/usr/lib/go-1.16/bin/go', + target => $cfssl::params::go_targetlink, } -> exec { 'install goose': command => '/usr/local/bin/go get bitbucket.org/liamstask/goose/cmd/goose', diff --git a/manifests/init.pp b/manifests/init.pp index 5c3f5251aad5c9b2be156ecc23404a60ca6c1961..4970f61fc96feb0622d042f70425eae5bb3c084d 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -73,17 +73,11 @@ class cfssl ( Integer $crl_expiry = 604800, String[1] $crl_gentimer = '*:00:00', Optional[String[1]] $serve_ca = undef, -) { +) inherits cfssl::params { include cfssl::goose include postgresql::server - $_binaries = ['cfssljson','cfssl-certinfo'] - $_systemd_unit_file = '/etc/systemd/system/cfssl.service' - $_goose_cfssldbmigrate_path = "/home/${sysuser}/goose-cfssldbmigrate" - $_serve_config_json = 'serve-config.json' - $_db_config_json = 'db-config.json' - $_systemd_unitdir = '/etc/systemd/system' - $_crlunits = ['cfssl-gencrl.service', 'cfssl-gencrl.timer'] + $_goose_cfssldbmigrate_path = "/home/${cfssl::sysuser}/goose-cfssldbmigrate" group { $sysgroup : ensure => present, @@ -110,7 +104,7 @@ class cfssl ( require => User[$sysuser], } - $_binaries.each | String $_bin | { + $cfssl::params::binaries.each | String $_bin | { $_archiveurn = "v${version}/${_bin}_${version}_linux_amd64" archive { "${binpath}/${_bin}" : @@ -141,14 +135,14 @@ class cfssl ( password => postgresql::postgresql_password($dbuser, $dbpassword), } - file { "${confdir}/${_db_config_json}": + file { "${confdir}/${cfssl::params::db_config_json}": ensure => file, mode => '0600', owner => $sysuser, group => $sysgroup, content => epp('cfssl/db-config.json.epp'), } - file { "${confdir}/${_serve_config_json}": + file { "${confdir}/${cfssl::params::serve_config_json}": ensure => file, mode => '0600', owner => $sysuser, @@ -175,13 +169,13 @@ class cfssl ( command => "/home/${sysuser}/go/bin/goose --env puppetmigrate -path ${_goose_cfssldbmigrate_path}/certdb/pg up", user => $sysuser, environment => ["HOME=/home/${sysuser}/"], + onlyif => "/home/${sysuser}/go/bin/goose --env puppetmigrate -path ${_goose_cfssldbmigrate_path}/certdb/pg status | grep -q 'Pending'", require => [ User[$sysuser], Vcsrepo[$_goose_cfssldbmigrate_path], Postgresql::Server::Db[$dbname], + Class[cfssl::goose], ], - subscribe => Vcsrepo[$_goose_cfssldbmigrate_path], - refreshonly => true, } class { 'cfssl::ca::root': @@ -201,7 +195,7 @@ class cfssl ( } if $serve_ca { - file { $_systemd_unit_file: + file { $cfssl::params::systemd_unit_file: ensure => file, mode => '0644', owner => 0, @@ -215,59 +209,13 @@ class cfssl ( Archive["${binpath}/cfssl"], Postgresql::Server::Db[$dbname], Exec['goose pg up'], - File["${confdir}/${_serve_config_json}"], - File["${confdir}/${_db_config_json}"], + File["${confdir}/${cfssl::params::serve_config_json}"], + File["${confdir}/${cfssl::params::db_config_json}"], Class['cfssl::ca::root'], ], subscribe => Archive["${binpath}/cfssl"], provider => 'systemd', } - - if $cfssl::crl_manage { - ensure_packages(['jq','coreutils'], { ensure => 'present' }) - - file { "${cfssl::binpath}/cfssl-gencrl.sh": - ensure => file, - mode => '0755', - owner => 0, - group => 0, - content => epp('cfssl/cfssl-gencrl.sh.epp'), - } - - $_crlunits.each | String $_crlunit | { - file { "${_systemd_unitdir}/${_crlunit}": - ensure => file, - mode => '0644', - owner => 0, - group => 0, - content => epp("cfssl/${$_crlunit}.epp"), - } - ~> service { $_crlunit: - ensure => 'running', - enable => true, - require => [ - File["${cfssl::binpath}/cfssl-gencrl.sh"], - Service['cfssl'], - ], - subscribe => Service['cfssl'], - provider => 'systemd', - } - } - } else { - $_crlunits.each | String $_crlunit | { - service { $_crlunit: - ensure => 'stopped', - enable => false, - provider => 'systemd', - } - -> file { "${_systemd_unitdir}/${_crlunit}": - ensure => absent, - } - } - file { "${cfssl::binpath}/cfssl-gencrl.sh": - ensure => absent, - } - } } else { service { 'cfssl': ensure => 'stopped', @@ -276,12 +224,25 @@ class cfssl ( Archive["${binpath}/cfssl"], Postgresql::Server::Db[$dbname], Exec['goose pg up'], - File["${confdir}/${_serve_config_json}"], - File["${confdir}/${_db_config_json}"], + File["${confdir}/${cfssl::params::serve_config_json}"], + File["${confdir}/${cfssl::params::db_config_json}"], Class['cfssl::ca::root'], ], subscribe => Archive["${binpath}/cfssl"], provider => 'systemd', } } + + if $cfssl::crl_manage { + $_ca_list = [$rootca_manifest['cn'], $intermediatesca.keys, $serve_ca].flatten.unique + class { 'cfssl::ca::gencrls': + authorities => $_ca_list, + require => [ + Archive["${binpath}/cfssl"], + Postgresql::Server::Db[$dbname], + Exec['goose pg up'], + Class['cfssl::ca::root'], + ], + } + } } diff --git a/manifests/params.pp b/manifests/params.pp new file mode 100644 index 0000000000000000000000000000000000000000..bcfe87a1e1b647e42a3646ece699bfb43c9bdf93 --- /dev/null +++ b/manifests/params.pp @@ -0,0 +1,15 @@ +# @summary A short summary of the purpose of this class +# +# A description of what this class does +# +# @example +# include cfssl::params +class cfssl::params { + $binaries = ['cfssljson','cfssl-certinfo'] + $serve_config_json = 'serve-config.json' + $db_config_json = 'db-config.json' + $systemd_unitdir = '/etc/systemd/system' + $systemd_unit_file = "${systemd_unitdir}/cfssl.service" + $go_package = 'golang-1.16' + $go_targetlink = '/usr/lib/go-1.16/bin/go' +} diff --git a/spec/acceptance/cfssl_spec.rb b/spec/acceptance/cfssl_spec.rb index 2f057072043da97cf914b0d3dae73eefd211fcf0..b89aa7e0ed891a27955da6e42dbeba945c2ad9a4 100644 --- a/spec/acceptance/cfssl_spec.rb +++ b/spec/acceptance/cfssl_spec.rb @@ -62,7 +62,7 @@ describe 'cfssl' do its(:stdout) { is_expected.to match %r{CA:TRUE} } end - describe command('openssl crl -in /var/cfssl/crl.pem -text -noout') do + describe command('openssl crl -in /var/cfssl/crl-MYEXEMPLEROOTCA.pem -text -noout') do its(:stdout) { is_expected.to match %r{Certificate Revocation List } } its(:stdout) { is_expected.to match %r{Issuer: C = FR, L = MONTPELLIER, O = MYEXEMPLE ORG, CN = MYEXEMPLE ROOT CA} } its(:stdout) { is_expected.to match %r{No Revoked Certificates} } @@ -118,7 +118,12 @@ describe 'cfssl' do its(:stdout) { is_expected.to match %r{CA:TRUE, pathlen:1} } end - describe command('openssl crl -in /var/cfssl/crl.pem -text -noout') do + describe command('openssl crl -in /var/cfssl/crl-MYEXEMPLEROOTCA.pem -text -noout') do + its(:stdout) { is_expected.to match %r{Certificate Revocation List } } + its(:stdout) { is_expected.to match %r{Issuer: C = FR, L = MONTPELLIER, O = MYEXEMPLE ORG, CN = MYEXEMPLE ROOT CA} } + its(:stdout) { is_expected.to match %r{No Revoked Certificates} } + end + describe command('openssl crl -in /var/cfssl/crl-MYEXEMPLEINTERMDIATECA.pem -text -noout') do its(:stdout) { is_expected.to match %r{Certificate Revocation List } } its(:stdout) { is_expected.to match %r{Issuer: C = FR, L = MONTPELLIER, O = MYEXEMPLE ORG, CN = MYEXEMPLE INTERMDIATE CA} } its(:stdout) { is_expected.to match %r{No Revoked Certificates} } diff --git a/spec/classes/ca/gencrls_spec.rb b/spec/classes/ca/gencrls_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..5be088292464c7fc2794d620daca675b274f6660 --- /dev/null +++ b/spec/classes/ca/gencrls_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'cfssl::ca::gencrls' do + on_supported_os.each do |os, os_facts| + context "on #{os}" do + let(:facts) { os_facts } + + it { is_expected.to compile } + end + end +end diff --git a/spec/classes/params_spec.rb b/spec/classes/params_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..2381fb9860d7fcf975e761e2bdb14d3118e0e9fb --- /dev/null +++ b/spec/classes/params_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'cfssl::params' do + on_supported_os.each do |os, os_facts| + context "on #{os}" do + let(:facts) { os_facts } + + it { is_expected.to compile } + end + end +end diff --git a/templates/cfssl-gencrl.service.epp b/templates/cfssl-gencrl.service.epp index 232b1a0923017e826b65016c2377c8e017b1eea6..44cb09dc1e6372cfeaa8bcc6b3a3e52f6d8f2a02 100644 --- a/templates/cfssl-gencrl.service.epp +++ b/templates/cfssl-gencrl.service.epp @@ -1,9 +1,13 @@ +<%- | String $ca +| -%> [Unit] -Description=CloudFlare's PKI CRL generator +Description=CFSSL CRL generator for CA <%= $ca %> Requires=network-online.target After=cfssl.service [Service] Type=oneshot RemainAfterExit=yes -ExecStart=<%= $cfssl::binpath %>/cfssl-gencrl.sh +Environment=CRL=<%= $cfssl::crldir %>/crl-<%= $ca %>.pem +ExecStart=sh -c 'echo "-----BEGIN X509 CRL-----" > $CRL ; cfssl crl -db-config <%= $cfssl::confdir %>/<%= $cfssl::params::db_config_json %> -ca <%= $cfssl::confdir %>/ca/<%= $ca %>.pem -ca-key <%= $cfssl::confdir %>/ca/<%= $ca %>-key.pem | fold -w 64 >> $CRL ; echo "-----END X509 CRL-----" >> $CRL' + diff --git a/templates/cfssl-gencrl.sh.epp b/templates/cfssl-gencrl.sh.epp deleted file mode 100644 index 980a73ac6575b6e5d7e1f2676b62724486423c0f..0000000000000000000000000000000000000000 --- a/templates/cfssl-gencrl.sh.epp +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -TMPFILE="/tmp/crl${$}.pem" - -echo "-----BEGIN X509 CRL-----" > $TMPFILE -curl -s -d '{}' -H "Content-Type: application/json" -X GET <%= $cfssl::binding_ip %>:<%= $cfssl::port %>/api/v1/cfssl/crl?expiry=<%= $cfssl::crl_expiry %>s | jq -r '.result' | fold -w 64 >> $TMPFILE -echo "-----END X509 CRL-----" >> $TMPFILE - -if [ -s $TMPFILE ] -then - if openssl crl -in $TMPFILE -text -noout | grep -qP '^Certificate Revocation List ' - then - # the temp file looks as expected, we move it to crldir - cp $TMPFILE <%= $cfssl::crldir %>/crl.pem - else - echo "gencrl : missing header 'Certificate Revocation List'" - exit 1 - fi -else - echo "gencrl : empty file" - exit 1 -fi - - diff --git a/templates/cfssl-gencrl.timer.epp b/templates/cfssl-gencrl.timer.epp index 61c89707b07f80600c38e7aa76a30d492a114165..70d741528dd77c2a6bf483c0c377ff6d3d77223b 100644 --- a/templates/cfssl-gencrl.timer.epp +++ b/templates/cfssl-gencrl.timer.epp @@ -1,5 +1,7 @@ +<%- | String $ca +| -%> [Unit] -Description=CloudFlare's PKI CRL generator +Description=CFSSL CRL generator for CA <%= $ca %> [Timer] OnCalendar=*-*-* <%= $cfssl::crl_gentimer %> diff --git a/templates/cfssl.service.epp b/templates/cfssl.service.epp index f49b637c5606733e3560f81172b29a55deee870d..17924be2f7d22318ac872c7cd02e018274f71b58 100644 --- a/templates/cfssl.service.epp +++ b/templates/cfssl.service.epp @@ -6,7 +6,7 @@ After=network-online.target [Service] User=<%= $cfssl::sysuser %> Group=<%= $cfssl::sysgroup %> -ExecStart=<%= $cfssl::binpath %>/cfssl -log_dir <%= $cfssl::logdir %> serve -address <%= $cfssl::binding_ip %> -ca <%= $cfssl::confdir %>/ca/<%= regsubst($cfssl::serve_ca, '\s', '', 'G') %>.pem -ca-key <%= $cfssl::confdir %>/ca/<%= regsubst($cfssl::serve_ca, '\s', '', 'G') %>-key.pem -port <%= $cfssl::port %> -db-config <%= $cfssl::confdir %>/db-config.json -config <%= $cfssl::confdir %>/serve-config.json -loglevel <%= $cfssl::log_level %> +ExecStart=<%= $cfssl::binpath %>/cfssl -log_dir <%= $cfssl::logdir %> serve -address <%= $cfssl::binding_ip %> -ca <%= $cfssl::confdir %>/ca/<%= regsubst($cfssl::serve_ca, '\s', '', 'G') %>.pem -ca-key <%= $cfssl::confdir %>/ca/<%= regsubst($cfssl::serve_ca, '\s', '', 'G') %>-key.pem -port <%= $cfssl::port %> -db-config <%= $cfssl::confdir %>/<%= $cfssl::params::db_config_json %> -config <%= $cfssl::confdir %>/<%= $cfssl::params::serve_config_json %> -loglevel <%= $cfssl::log_level %> Restart=always PrivateTmp=yes ProtectSystem=full