Skip to content

Commit cb67fdf

Browse files
author
Marius Burkard
committed
- added acme.sh support
1 parent aed1f6f commit cb67fdf

File tree

3 files changed

+139
-34
lines changed

3 files changed

+139
-34
lines changed

interface/lib/classes/system.inc.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public function has_service($userid, $service) {
4141

4242
// simple query cache
4343
if($this->client_service===null)
44-
$this->client_service = $app->db->queryOneRecord("SELECT client.* FROM sys_user, client WHERE sys_user.userid = ? AND sys_user.client_id = client.client_id", $userid);
44+
$this->client_service = $app->db->queryOneRecord("SELECT client.* FROM sys_user, client WHERE sys_user.userid = ? AND sys_user.client_id = client.client_id", $userid);
4545

4646
// isn't service
4747
if(!$this->client_service) return false;

server/lib/classes/cron.d/900-letsencrypt.inc.php

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,11 @@ class cronjob_letsencrypt extends cronjob {
3535

3636
/* this function is optional if it contains no custom code */
3737
public function onPrepare() {
38-
global $app;
39-
4038
parent::onPrepare();
4139
}
4240

4341
/* this function is optional if it contains no custom code */
4442
public function onBeforeRun() {
45-
global $app;
46-
4743
return parent::onBeforeRun();
4844
}
4945

@@ -52,9 +48,19 @@ public function onRunJob() {
5248

5349
$server_config = $app->getconf->get_server_config($conf['server_id'], 'server');
5450
if(!isset($server_config['migration_mode']) || $server_config['migration_mode'] != 'y') {
55-
$letsencrypt = explode("\n", shell_exec('which letsencrypt certbot /root/.local/share/letsencrypt/bin/letsencrypt /opt/eff.org/certbot/venv/bin/certbot'));
56-
$letsencrypt = reset($letsencrypt);
57-
if(is_executable($letsencrypt)) {
51+
52+
$acme = $app->letsencrypt->get_acme_script();
53+
if($acme) {
54+
// skip letsencrypt
55+
parent::onRunJob();
56+
return;
57+
}
58+
59+
$letsencrypt = $app->letsencrypt->get_certbot_script();
60+
if($letsencrypt) {
61+
$ret = null;
62+
$val = 0;
63+
$matches = array();
5864
$version = exec($letsencrypt . ' --version 2>&1', $ret, $val);
5965
if(preg_match('/^(\S+|\w+)\s+(\d+(\.\d+)+)$/', $version, $matches)) {
6066
$type = strtolower($matches[1]);
@@ -85,11 +91,7 @@ public function onRunJob() {
8591

8692
/* this function is optional if it contains no custom code */
8793
public function onAfterRun() {
88-
global $app;
89-
9094
parent::onAfterRun();
9195
}
9296

9397
}
94-
95-
?>

server/lib/classes/letsencrypt.inc.php

Lines changed: 125 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,110 @@ class letsencrypt {
4242
public function __construct(){
4343

4444
}
45+
46+
public function get_acme_script() {
47+
$acme = excplode("\n", shell_exec('which /usr/local/ispconfig/server/scripts/acme.sh /root/.acme.sh/acme.sh'));
48+
$acme = reset($acme);
49+
if(is_executable($acme)) {
50+
return $acme;
51+
} else {
52+
return false;
53+
}
54+
}
55+
56+
public function get_acme_command($domains, $key_file, $bundle_file, $cert_file) {
57+
58+
$letsencrypt = $this->get_acme_script();
59+
60+
$cmd = '';
61+
// generate cli format
62+
foreach($domains as $domain) {
63+
$cmd .= (string) " -d " . $domain;
64+
}
65+
66+
$cmd = $letsencrypt . " --issue $cmd -w /usr/local/ispconfig/interface/acme && " . $letsencrypt . " --install-cert " . $cmd . " --key-file " . escapeshellarg($key_file) . " --fullchain-file " . escapeshellarg($bundle_file) . " --cert-file " . escapeshellarg($cert_file) . " --reloadcmd " . escapeshellarg($this->get_reload_command());
67+
68+
return $cmd;
69+
}
70+
71+
public function get_certbot_script() {
72+
$letsencrypt = explode("\n", shell_exec('which letsencrypt certbot /root/.local/share/letsencrypt/bin/letsencrypt /opt/eff.org/certbot/venv/bin/certbot'));
73+
$letsencrypt = reset($letsencrypt);
74+
if(is_executable($letsencrypt)) {
75+
return $letsencrypt;
76+
} else {
77+
return false;
78+
}
79+
}
4580

81+
private function install_acme() {
82+
$install_cmd = 'wget -O - https://get.acme.sh | sh';
83+
$ret = null;
84+
$val = 0;
85+
exec($install_cmd . ' 2>&1', $ret, $val);
86+
87+
return ($val == 0 ? true : false);
88+
}
89+
90+
private function get_reload_command() {
91+
global $app, $conf;
92+
93+
$web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
94+
95+
$daemon = '';
96+
switch ($web_config['server_type']) {
97+
case 'nginx':
98+
$daemon = $web_config['server_type'];
99+
break;
100+
default:
101+
if(is_file($conf['init_scripts'] . '/' . 'httpd24-httpd') || is_dir('/opt/rh/httpd24/root/etc/httpd')) {
102+
$daemon = 'httpd24-httpd';
103+
} elseif(is_file($conf['init_scripts'] . '/' . 'httpd') || is_dir('/etc/httpd')) {
104+
$daemon = 'httpd';
105+
} else {
106+
$daemon = 'apache2';
107+
}
108+
}
109+
110+
$cmd = $app->system->getinitcommand($daemon, 'force-reload');
111+
return $cmd;
112+
}
113+
114+
public function get_certbot_command($domains) {
115+
116+
$letsencrypt = $this->get_certbot_script();
117+
118+
$cmd = '';
119+
// generate cli format
120+
foreach($domains as $domain) {
121+
$cmd .= (string) " --domains " . $domain;
122+
}
123+
124+
$matches = array();
125+
$ret = null;
126+
$val = 0;
127+
$letsencrypt_version = exec($letsencrypt . ' --version 2>&1', $ret, $val);
128+
if(preg_match('/^(\S+|\w+)\s+(\d+(\.\d+)+)$/', $letsencrypt_version, $matches)) {
129+
$letsencrypt_version = $matches[2];
130+
}
131+
if (version_compare($letsencrypt_version, '0.22', '>=')) {
132+
$acme_version = 'https://acme-v02.api.letsencrypt.org/directory';
133+
} else {
134+
$acme_version = 'https://acme-v01.api.letsencrypt.org/directory';
135+
}
136+
137+
$cmd = $letsencrypt . " certonly -n --text --agree-tos --expand --authenticator webroot --server $acme_version --rsa-key-size 4096 --email postmaster@$domain $cmd --webroot-path /usr/local/ispconfig/interface/acme";
138+
139+
return $cmd;
140+
}
141+
46142
public function get_letsencrypt_certificate_paths($domains = array()) {
47143
global $app;
48144

145+
if($this->get_acme_script()) {
146+
return false;
147+
}
148+
49149
if(empty($domains)) return false;
50150
if(!is_dir($this->renew_config_path)) return false;
51151

@@ -183,11 +283,17 @@ public function request_certificates($data, $server_type = 'apache') {
183283
$web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
184284
$server_config = $app->getconf->get_server_config($conf['server_id'], 'server');
185285

286+
$use_acme = false;
287+
if($this->get_acme_script()) {
288+
$use_acme = true;
289+
} elseif(!$this->get_certbot_script()) {
290+
// acme and le missing
291+
$this->install_acme();
292+
}
293+
186294
$tmp = $app->letsencrypt->get_website_certificate_paths($data);
187295
$domain = $tmp['domain'];
188296
$key_file = $tmp['key'];
189-
$key_file2 = $tmp['key2'];
190-
$csr_file = $tmp['csr'];
191297
$crt_file = $tmp['crt'];
192298
$bundle_file = $tmp['bundle'];
193299

@@ -256,43 +362,40 @@ public function request_certificates($data, $server_type = 'apache') {
256362
$app->log("There were " . $le_domain_count . " domains in the domain list. LE only supports 100, so we strip the rest.", LOGLEVEL_WARN);
257363
}
258364

259-
// generate cli format
260-
foreach($temp_domains as $temp_domain) {
261-
$cli_domain_arg .= (string) " --domains " . $temp_domain;
262-
}
263-
264365
// unset useless data
265366
unset($subdomains);
266367
unset($aliasdomains);
267368

268369
$letsencrypt_cmd = '';
370+
if($use_acme) {
371+
$letsencrypt_cmd = $this->get_acme_command($temp_domains, $key_file, $bundle_file, $crt_file);
372+
} else {
373+
$letsencrypt_cmd = $this->get_certbot_command($temp_domains);
374+
}
375+
269376
$success = false;
270377
if(!empty($cli_domain_arg)) {
271378
if(!isset($server_config['migration_mode']) || $server_config['migration_mode'] != 'y') {
272379
$app->log("Create Let's Encrypt SSL Cert for: $domain", LOGLEVEL_DEBUG);
273380
$app->log("Let's Encrypt SSL Cert domains: $cli_domain_arg", LOGLEVEL_DEBUG);
274381

275-
$letsencrypt = explode("\n", shell_exec('which letsencrypt certbot /root/.local/share/letsencrypt/bin/letsencrypt /opt/eff.org/certbot/venv/bin/certbot'));
276-
$letsencrypt = reset($letsencrypt);
277-
if(is_executable($letsencrypt)) {
278-
$letsencrypt_version = exec($letsencrypt . ' --version 2>&1', $ret, $val);
279-
if(preg_match('/^(\S+|\w+)\s+(\d+(\.\d+)+)$/', $letsencrypt_version, $matches)) {
280-
$letsencrypt_version = $matches[2];
281-
}
282-
if ($letsencrypt_version >=0.22) {
283-
$acme_version = 'https://acme-v02.api.letsencrypt.org/directory';
284-
} else {
285-
$acme_version = 'https://acme-v01.api.letsencrypt.org/directory';
286-
}
287-
$letsencrypt_cmd = $letsencrypt . " certonly -n --text --agree-tos --expand --authenticator webroot --server $acme_version --rsa-key-size 4096 --email postmaster@$domain $cli_domain_arg --webroot-path /usr/local/ispconfig/interface/acme";
288-
$success = $app->system->_exec($letsencrypt_cmd);
289-
}
382+
$success = $app->system->_exec($letsencrypt_cmd);
290383
} else {
291384
$app->log("Migration mode active, skipping Let's Encrypt SSL Cert creation for: $domain", LOGLEVEL_DEBUG);
292385
$success = true;
293386
}
294387
}
295388

389+
if($use_acme === true) {
390+
if(!$success) {
391+
$app->log('Let\'s Encrypt SSL Cert for: ' . $domain . ' could not be issued.', LOGLEVEL_WARN);
392+
$app->log($letsencrypt_cmd, LOGLEVEL_WARN);
393+
return false;
394+
} else {
395+
return true;
396+
}
397+
}
398+
296399
$le_files = $this->get_letsencrypt_certificate_paths($temp_domains);
297400
unset($temp_domains);
298401

0 commit comments

Comments
 (0)