@@ -37,15 +37,136 @@ class letsencrypt {
3737 */
3838 private $ base_path = '/etc/letsencrypt ' ;
3939 private $ renew_config_path = '/etc/letsencrypt/renewal ' ;
40-
40+ private $ certbot_use_certcommand = false ;
4141
4242 public function __construct (){
4343
4444 }
45+
46+ public function get_acme_script () {
47+ $ acme = explode ("\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+ if ($ cmd == '' ) {
67+ return false ;
68+ }
69+
70+ $ cmd = 'R=0 ; C=0 ; ' . $ letsencrypt . ' --issue ' . $ cmd . ' -w /usr/local/ispconfig/interface/acme ; R=$? ; if [[ $R -eq 0 || $R -eq 2 ]] ; then ' . $ 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 ()) . '; C=$? ; fi ; if [[ $C -eq 0 ]] ; then exit $R ; else exit $C ; fi ' ;
71+
72+ return $ cmd ;
73+ }
74+
75+ public function get_certbot_script () {
76+ $ letsencrypt = explode ("\n" , shell_exec ('which letsencrypt certbot /root/.local/share/letsencrypt/bin/letsencrypt /opt/eff.org/certbot/venv/bin/certbot ' ));
77+ $ letsencrypt = reset ($ letsencrypt );
78+ if (is_executable ($ letsencrypt )) {
79+ return $ letsencrypt ;
80+ } else {
81+ return false ;
82+ }
83+ }
4584
85+ private function install_acme () {
86+ $ install_cmd = 'wget -O - https://get.acme.sh | sh ' ;
87+ $ ret = null ;
88+ $ val = 0 ;
89+ exec ($ install_cmd . ' 2>&1 ' , $ ret , $ val );
90+
91+ return ($ val == 0 ? true : false );
92+ }
93+
94+ private function get_reload_command () {
95+ global $ app , $ conf ;
96+
97+ $ web_config = $ app ->getconf ->get_server_config ($ conf ['server_id ' ], 'web ' );
98+
99+ $ daemon = '' ;
100+ switch ($ web_config ['server_type ' ]) {
101+ case 'nginx ' :
102+ $ daemon = $ web_config ['server_type ' ];
103+ break ;
104+ default :
105+ if (is_file ($ conf ['init_scripts ' ] . '/ ' . 'httpd24-httpd ' ) || is_dir ('/opt/rh/httpd24/root/etc/httpd ' )) {
106+ $ daemon = 'httpd24-httpd ' ;
107+ } elseif (is_file ($ conf ['init_scripts ' ] . '/ ' . 'httpd ' ) || is_dir ('/etc/httpd ' )) {
108+ $ daemon = 'httpd ' ;
109+ } else {
110+ $ daemon = 'apache2 ' ;
111+ }
112+ }
113+
114+ $ cmd = $ app ->system ->getinitcommand ($ daemon , 'force-reload ' );
115+ return $ cmd ;
116+ }
117+
118+ public function get_certbot_command ($ domains ) {
119+ global $ app ;
120+
121+ $ letsencrypt = $ this ->get_certbot_script ();
122+
123+ $ cmd = '' ;
124+ // generate cli format
125+ foreach ($ domains as $ domain ) {
126+ $ cmd .= (string ) " --domains " . $ domain ;
127+ }
128+
129+ if ($ cmd == '' ) {
130+ return false ;
131+ }
132+
133+ $ matches = array ();
134+ $ ret = null ;
135+ $ val = 0 ;
136+
137+ $ letsencrypt_version = exec ($ letsencrypt . ' --version 2>&1 ' , $ ret , $ val );
138+ if (preg_match ('/^(\S+|\w+)\s+(\d+(\.\d+)+)$/ ' , $ letsencrypt_version , $ matches )) {
139+ $ letsencrypt_version = $ matches [2 ];
140+ }
141+ if (version_compare ($ letsencrypt_version , '0.22 ' , '>= ' )) {
142+ $ acme_version = 'https://acme-v02.api.letsencrypt.org/directory ' ;
143+ } else {
144+ $ acme_version = 'https://acme-v01.api.letsencrypt.org/directory ' ;
145+ }
146+ if (version_compare ($ letsencrypt_version , '0.30 ' , '>= ' )) {
147+ $ app ->log ("LE version is " . $ letsencrypt_version . ", so using certificates command " , LOGLEVEL_DEBUG );
148+ $ this ->certbot_use_certcommand = true ;
149+ $ webroot_map = array ();
150+ for ($ i = 0 ; $ i < count ($ domains ); $ i ++) {
151+ $ webroot_map [$ domains [$ i ]] = '/usr/local/ispconfig/interface/acme ' ;
152+ }
153+ $ webroot_args = "--webroot-map " . escapeshellarg (str_replace (array ("\r" , "\n" ), '' , json_encode ($ webroot_map )));
154+ } else {
155+ $ webroot_args = "$ cmd --webroot-path /usr/local/ispconfig/interface/acme " ;
156+ }
157+
158+ $ 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 " ;
159+
160+ return $ cmd ;
161+ }
162+
46163 public function get_letsencrypt_certificate_paths ($ domains = array ()) {
47164 global $ app ;
48165
166+ if ($ this ->get_acme_script ()) {
167+ return false ;
168+ }
169+
49170 if (empty ($ domains )) return false ;
50171 if (!is_dir ($ this ->renew_config_path )) return false ;
51172
@@ -133,9 +254,13 @@ public function get_letsencrypt_certificate_paths($domains = array()) {
133254 }
134255
135256 private function get_ssl_domain ($ data ) {
136- $ domain = $ data ['new ' ]['ssl_domain ' ];
137- if (!$ domain ) $ domain = $ data ['new ' ]['domain ' ];
257+ global $ app ;
138258
259+ $ domain = $ data ['new ' ]['ssl_domain ' ];
260+ if (!$ domain ) {
261+ $ domain = $ data ['new ' ]['domain ' ];
262+ }
263+
139264 if ($ data ['new ' ]['ssl ' ] == 'y ' && $ data ['new ' ]['ssl_letsencrypt ' ] == 'y ' ) {
140265 $ domain = $ data ['new ' ]['domain ' ];
141266 if (substr ($ domain , 0 , 2 ) === '*. ' ) {
@@ -149,8 +274,6 @@ private function get_ssl_domain($data) {
149274 }
150275
151276 public function get_website_certificate_paths ($ data ) {
152- global $ app ;
153-
154277 $ ssl_dir = $ data ['new ' ]['document_root ' ].'/ssl ' ;
155278 $ domain = $ this ->get_ssl_domain ($ data );
156279
@@ -183,11 +306,17 @@ public function request_certificates($data, $server_type = 'apache') {
183306 $ web_config = $ app ->getconf ->get_server_config ($ conf ['server_id ' ], 'web ' );
184307 $ server_config = $ app ->getconf ->get_server_config ($ conf ['server_id ' ], 'server ' );
185308
309+ $ use_acme = false ;
310+ if ($ this ->get_acme_script ()) {
311+ $ use_acme = true ;
312+ } elseif (!$ this ->get_certbot_script ()) {
313+ // acme and le missing
314+ $ this ->install_acme ();
315+ }
316+
186317 $ tmp = $ app ->letsencrypt ->get_website_certificate_paths ($ data );
187318 $ domain = $ tmp ['domain ' ];
188319 $ key_file = $ tmp ['key ' ];
189- $ key_file2 = $ tmp ['key2 ' ];
190- $ csr_file = $ tmp ['csr ' ];
191320 $ crt_file = $ tmp ['crt ' ];
192321 $ bundle_file = $ tmp ['bundle ' ];
193322
@@ -256,66 +385,50 @@ public function request_certificates($data, $server_type = 'apache') {
256385 $ app ->log ("There were " . $ le_domain_count . " domains in the domain list. LE only supports 100, so we strip the rest. " , LOGLEVEL_WARN );
257386 }
258387
259- // generate cli format
260- foreach ($ temp_domains as $ temp_domain ) {
261- $ cli_domain_arg .= (string ) " --domains " . $ temp_domain ;
262- }
263-
264388 // unset useless data
265389 unset($ subdomains );
266390 unset($ aliasdomains );
267391
268- $ letsencrypt_use_certcommand = false ;
392+ $ this -> certbot_use_certcommand = false ;
269393 $ letsencrypt_cmd = '' ;
270- $ letsencrypt = false ;
271- $ success = false ;
272-
273- $ letsencrypt = explode ("\n" , shell_exec ('which letsencrypt certbot /root/.local/share/letsencrypt/bin/letsencrypt /opt/eff.org/certbot/venv/bin/certbot ' ));
274- $ letsencrypt = reset ($ letsencrypt );
275- if (!is_executable ($ letsencrypt )) {
276- $ letsencrypt = false ;
394+ $ allow_return_codes = null ;
395+ if ($ use_acme ) {
396+ $ letsencrypt_cmd = $ this ->get_acme_command ($ temp_domains , $ key_file , $ bundle_file , $ crt_file );
397+ $ allow_return_codes = array (2 );
398+ } else {
399+ $ letsencrypt_cmd = $ this ->get_certbot_command ($ temp_domains );
277400 }
278- if (!empty ($ cli_domain_arg )) {
401+
402+ $ success = false ;
403+ if ($ letsencrypt_cmd ) {
279404 if (!isset ($ server_config ['migration_mode ' ]) || $ server_config ['migration_mode ' ] != 'y ' ) {
280405 $ app ->log ("Create Let's Encrypt SSL Cert for: $ domain " , LOGLEVEL_DEBUG );
281406 $ app ->log ("Let's Encrypt SSL Cert domains: $ cli_domain_arg " , LOGLEVEL_DEBUG );
282-
283- if ($ letsencrypt ) {
284- $ letsencrypt_version = exec ($ letsencrypt . ' --version 2>&1 ' , $ ret , $ val );
285- if (preg_match ('/^(\S+|\w+)\s+(\d+(\.\d+)+)$/ ' , $ letsencrypt_version , $ matches )) {
286- $ letsencrypt_version = $ matches [2 ];
287- }
288- if (version_compare ($ letsencrypt_version , '0.22 ' , '>= ' )) {
289- $ acme_version = 'https://acme-v02.api.letsencrypt.org/directory ' ;
290- } else {
291- $ acme_version = 'https://acme-v01.api.letsencrypt.org/directory ' ;
292- }
293- if (version_compare ($ letsencrypt_version , '0.30 ' , '>= ' )) {
294- $ app ->log ("LE version is " . $ letsencrypt_version . ", so using certificates command " , LOGLEVEL_DEBUG );
295- $ letsencrypt_use_certcommand = true ;
296- $ webroot_map = array ();
297- for ($ i = 0 ; $ i < count ($ temp_domains ); $ i ++) {
298- $ webroot_map [$ temp_domains [$ i ]] = '/usr/local/ispconfig/interface/acme ' ;
299- }
300- $ webroot_args = "--webroot-map " . escapeshellarg (str_replace (array ("\r" , "\n" ), '' , json_encode ($ webroot_map )));
301- } else {
302- $ webroot_args = "$ cli_domain_arg --webroot-path /usr/local/ispconfig/interface/acme " ;
303- }
304-
305- $ letsencrypt_cmd = $ letsencrypt . " certonly -n --text --agree-tos --expand --authenticator webroot --server $ acme_version --rsa-key-size 4096 --email postmaster@ $ domain $ webroot_args " ;
306- $ success = $ app ->system ->_exec ($ letsencrypt_cmd );
307- }
407+
408+ $ success = $ app ->system ->_exec ($ letsencrypt_cmd , $ allow_return_codes );
308409 } else {
309410 $ app ->log ("Migration mode active, skipping Let's Encrypt SSL Cert creation for: $ domain " , LOGLEVEL_DEBUG );
310411 $ success = true ;
311412 }
312413 }
414+
415+ if ($ use_acme === true ) {
416+ if (!$ success ) {
417+ $ app ->log ('Let \'s Encrypt SSL Cert for: ' . $ domain . ' could not be issued. ' , LOGLEVEL_WARN );
418+ $ app ->log ($ letsencrypt_cmd , LOGLEVEL_WARN );
419+ return false ;
420+ } else {
421+ return true ;
422+ }
423+ }
424+
313425 $ le_files = array ();
314- if ($ letsencrypt_use_certcommand === true && $ letsencrypt ) {
315- $ letsencrypt_cmd = $ letsencrypt . " certificates " . $ cli_domain_arg ;
426+ if ($ this -> certbot_use_certcommand === true && $ letsencrypt_cmd ) {
427+ $ letsencrypt_cmd = $ letsencrypt_cmd . " certificates " . $ cli_domain_arg ;
316428 $ output = explode ("\n" , shell_exec ($ letsencrypt_cmd . " 2>/dev/null | grep -v '^ \$' " ));
317429 $ le_path = '' ;
318430 $ skip_to_next = true ;
431+ $ matches = null ;
319432 foreach ($ output as $ outline ) {
320433 $ outline = trim ($ outline );
321434 $ app ->log ("LE CERT OUTPUT: " . $ outline , LOGLEVEL_DEBUG );
@@ -415,6 +528,4 @@ public function request_certificates($data, $server_type = 'apache') {
415528 return false ;
416529 }
417530 }
418- }
419-
420- ?>
531+ }
0 commit comments