Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
puppet-code (0.1.0-1build280) noble; urgency=medium

* commit event. see changes history in git log

-- root <packager@infrahouse.com> Wed, 21 Jan 2026 00:57:49 +0000

puppet-code (0.1.0-1build279) noble; urgency=medium

* commit event. see changes history in git log
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
String $openvpn_topology,
String $openvpn_network,
String $openvpn_netmask,
$mailto = lookup(
'profile::cron::mailto', undef, undef, "root@${facts['networking']['hostname']}.${facts['networking']['domain']}"
),
) {

$dns_name = $facts['efs']['dns_name']
Expand All @@ -29,15 +32,17 @@
mount_target => $openvp_config_directory,
}

$ca_passphrase = aws_get_secret(
$facts['openvpn']['ca_key_passphrase_secret'],
$facts['ec2_metadata']['placement']['region']
)

file { $openvpn_easyrsa_passin_file:
ensure => file,
owner => 'root',
group => 'root',
mode => '0400',
content => aws_get_secret(
$facts['openvpn']['ca_key_passphrase_secret'],
$facts['ec2_metadata']['placement']['region']
),
content => $ca_passphrase,
require => [
Mount[$openvp_config_directory],
]
Expand All @@ -52,6 +57,17 @@
],
}

file { "${openvp_config_directory}/README":
ensure => file,
content => template('profile/openvpn_server/README.erb'),
owner => 'root',
group => 'root',
mode => '0644',
require => [
Mount[$openvp_config_directory],
],
}

exec {'generate_dh2048_pem':
path => '/usr/bin',
command => 'openssl dhparam -out dh2048.pem 2048',
Expand Down Expand Up @@ -115,25 +131,39 @@
]
}

# Note: Passphrase is briefly visible in process environment during execution.
# This is required because Easy-RSA with OpenSSL 3.x doesn't support file: prefix.
# The passphrase file itself is secured at mode 0400 (root read-only).
# Alternative approaches (stdin redirection) are not supported by Easy-RSA batch mode.
exec { 'generate_ca':
command => "/usr/share/easy-rsa/easyrsa --vars=${openvp_config_directory}/vars build-ca",
cwd => $openvp_config_directory,
creates => "${openvp_config_directory}/pki/private/ca.key",
require => [
command => "/usr/share/easy-rsa/easyrsa --vars=${openvp_config_directory}/vars build-ca",
cwd => $openvp_config_directory,
environment => [
"EASYRSA_PASSIN=pass:${ca_passphrase}",
"EASYRSA_PASSOUT=pass:${ca_passphrase}",
"EASYRSA_REQ_CN=${openvpn_easyrsa_req_org} CA",
],
creates => "${openvp_config_directory}/pki/private/ca.key",
require => [
Mount[$openvp_config_directory],
Package[easy-rsa],
File["${openvp_config_directory}/vars"],
File[$openvpn_easyrsa_passin_file],
]
}

exec { 'generate_server_key':
command => "/usr/share/easy-rsa/easyrsa --vars=${openvp_config_directory}/vars build-server-full server nopass inline",
cwd => $openvp_config_directory,
creates => "${openvp_config_directory}/pki/private/server.key",
require => [
command => "/usr/share/easy-rsa/easyrsa --vars=${openvp_config_directory}/vars build-server-full server nopass",
cwd => $openvp_config_directory,
environment => [
"EASYRSA_PASSIN=pass:${ca_passphrase}",
],
creates => "${openvp_config_directory}/pki/private/server.key",
require => [
Mount[$openvp_config_directory],
Package[easy-rsa],
File["${openvp_config_directory}/vars"],
Exec[generate_ca],
]
}

Expand All @@ -145,16 +175,45 @@
}

exec { 'generate_gen_crl':
command => "/usr/share/easy-rsa/easyrsa --vars=${openvp_config_directory}/vars gen-crl",
cwd => $openvp_config_directory,
creates => $openvpn_crl_path,
require => [
command => "/usr/share/easy-rsa/easyrsa --vars=${openvp_config_directory}/vars gen-crl",
cwd => $openvp_config_directory,
environment => [
"EASYRSA_PASSIN=pass:${ca_passphrase}",
],
creates => $openvpn_crl_path,
require => [
Mount[$openvp_config_directory],
Package[easy-rsa],
File["${openvp_config_directory}/vars"],
File[$openvpn_easyrsa_passin_file],
Exec[generate_pki],
Exec[generate_ca],
]
}

# Certificate Revocation List (CRL) Management:
# - CRL expires every 180 days (EASYRSA_CRL_DAYS in vars.erb)
# - Automated regeneration: Monthly on the 1st at 3 AM
# - Manual regeneration: Run /etc/openvpn/regenerate-crl.sh as root
# - OpenVPN automatically re-reads CRL on each connection (no restart needed)
# - Monitor CRL age: Check /etc/openvpn/pki/crl.pem modification time
# - Logs to syslog with tag 'openvpn-crl' (auth facility)
$crl_regen_script = "${openvp_config_directory}/regenerate-crl.sh"

file { $crl_regen_script:
ensure => file,
owner => 'root',
group => 'root',
mode => '0700',
content => template('profile/openvpn_server/regenerate-crl.sh.erb'),
require => Exec['generate_gen_crl'],
}
cron { 'regenerate_openvpn_crl':
command => $crl_regen_script,
user => 'root',
hour => 3,
minute => 0,
monthday => 1,
environment => ["MAILTO=${mailto}"],
require => File[$crl_regen_script],
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
OpenVPN Server - Operational Guide
===================================
Managed by Puppet - do not edit manually

This file is deployed by profile::openvpn_server::config


Certificate Revocation List (CRL) Management
---------------------------------------------

CRL Configuration:
- Expiration: 180 days (EASYRSA_CRL_DAYS in vars)
- Auto-regeneration: Monthly on the 1st at 3 AM
- Location: <%= @openvp_config_directory %>/pki/crl.pem

Logging:
- Syslog tag: openvpn-crl
- Facility: auth
- View logs: journalctl -t openvpn-crl

Manual CRL Regeneration:
sudo <%= @openvp_config_directory %>/regenerate-crl.sh

Check CRL Age:
stat <%= @openvp_config_directory %>/pki/crl.pem | grep Modify

Check CRL Expiration:
openssl crl -in <%= @openvp_config_directory %>/pki/crl.pem -noout -nextupdate


Troubleshooting
---------------

CRL regeneration failed:
1. Check logs: journalctl -t openvpn-crl -n 50
2. Verify CA passphrase file exists: ls -la <%= @openvpn_easyrsa_passin_file %>
3. Test passphrase manually:
cd <%= @openvp_config_directory %>
EASYRSA_PASSIN="pass:$(cat <%= @openvpn_easyrsa_passin_file %>)" \
/usr/share/easy-rsa/easyrsa --vars=<%= @openvp_config_directory %>/vars gen-crl
4. Check AWS Secrets Manager access if passphrase file is empty/missing

OpenVPN not accepting connections:
1. Check service: systemctl status openvpn@server
2. Check CRL validity: openssl crl -in <%= @openvp_config_directory %>/pki/crl.pem -noout -nextupdate
3. Check logs: journalctl -u openvpn@server -n 100

Client certificate revoked unexpectedly:
- CRL is re-read on each connection, no restart needed after regeneration
- Check index.txt for certificate status:
cat <%= @openvp_config_directory %>/pki/index.txt


Key Files
---------

<%= @openvp_config_directory %>/
pki/
ca.crt - CA certificate (public)
crl.pem - Certificate Revocation List
private/
ca.key - CA private key (encrypted)
server.key - Server private key
issued/
server.crt - Server certificate
index.txt - Certificate database
vars - Easy-RSA configuration
server.conf - OpenVPN server configuration
regenerate-crl.sh - CRL regeneration script
ca_passphrase - CA key passphrase (mode 0400)


Related Documentation
---------------------

Easy-RSA: https://github.com/OpenVPN/easy-rsa
OpenSSL 3.x issues: https://github.com/OpenVPN/easy-rsa/issues/692
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Regenerate OpenVPN CRL (Certificate Revocation List)
# Managed by Puppet - do not edit manually
#
# Silent on success, outputs errors only (suitable for cron)

set -e

cd <%= @openvp_config_directory %>

# Read passphrase from file and use pass: prefix
# Note: file: prefix causes "Error reading password from BIO" with OpenSSL 3.x in batch mode
# See: https://github.com/OpenVPN/easy-rsa/issues/692
CA_PASSPHRASE=$(cat <%= @openvpn_easyrsa_passin_file %>)
export EASYRSA_PASSIN="pass:${CA_PASSPHRASE}"

OUTPUT=$(/usr/share/easy-rsa/easyrsa --vars=<%= @openvp_config_directory %>/vars gen-crl 2>&1) || {
echo "ERROR: Failed to regenerate CRL" >&2
echo "$OUTPUT" >&2
logger -t openvpn-crl -p auth.err "Failed to regenerate CRL"
exit 1
}

# Log success to syslog for audit trail
logger -t openvpn-crl -p auth.info "Successfully regenerated CRL"

# OpenVPN re-reads the CRL file on each new connection attempt,
# so no service restart is needed.
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ set_var EASYRSA_NS_COMMENT "Easy-RSA Generated Certificate"
set_var EASYRSA_TEMP_FILE "$EASYRSA_TEMP_DIR/extensions.temp"

set_var EASYRSA_BATCH true
set_var EASYRSA_NO_PASS 1
# !!
# NOTE: ADVANCED OPTIONS BELOW THIS POINT
# PLAY WITH THEM AT YOUR OWN RISK
Expand Down