]>
git.proxmox.com Git - pve-manager.git/blob - PVE/API2/ACMEAccount.pm
1 package PVE
::API2
::ACMEAccount
;
8 use PVE
::Exception
qw(raise_param_exc);
9 use PVE
::JSONSchema
qw(get_standard_option);
10 use PVE
::RPCEnvironment
;
11 use PVE
::Tools
qw(extract_param);
13 use base
qw(PVE::RESTHandler);
15 my $acme_directories = [
17 name
=> 'Let\'s Encrypt V2',
18 url
=> 'https://acme-v02.api.letsencrypt.org/directory',
21 name
=> 'Let\'s Encrypt V2 Staging',
22 url
=> 'https://acme-staging-v02.api.letsencrypt.org/directory',
26 my $acme_default_directory_url = $acme_directories->[0]->{url
};
28 my $account_contact_from_param = sub {
30 return [ map { "mailto:$_" } PVE
::Tools
::split_list
(extract_param
($param, 'contact')) ];
33 my $acme_account_dir = PVE
::CertHelpers
::acme_account_dir
();
35 __PACKAGE__-
>register_method ({
39 permissions
=> { user
=> 'all' },
40 description
=> "ACMEAccount index.",
42 additionalProperties
=> 0,
52 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
58 { name
=> 'account' },
60 { name
=> 'directories' },
64 __PACKAGE__-
>register_method ({
65 name
=> 'account_index',
68 permissions
=> { user
=> 'all' },
69 description
=> "ACMEAccount index.",
72 additionalProperties
=> 0,
82 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
87 my $accounts = PVE
::CertHelpers
::list_acme_accounts
();
88 return [ map { { name
=> $_ } } @$accounts ];
91 __PACKAGE__-
>register_method ({
92 name
=> 'register_account',
95 description
=> "Register a new ACME account with CA.",
98 additionalProperties
=> 0,
100 name
=> get_standard_option
('pve-acme-account-name'),
101 contact
=> get_standard_option
('pve-acme-account-contact'),
104 description
=> 'URL of CA TermsOfService - setting this indicates agreement.',
107 directory
=> get_standard_option
('pve-acme-directory-url', {
108 default => $acme_default_directory_url,
119 my $account_name = extract_param
($param, 'name') // 'default';
120 my $account_file = "${acme_account_dir}/${account_name}";
122 mkdir $acme_account_dir;
124 raise_param_exc
({'name' => "ACME account config file '${account_name}' already exists."})
127 my $directory = extract_param
($param, 'directory') // $acme_default_directory_url;
128 my $contact = $account_contact_from_param->($param);
130 my $rpcenv = PVE
::RPCEnvironment
::get
();
132 my $authuser = $rpcenv->get_user();
135 PVE
::Cluster
::cfs_lock_acme
($account_name, 10, sub {
136 die "ACME account config file '${account_name}' already exists.\n"
139 my $acme = PVE
::ACME-
>new($account_file, $directory);
140 print "Generating ACME account key..\n";
142 print "Registering ACME account..\n";
143 eval { $acme->new_account($param->{tos_url
}, contact
=> $contact); };
146 unlink $account_file;
147 die "Registration failed!\n";
149 print "Registration successful, account URL: '$acme->{location}'\n";
154 return $rpcenv->fork_worker('acmeregister', undef, $authuser, $realcmd);
157 my $update_account = sub {
158 my ($param, $msg, %info) = @_;
160 my $account_name = extract_param
($param, 'name') // 'default';
161 my $account_file = "${acme_account_dir}/${account_name}";
163 raise_param_exc
({'name' => "ACME account config file '${account_name}' does not exist."})
164 if ! -e
$account_file;
167 my $rpcenv = PVE
::RPCEnvironment
::get
();
169 my $authuser = $rpcenv->get_user();
172 PVE
::Cluster
::cfs_lock_acme
($account_name, 10, sub {
173 die "ACME account config file '${account_name}' does not exist.\n"
174 if ! -e
$account_file;
176 my $acme = PVE
::ACME-
>new($account_file);
178 $acme->update_account(%info);
179 if ($info{status
} && $info{status
} eq 'deactivated') {
180 my $deactivated_name;
182 my $candidate = "${acme_account_dir}/_deactivated_${account_name}_${i}";
183 if (! -e
$candidate) {
184 $deactivated_name = $candidate;
188 if ($deactivated_name) {
189 print "Renaming account file from '$account_file' to '$deactivated_name'\n";
190 rename($account_file, $deactivated_name) or
191 warn ".. failed - $!\n";
193 warn "No free slot to rename deactivated account file '$account_file', leaving in place\n";
200 return $rpcenv->fork_worker("acme${msg}", undef, $authuser, $realcmd);
203 __PACKAGE__-
>register_method ({
204 name
=> 'update_account',
205 path
=> 'account/{name}',
207 description
=> "Update existing ACME account information with CA. Note: not specifying any new account information triggers a refresh.",
210 additionalProperties
=> 0,
212 name
=> get_standard_option
('pve-acme-account-name'),
213 contact
=> get_standard_option
('pve-acme-account-contact', {
224 my $contact = $account_contact_from_param->($param);
225 if (scalar @$contact) {
226 return $update_account->($param, 'update', contact
=> $contact);
228 return $update_account->($param, 'refresh');
232 __PACKAGE__-
>register_method ({
233 name
=> 'get_account',
234 path
=> 'account/{name}',
236 description
=> "Return existing ACME account information.",
239 additionalProperties
=> 0,
241 name
=> get_standard_option
('pve-acme-account-name'),
246 additionalProperties
=> 0,
253 directory
=> get_standard_option
('pve-acme-directory-url', {
269 my $account_name = extract_param
($param, 'name') // 'default';
270 my $account_file = "${acme_account_dir}/${account_name}";
272 raise_param_exc
({'name' => "ACME account config file '${account_name}' does not exist."})
273 if ! -e
$account_file;
275 my $acme = PVE
::ACME-
>new($account_file);
279 $res->{account
} = $acme->{account
};
280 $res->{directory
} = $acme->{directory
};
281 $res->{location
} = $acme->{location
};
282 $res->{tos
} = $acme->{tos
};
287 __PACKAGE__-
>register_method ({
288 name
=> 'deactivate_account',
289 path
=> 'account/{name}',
291 description
=> "Deactivate existing ACME account at CA.",
294 additionalProperties
=> 0,
296 name
=> get_standard_option
('pve-acme-account-name'),
305 return $update_account->($param, 'deactivate', status
=> 'deactivated');
308 __PACKAGE__-
>register_method ({
312 description
=> "Retrieve ACME TermsOfService URL from CA.",
313 permissions
=> { user
=> 'all' },
315 additionalProperties
=> 0,
317 directory
=> get_standard_option
('pve-acme-directory-url', {
318 default => $acme_default_directory_url,
325 description
=> 'ACME TermsOfService URL.',
330 my $directory = extract_param
($param, 'directory') // $acme_default_directory_url;
332 my $acme = PVE
::ACME-
>new(undef, $directory);
333 my $meta = $acme->get_meta();
335 return $meta ?
$meta->{termsOfService
} : undef;
338 __PACKAGE__-
>register_method ({
339 name
=> 'get_directories',
340 path
=> 'directories',
342 description
=> "Get named known ACME directory endpoints.",
343 permissions
=> { user
=> 'all' },
345 additionalProperties
=> 0,
352 additionalProperties
=> 0,
357 url
=> get_standard_option
('pve-acme-directory-url'),
364 return $acme_directories;