[PATCH manager] cli: acme: Allow to get certificates from CAs requiring EAB
Ember 'n0emis' Keske
mailinglists at noemis.me
Tue Apr 12 21:13:23 CEST 2022
From: Ember 'n0emis' Keske <git at n0emis.eu>
to allow to fetch certificates from CA's like sectigo or zerossl.
The CLI checks, if the CA requires EAB credentials and promts
the user to enter them on demand.
Signed-off-by: Ember 'n0emis' Keske <git at n0emis.eu>
---
PVE/API2/ACMEAccount.pm | 74 ++++++++++++++++++++++++++++++++++++++++-
PVE/CLI/pvenode.pm | 12 +++++++
2 files changed, 85 insertions(+), 1 deletion(-)
diff --git a/PVE/API2/ACMEAccount.pm b/PVE/API2/ACMEAccount.pm
index b790843a..ca285da5 100644
--- a/PVE/API2/ACMEAccount.pm
+++ b/PVE/API2/ACMEAccount.pm
@@ -2,6 +2,7 @@ package PVE::API2::ACMEAccount;
use strict;
use warnings;
+use Digest::SHA;
use PVE::ACME;
use PVE::CertHelpers;
@@ -35,6 +36,31 @@ my $account_contact_from_param = sub {
my @addresses = PVE::Tools::split_list(extract_param($_[0], 'contact'));
return [ map { "mailto:$_" } @addresses ];
};
+my $generate_eab = sub {
+ my ($acme, $param) = @_;
+ my $payload = PVE::ACME::encode(PVE::ACME::tojs($acme->jwk()));
+
+ my $kid = extract_param($param, 'eab_kid');
+ my $key = extract_param($param, 'eab_hmac_key');
+
+ my $url = $acme->_method('newAccount');
+
+ my $protected = {
+ alg => 'HS256',
+ kid => $kid,
+ url => $url,
+ };
+
+ $protected = PVE::ACME::encode(PVE::ACME::tojs($protected, canonical=>1));
+ my $signdata = "$protected.$payload";
+ my $signature = PVE::ACME::encode(Digest::SHA::hmac_sha256($signdata, MIME::Base64::decode_base64url($key)));
+
+ return {
+ protected => $protected,
+ payload => $payload,
+ signature => $signature,
+ };
+};
my $acme_account_dir = PVE::CertHelpers::acme_account_dir();
__PACKAGE__->register_method ({
@@ -115,6 +141,16 @@ __PACKAGE__->register_method ({
default => $acme_default_directory_url,
optional => 1,
}),
+ eab_kid => {
+ type => 'string',
+ description => 'Key Identifier for External Account Binding.',
+ optional => 1,
+ },
+ eab_hmac_key => {
+ type => 'string',
+ description => 'HMAC key for External Account Binding.',
+ optional => 1,
+ },
},
},
returns => {
@@ -145,7 +181,12 @@ __PACKAGE__->register_method ({
print "Generating ACME account key..\n";
$acme->init(4096);
print "Registering ACME account..\n";
- eval { $acme->new_account($param->{tos_url}, contact => $contact); };
+ if ($param->{eab_kid}) {
+ my $eab = $generate_eab->($acme, $param);
+ eval { $acme->new_account($param->{tos_url}, contact => $contact, externalAccountBinding => $eab); };
+ } else {
+ eval { $acme->new_account($param->{tos_url}, contact => $contact); };
+ }
if (my $err = $@) {
unlink $account_file;
die "Registration failed: $err\n";
@@ -339,6 +380,37 @@ __PACKAGE__->register_method ({
return $meta ? $meta->{termsOfService} : undef;
}});
+__PACKAGE__->register_method ({
+ name => 'get_eab',
+ path => 'eab',
+ method => 'GET',
+ description => "Retrieve where an external Account is required from CA.",
+ permissions => { user => 'all' },
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ directory => get_standard_option('pve-acme-directory-url', {
+ default => $acme_default_directory_url,
+ optional => 1,
+ }),
+ },
+ },
+ returns => {
+ type => 'boolean',
+ default => 0,
+ description => 'returns 1 if an external Account is required',
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $directory = extract_param($param, 'directory') // $acme_default_directory_url;
+
+ my $acme = PVE::ACME->new(undef, $directory);
+ my $meta = $acme->get_meta();
+
+ return $meta ? $meta->{externalAccountRequired} : 0;
+ }});
+
__PACKAGE__->register_method ({
name => 'get_directories',
path => 'directories',
diff --git a/PVE/CLI/pvenode.pm b/PVE/CLI/pvenode.pm
index acef6c3b..658f5ee8 100644
--- a/PVE/CLI/pvenode.pm
+++ b/PVE/CLI/pvenode.pm
@@ -129,6 +129,18 @@ __PACKAGE__->register_method({
} else {
print "No Terms of Service found, proceeding.\n";
}
+ print "\nAttempting to check wether an external Account is required for '$param->{directory}'..\n";
+ my $eab = PVE::API2::ACMEAccount->get_eab({ directory => $param->{directory} });
+ if ($eab) {
+ print "External Account Binding information is required.\n";
+ my $term = Term::ReadLine->new('pvenode');
+ my $kid = $term->readline('Enter the Key Identifier for External Account Binding (KID): ');
+ my $hmac_key = $term->readline('Enter the HMAC key for External Account Binding: ');
+ $param->{eab_kid} = $kid;
+ $param->{eab_hmac_key} = $hmac_key;
+ } else {
+ print "No external Account required, proceeding.\n";
+ }
print "\nAttempting to register account with '$param->{directory}'..\n";
$upid_exit->(PVE::API2::ACMEAccount->register_account($param));
--
2.35.1
More information about the pve-devel
mailing list