]> git.proxmox.com Git - proxmox-acme.git/blob - src/PVE/ACME/DNSChallenge.pm
plugins: unify extract_challenge
[proxmox-acme.git] / src / PVE / ACME / DNSChallenge.pm
1 package PVE::ACME::DNSChallenge;
2
3 use strict;
4 use warnings;
5
6 use Digest::SHA qw(sha256);
7 use PVE::Tools;
8
9 use base qw(PVE::ACME::Challenge);
10
11 my $ACME_PATH = '/usr/share/proxmox-acme/proxmox-acme';
12
13 sub supported_challenge_types {
14 return ["dns-01"];
15 }
16
17 sub type {
18 return 'dns';
19 }
20
21 my $api_name_list = [
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 ];
122
123 sub properties {
124 return {
125 api => {
126 description => "API plugin name",
127 type => 'string',
128 enum => $api_name_list,
129 },
130 data => {
131 type => 'string',
132 description => 'DNS plugin data.',
133 },
134 };
135 }
136
137 sub options {
138 return {
139 api => {},
140 data => { optional => 1 },
141 nodes => { optional => 1 },
142 disable => { optional => 1 },
143 };
144 }
145
146 sub get_subplugins {
147 return $api_name_list;
148 }
149
150 my $proxmox_acme_command = sub {
151 my ($self, $acme, $auth, $data, $action) = @_;
152
153 die "No plugin data for DNSChallenge\n" if !defined($data->{plugin});
154
155 my $alias = $data->{alias};
156 my $domain = $auth->{identifier}->{value};
157
158 my $challenge = $self->extract_challenge($auth->{challenges});
159 my $key_auth = $acme->key_authorization($challenge->{token});
160
161 my $txtvalue = PVE::ACME::encode(sha256($key_auth));
162 my $dnsplugin = $data->{plugin}->{api};
163 my $plugin_conf_string = $data->{plugin}->{data};
164
165 # for security reasons, we execute the command as nobody
166 # we can't verify that the code of the DNSPlugins are harmless.
167 my $cmd = ["setpriv", "--reuid", "nobody", "--regid", "nogroup", "--clear-groups", "--"];
168
169 # The order of the parameters passed to proxmox-acme is important
170 # proxmox-acme <setup|teardown> $plugin <$domain|$alias> $txtvalue [$plugin_conf_string]
171 push @$cmd, "/bin/bash", $ACME_PATH, $action, $dnsplugin;
172 if ($alias) {
173 push @$cmd, $alias;
174 } else {
175 push @$cmd, $domain;
176 }
177 push @$cmd, $txtvalue, $plugin_conf_string;
178
179 PVE::Tools::run_command($cmd);
180
181 $data->{url} = $challenge->{url};
182
183 return $domain;
184 };
185
186 sub setup {
187 my ($self, $acme, $auth, $data) = @_;
188
189 my $domain = $proxmox_acme_command->($self, $acme, $auth, $data, 'setup');
190 print "Add TXT record: _acme-challenge.$domain\n";
191 }
192
193 sub teardown {
194 my ($self, $acme, $auth, $data) = @_;
195
196 my $domain = $proxmox_acme_command->($self, $acme, $auth, $data, 'teardown');
197 print "Remove TXT record: _acme-challenge.$domain\n";
198 }
199
200 1;