[pve-devel] [PATCH v2 proxmox-acme] support downloading alternate chains

Stoiko Ivanov s.ivanov at proxmox.com
Fri Oct 8 10:52:40 CEST 2021


Tested again against LE production endpoint - LGTM :)
Thanks!

Reviewed-By: Stoiko Ivanov <s.ivanov at proxmox.com>
Tested-By: Stoiko Ivanov <s.ivanov at proxmox.com>

On Fri,  8 Oct 2021 10:18:21 +0200
Fabian Grünbichler <f.gruenbichler at proxmox.com> wrote:

> the current default chains end with an expired root certificate for
> maximum compatibility with old Android versions. this breaks some other
> older clients (openssl, gnutls) which don't expect chains to contain any
> expired certificates, even if they are 'above' the trust anchor.
> 
> by setting $root, it is possible to specify which root the ACME provided
> certificate chain should end with, downloading alternate chains as
> necessary.
> 
> Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
> ---
> 
> Notes:
>     v2: 
>     - only check issuer
>     - also check default chain
>     - add 'i' to RE check
> 
>     only tested with pebble
> 
>  src/PVE/ACME.pm | 35 ++++++++++++++++++++++++++++++++++-
>  1 file changed, 34 insertions(+), 1 deletion(-)
> 
> diff --git a/src/PVE/ACME.pm b/src/PVE/ACME.pm
> index 265482d..57578d7 100644
> --- a/src/PVE/ACME.pm
> +++ b/src/PVE/ACME.pm
> @@ -442,17 +442,50 @@ sub deactivate_authorization {
>  
>  # Get certificate
>  # GET-as-POST to order's certificate URL
> +# if $root is specified, attempts to find a matching (alternate) chain
>  # Expects a '200 OK' reply
>  # returns certificate chain in PEM format
>  sub get_certificate {
> -    my ($self, $order) = @_;
> +    my ($self, $order, $root) = @_;
>  
>      $self->fatal("no certificate URL available (yet?)", $order)
>         if !$order->{certificate};
>  
> +    my $check_root = sub {
> +	my ($chain) = @_;
> +
> +	my @certs = PVE::Certificate::split_pem($chain);
> +	my $root_pem = $certs[-1];
> +
> +	my ($file, $fh) = PVE::Tools::tempfile_contents($root_pem);
> +	my $info = PVE::Certificate::get_certificate_info($file);
> +
> +	return defined($info->{issuer}) && $info->{issuer} =~ m/\Q$root\E/i;
> +    };
> +
>      my $r = $self->do(POST => $order->{certificate}, '');
>      my $return = eval {
> +	# default chain
>  	my $res = __get_result($r, 200, 1);
> +	if ($root && !$check_root->($res)) {
> +	    # alternate chains if requested and default didn't match
> +	    $res = undef;
> +	    my @links = $r->header('link');
> +	    for my $link (@links) {
> +		if ($link =~ /^<(.*)>;rel="alternate"$/) {
> +		    my $url = $1;
> +		    my $chain = eval { __get_result($self->do(POST => $url, ''), 200, 1); };
> +		    die "failed to retrieve alternate chain from '$url' - $@\n" if $@;
> +		    if ($check_root->($chain)) {
> +			$res = $chain;
> +			last;
> +		    }
> +		}
> +	    }
> +	    die "no matching alternate chain for '$root' returned by server\n"
> +		if !defined($res);
> +	}
> +
>  	if ($res =~ /^(-----BEGIN CERTIFICATE-----)(.+)(-----END CERTIFICATE-----)$/s) { # untaint
>  	    return $1 . $2 . $3;
>  	}





More information about the pve-devel mailing list