]> git.proxmox.com Git - proxmox-acme.git/blame - src/PVE/ACME/DNSChallenge.pm
DNS Challenge: add validation-delay plugin option
[proxmox-acme.git] / src / PVE / ACME / DNSChallenge.pm
CommitLineData
98b96d9e
WL
1package PVE::ACME::DNSChallenge;
2
3use strict;
4use warnings;
5
6use Digest::SHA qw(sha256);
7use PVE::Tools;
8
9use base qw(PVE::ACME::Challenge);
10
11my $ACME_PATH = '/usr/share/proxmox-acme/proxmox-acme';
12
13sub supported_challenge_types {
122626b3 14 return ["dns-01"];
98b96d9e
WL
15}
16
17sub type {
18 return 'dns';
19}
20
d93b0e87 21my $plugin_names = [
98b96d9e
WL
22 'acmedns',
23 'acmeproxy',
24 'active24',
25 'ad',
26 'ali',
27 'autodns',
28 'aws',
29 'azure',
30 'cf',
31 'clouddns',
32 'cloudns',
33 'cn',
34 'conoha',
35 'constellix',
36 'cx',
37 'cyon',
38 'da',
39 'ddnss',
40 'desec',
41 'dgon',
42 'dnsimple',
43 'do',
44 'doapi',
45 'domeneshop',
46 'dp',
47 'dpi',
48 'dreamhost',
49 'duckdns',
50 'durabledns',
51 'dyn',
52 'dynu',
53 'dynv6',
54 'easydns',
55 'euserv',
56 'exoscale',
57 'freedns',
58 'gandi_livedns',
59 'gcloud',
60 'gd',
61 'gdnsdk',
62 'he',
63 'hexonet',
64 'hostingde',
65 'infoblox',
66 'internetbs',
67 'inwx',
68 'ispconfig',
69 'jd',
70 'kas',
71 'kinghost',
72 'knot',
73 'leaseweb',
74 'lexicon',
75 'linode',
76 'linode_v4',
77 'loopia',
78 'lua',
79 'maradns',
80 'me',
81 'miab',
82 'misaka',
83 'myapi',
84 'mydevil',
85 'mydnsjp',
86 'namecheap',
87 'namecom',
88 'namesilo',
89 'nederhost',
90 'neodigit',
91 'netcup',
92 'nic',
93 'nsd',
94 'nsone',
95 'nsupdate',
96 'nw',
97 'one',
98 'online',
99 'openprovider',
100 'opnsense',
101 'ovh',
102 'pdns',
103 'pleskxml',
104 'pointhq',
105 'rackspace',
106 'rcode0',
107 'regru',
108 'schlundtech',
109 'selectel',
110 'servercow',
111 'tele3',
112 'ultra',
113 'unoeuro',
114 'variomedia',
115 'vscale',
116 'vultr',
117 'yandex',
118 'zilore',
119 'zone',
120 'zonomi',
121];
e3924936
TL
122sub get_supported_plugins {
123 return $plugin_names;
124}
98b96d9e
WL
125
126sub properties {
127 return {
128 api => {
129 description => "API plugin name",
130 type => 'string',
d93b0e87 131 enum => $plugin_names,
98b96d9e
WL
132 },
133 data => {
134 type => 'string',
135 description => 'DNS plugin data.',
136 },
4317ba99
TL
137 'validation-delay' => {
138 type => 'integer',
139 description => 'Extra delay in seconds to wait before requesting validation.'
140 .' Allows to cope with a long TTL of DNS records.',
141 # low default, but our bet is that the acme-challenge domain isn't
142 # cached at all, so it hopefully shouldn't run into TTL issues
143 default => 30,
144 optional => 1,
145 minimum => 0,
146 maximum => 2 * 24 * 60 * 60,
147 }
98b96d9e
WL
148 };
149}
150
151sub options {
152 return {
153 api => {},
13b63882 154 data => { optional => 1 },
98b96d9e
WL
155 nodes => { optional => 1 },
156 disable => { optional => 1 },
4317ba99 157 'validation-delay' => { optional => 1 },
98b96d9e
WL
158 };
159}
160
f00829fd
FG
161my $proxmox_acme_command = sub {
162 my ($self, $acme, $auth, $data, $action) = @_;
98b96d9e
WL
163
164 die "No plugin data for DNSChallenge\n" if !defined($data->{plugin});
f00829fd
FG
165
166 my $alias = $data->{alias};
167 my $domain = $auth->{identifier}->{value};
168
169 my $challenge = $self->extract_challenge($auth->{challenges});
170 my $key_auth = $acme->key_authorization($challenge->{token});
171
172 my $txtvalue = PVE::ACME::encode(sha256($key_auth));
98b96d9e
WL
173 my $dnsplugin = $data->{plugin}->{api};
174 my $plugin_conf_string = $data->{plugin}->{data};
175
176 # for security reasons, we execute the command as nobody
177 # we can't verify that the code of the DNSPlugins are harmless.
f0ed0733 178 my $cmd = ["setpriv", "--reuid", "nobody", "--regid", "nogroup", "--clear-groups", "--reset-env", "--"];
98b96d9e 179
f00829fd
FG
180 # The order of the parameters passed to proxmox-acme is important
181 # proxmox-acme <setup|teardown> $plugin <$domain|$alias> $txtvalue [$plugin_conf_string]
182 push @$cmd, "/bin/bash", $ACME_PATH, $action, $dnsplugin;
183 if ($alias) {
184 push @$cmd, $alias;
185 } else {
186 push @$cmd, $domain;
187 }
13bc64ea
FG
188 my $input = "$txtvalue\n";
189 $input .= "$plugin_conf_string\n" if $plugin_conf_string;
f00829fd 190
13bc64ea 191 PVE::Tools::run_command($cmd, input => $input);
f00829fd
FG
192
193 $data->{url} = $challenge->{url};
194
195 return $domain;
196};
197
198sub setup {
199 my ($self, $acme, $auth, $data) = @_;
200
201 my $domain = $proxmox_acme_command->($self, $acme, $auth, $data, 'setup');
98b96d9e 202 print "Add TXT record: _acme-challenge.$domain\n";
4317ba99
TL
203
204 # FIXME: probe ourself for propagation of TXT record, while not 100%
205 # failsafe it's good enough of a heuristic to do away with fixed sleep
206 # intervalls - original acme.sh employs that heuristic too.
207 my $delay = $data->{'validation-delay'} // 30;
208 if ($delay > 0) {
209 print "Sleeping $delay seconds to wait for TXT record propagation\n";
210 sleep($delay); # don't care for EINTR
211 }
98b96d9e
WL
212}
213
98b96d9e 214sub teardown {
f00829fd 215 my ($self, $acme, $auth, $data) = @_;
98b96d9e 216
f00829fd 217 my $domain = $proxmox_acme_command->($self, $acme, $auth, $data, 'teardown');
98b96d9e
WL
218 print "Remove TXT record: _acme-challenge.$domain\n";
219}
220
2211;