]> git.proxmox.com Git - proxmox-acme.git/blobdiff - src/PVE/ACME/DNSChallenge.pm
dns: cope with plugin json index not being available
[proxmox-acme.git] / src / PVE / ACME / DNSChallenge.pm
index 8e61c254e8368c75a67a85142606187b0c49541e..7214d8889ecadee1c8b9b1a0c58e2ed127d5e679 100644 (file)
@@ -4,8 +4,12 @@ use strict;
 use warnings;
 
 use Digest::SHA qw(sha256);
+use JSON;
+
 use PVE::Tools;
 
+use PVE::ACME;
+
 use base qw(PVE::ACME::Challenge);
 
 my $ACME_PATH = '/usr/share/proxmox-acme/proxmox-acme';
@@ -18,119 +22,41 @@ sub type {
     return 'dns';
 }
 
-my $api_name_list = [
-    'acmedns',
-    'acmeproxy',
-    'active24',
-    'ad',
-    'ali',
-    'autodns',
-    'aws',
-    'azure',
-    'cf',
-    'clouddns',
-    'cloudns',
-    'cn',
-    'conoha',
-    'constellix',
-    'cx',
-    'cyon',
-    'da',
-    'ddnss',
-    'desec',
-    'dgon',
-    'dnsimple',
-    'do',
-    'doapi',
-    'domeneshop',
-    'dp',
-    'dpi',
-    'dreamhost',
-    'duckdns',
-    'durabledns',
-    'dyn',
-    'dynu',
-    'dynv6',
-    'easydns',
-    'euserv',
-    'exoscale',
-    'freedns',
-    'gandi_livedns',
-    'gcloud',
-    'gd',
-    'gdnsdk',
-    'he',
-    'hexonet',
-    'hostingde',
-    'infoblox',
-    'internetbs',
-    'inwx',
-    'ispconfig',
-    'jd',
-    'kas',
-    'kinghost',
-    'knot',
-    'leaseweb',
-    'lexicon',
-    'linode',
-    'linode_v4',
-    'loopia',
-    'lua',
-    'maradns',
-    'me',
-    'miab',
-    'misaka',
-    'myapi',
-    'mydevil',
-    'mydnsjp',
-    'namecheap',
-    'namecom',
-    'namesilo',
-    'nederhost',
-    'neodigit',
-    'netcup',
-    'nic',
-    'nsd',
-    'nsone',
-    'nsupdate',
-    'nw',
-    'one',
-    'online',
-    'openprovider',
-    'opnsense',
-    'ovh',
-    'pdns',
-    'pleskxml',
-    'pointhq',
-    'rackspace',
-    'rcode0',
-    'regru',
-    'schlundtech',
-    'selectel',
-    'servercow',
-    'tele3',
-    'ultra',
-    'unoeuro',
-    'variomedia',
-    'vscale',
-    'vultr',
-    'yandex',
-    'zilore',
-    'zone',
-    'zonomi',
-];
+my $DNS_API_CHALLENGE_SCHEMA_FN = '/usr/share/proxmox-acme/dns-challenge-schema.json';
+
+my $plugin_cache;
+sub get_supported_plugins {
+    if (!$plugin_cache) {
+       $plugin_cache = -e $DNS_API_CHALLENGE_SCHEMA_FN # we allow this to be optional as not all users require
+           ? from_json(PVE::Tools::file_get_contents($DNS_API_CHALLENGE_SCHEMA_FN))
+           : {};
+    }
+    return $plugin_cache;
+}
 
 sub properties {
+    my $plugins = get_supported_plugins();
     return {
        api => {
            description => "API plugin name",
            type => 'string',
-           enum => $api_name_list,
+           enum => [sort keys %$plugins],
        },
        data => {
            type => 'string',
-           description => 'DNS plugin data.',
+           description => 'DNS plugin data. (base64 encoded)',
        },
+       'validation-delay' => {
+           type => 'integer',
+           description => 'Extra delay in seconds to wait before requesting validation.'
+               .' Allows to cope with a long TTL of DNS records.',
+           # low default, but our bet is that the acme-challenge domain isn't
+           # cached at all, so it hopefully shouldn't run into TTL issues
+           default => 30,
+           optional => 1,
+           minimum => 0,
+           maximum => 2 * 24 * 60 * 60,
+       }
     };
 }
 
@@ -140,6 +66,7 @@ sub options {
        data => { optional => 1 },
        nodes => { optional => 1 },
        disable => { optional => 1 },
+       'validation-delay' => { optional => 1 },
     };
 }
 
@@ -160,7 +87,7 @@ my $proxmox_acme_command = sub {
 
     # for security reasons, we execute the command as nobody
     # we can't verify that the code of the DNSPlugins are harmless.
-    my $cmd = ["setpriv", "--reuid", "nobody", "--regid", "nogroup", "--clear-groups", "--"];
+    my $cmd = ["setpriv", "--reuid", "nobody", "--regid", "nogroup", "--clear-groups", "--reset-env", "--"];
 
     # The order of the parameters passed to proxmox-acme is important
     # proxmox-acme <setup|teardown> $plugin <$domain|$alias> $txtvalue [$plugin_conf_string]
@@ -170,9 +97,10 @@ my $proxmox_acme_command = sub {
     } else {
        push @$cmd, $domain;
     }
-    push @$cmd, $txtvalue, $plugin_conf_string;
+    my $input = "$txtvalue\n";
+    $input .= "$plugin_conf_string\n" if $plugin_conf_string;
 
-    PVE::Tools::run_command($cmd);
+    PVE::Tools::run_command($cmd, input => $input);
 
     $data->{url} = $challenge->{url};
 
@@ -184,6 +112,12 @@ sub setup {
 
     my $domain = $proxmox_acme_command->($self, $acme, $auth, $data, 'setup');
     print "Add TXT record: _acme-challenge.$domain\n";
+
+    my $delay = $data->{plugin}->{'validation-delay'} // 30;
+    if ($delay > 0) {
+       print "Sleeping $delay seconds to wait for TXT record propagation\n";
+       sleep($delay); # don't care for EINTR
+    }
 }
 
 sub teardown {