Skip to content

OpenVPN CRL expires and cannot be regenerated automatically #226

@akuzminsky

Description

@akuzminsky

Problem

OpenVPN clients cannot connect when the Certificate Revocation List (CRL) expires:

VERIFY ERROR: depth=0, error=CRL has expired
TLS Error: TLS handshake failed

The CRL has a 180-day expiration (EASYRSA_CRL_DAYS 180 in vars.erb), but Puppet only generates it once during initial setup and never regenerates it.

Root Cause

Issue 1: CRL is only generated once

In modules/profile/manifests/openvpn_server/config.pp (lines 147-158):

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,  # <-- Only runs if file doesn't exist
  require => [...]
}

The creates parameter means this exec only runs once when the file doesn't exist. After the initial CRL is created, Puppet never regenerates it, even after it expires 180 days later.

Issue 2: Missing CA passphrase configuration

The vars.erb template is missing the EASYRSA_PASSIN variable needed for automated CRL regeneration:

Current vars.erb (lines 166-167):

set_var EASYRSA_BATCH true
set_var EASYRSA_NO_PASS 1

Missing:

set_var EASYRSA_PASSIN "file:<%= @openvp_config_directory %>/ca_passphrase"

Without this, any attempt to regenerate the CRL manually prompts for the CA passphrase:

root@ip-10-1-3-120:/etc/openvpn# /usr/share/easy-rsa/easyrsa gen-crl
Enter pass phrase for /etc/openvpn/pki/private/ca.key:

Impact

  • Severity: High - All OpenVPN clients lose connectivity when CRL expires
  • Frequency: Every 180 days (~6 months)
  • Workaround: Manual CRL regeneration by ops team

Observed Behavior

root@ip-10-1-3-120:/etc/openvpn# openssl crl -in /etc/openvpn/pki/crl.pem -noout -text
Certificate Revocation List (CRL):
    Last Update: Jun 27 21:05:32 2025 GMT
    Next Update: Dec 24 21:05:32 2025 GMT  # EXPIRED

Current date: January 2, 2026 - CRL expired 9 days ago.

Proposed Solution

1. Add EASYRSA_PASSIN to vars.erb template

set_var EASYRSA_BATCH true
set_var EASYRSA_NO_PASS 1
set_var EASYRSA_PASSIN "file:<%= @openvp_config_directory %>/ca_passphrase"

2. Add automated CRL regeneration via cron

In modules/profile/manifests/openvpn_server/config.pp, add:

cron { 'regenerate_openvpn_crl':
  command  => "cd ${openvp_config_directory} && /usr/share/easy-rsa/easyrsa --vars=${openvp_config_directory}/vars gen-crl && systemctl reload openvpn-server@server",
  user     => 'root',
  hour     => 3,
  minute   => 0,
  monthday => 1,  # Run on the 1st of each month
  require  => Exec['generate_gen_crl'],
}

Alternatively, replace the creates parameter with a check that validates CRL expiration and regenerates when needed.

Temporary Workaround

Manual CRL regeneration (requires SSH access to OpenVPN server):

cd /etc/openvpn
cat /etc/openvpn/ca_passphrase | /usr/share/easy-rsa/easyrsa --vars=/etc/openvpn/vars gen-crl
systemctl restart openvpn-server@server

Questions

  1. How did the CRL get generated initially without the passphrase? Was the CA created manually outside of Puppet?
  2. Should we also increase EASYRSA_CRL_DAYS to reduce regeneration frequency?

Environment

  • Puppet module: profile::openvpn_server
  • Affected files:
    • modules/profile/manifests/openvpn_server/config.pp
    • modules/profile/templates/openvpn_server/vars.erb
  • OpenVPN server: Ubuntu Noble (24.04)
  • Easy-RSA version: 3.x

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions