proxmox-perl-rs/pmg-rs/test.pl
Wolfgang Bumiller b6274a49a5 import pmg-rs
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2021-11-28 17:11:57 +01:00

173 lines
5.9 KiB
Perl

#!/usr/bin/env perl
use v5.28.0;
use Data::Dumper;
use lib '.';
use PMG::RS::Acme;
use PMG::RS::CSR;
# "Config:" The Acme server URL:
my $DIR = 'https://acme-staging-v02.api.letsencrypt.org/directory';
# Useage:
#
# * Create a new account:
# | ~/ $ ./test.pl ./account.json new 'somebody@example.invalid"
#
# The `./account.json` will be created using an EC P-256 key.
# Optionally an RSA key size can be passed as additional parameter to generate
# an account with an RSA key instead.
#
# From here on out the `./account.json` file must already exist:
#
# * Place a new order:
# | ~/ $ ./test.pl ./account.json new-order my.domain.com
# | $VAR1 = {
# | ... order data ...
# | 'authorizations' => [
# | 'https://acme.example/auths/1244',
# | ... possibly more ...
# | ]
# | }
# | Order URL: https://acme.example/order/1793
#
# Note: This ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
# URL will be used later for finalization and certifiate download.
# The `$VAR1` dump contains the order JSON data.
# The 'authorizations' URLs are going to be used next.
#
# * Get authorization info
# | ~/ $ ./test.pl ./account.json get-auth 'https://acme.example/auths/1244'
# | $VAR1 = {
# | ... auth data ...
# | 'challenges' => [
# | {
# | 'type' => 'dns-01',
# | 'url' => 'https://acme.example/challenge/8188/dns1'
# | }
# | ... likely more ...
# | ]
# | }
# | Key Authorization = SuperVeryMegaLongValue
# | dns-01 TXT value = ShorterValue
#
# Now perform the things you need to for the challenge, eg. setup the DNS
# entry using the provided TXT value.
# Then use the correct challenge's URL with req-auth
#
# * Request challenge validation
# | ~/ $ ./test.pl ./account.json \
# | req-challenge 'https://acme.example/challenge/8188/dns1
#
# * Repeat the above 2 steps for all authorizations.
# * Wait for the order to be valid via `get-order`
# | ~/ $ ./test.pl ./account.json get-order 'https://acme.example/order/1793'
# | $VAR1 = {
# | 'status' => 'valid',
# | 'finalize' => 'some URL',
# | ... order data ...
# | }
# | Order URL: https://acme.example/order/1793
#
# * Finalize the order via the *Order URL* and a private key to sign the
# request with (eg. generated via `openssl genrsa` or `openssl ecparam`).
# | ~/ $ ./test.pl ./account.json \
# | finalize my.domain.com ./my-private-key.pem \
# | 'https://acme.example/order/1793'
#
# * Wait for a 'certificate' property to pop up in the order
# (check via 'get-order')
#
# * Grab the certificate with the Order URL and a destination file name:
# | ~/ $ ./test.pl ./account.json get-cert \
# | 'https://acme.example/order/1793' \
# | ./my-cert.pem
my $account = shift // die "missing account file\n";
my $cmd = shift // die "missing account file\n";
sub load : prototype($) {
my ($file) = @_;
open(my $fh, '<', $file) or die "open($file): $!\n";
my $data = do {
local $/ = undef;
<$fh>
};
close($fh);
return $data;
}
sub store : prototype($$) {
my ($file, $data) = @_;
open(my $fh, '>', $file) or die "open($file): $!\n";
syswrite($fh, $data) == length($data)
or die "failed to write data to $file: $!\n";
close($fh);
}
if ($cmd eq 'new') {
my $mail = shift // die "missing mail address\n";
my $rsa_bits = shift;
if (defined($rsa_bits)) {
$rsa_bits = int($rsa_bits);
}
my $acme = PMG::RS::Acme->new($DIR);
$acme->new_account($account, 1, ["mailto:$mail"], undef);
} elsif ($cmd eq 'get-meta') {
#my $acme = PMG::RS::Acme->new($DIR);
my $acme = PMG::RS::Acme->new('https%3A%2F%2Facme-v02.api.letsencrypt.org%2Fdirectory');
my $data = $acme->get_meta();
say Dumper($data);
} elsif ($cmd eq 'new-order') {
my $domain = shift // die "missing domain\n";
my $acme = PMG::RS::Acme->load($account);
my ($url, $order) = $acme->new_order([$domain]);
say Dumper($order);
say "Order URL: $url\n";
} elsif ($cmd eq 'get-auth') {
my $url = shift // die "missing url\n";
my $acme = PMG::RS::Acme->load($account);
my $auth = $acme->get_authorization($url);
say Dumper($auth);
for my $challenge ($auth->{challenges}->@*) {
next if $challenge->{type} ne 'dns-01';
say "Key Authorization = ".$acme->key_authorization($challenge->{token});
say "dns-01 TXT value = ".$acme->dns_01_txt_value($challenge->{token});
}
} elsif ($cmd eq 'req-challenge') {
my $url = shift // die "missing url\n";
my $acme = PMG::RS::Acme->load($account);
my $challenge = $acme->request_challenge_validation($url);
say Dumper($challenge);
} elsif ($cmd eq 'finalize') {
my $domain = shift // die 'missing domain\n';
my $pkfile = shift // die "missing private key file\n";
my $order_url = shift // die "missing order URL\n";
my ($csr_der, $pkey_pem) = PMG::RS::CSR::generate_csr([$domain], {});
store($pkfile, $pkey_pem);
my $acme = PMG::RS::Acme->load($account);
my $order = $acme->get_order($order_url);
say Dumper($order);
die "order not ready\n" if $order->{status} ne 'ready';
$acme->finalize_order($order->{finalize}, $csr_der);
} elsif ($cmd eq 'get-order') {
my $order_url = shift // die "missing order URL\n";
my $acme = PMG::RS::Acme->load($account);
my $order = $acme->get_order($order_url);
say Dumper($order);
} elsif ($cmd eq 'get-cert') {
my $order_url = shift // die "missing order URL\n";
my $file_name = shift // die "missing destination file name\n";
my $acme = PMG::RS::Acme->load($account);
my $order = $acme->get_order($order_url);
my $cert_url = $order->{certificate};
die "certificate not ready\n" if !$cert_url;
say Dumper($order);
my $cert = $acme->get_certificate($cert_url);
store($file_name, $cert);
} else {
die "unknown command '$cmd'\n";
}