@@ -87,8 +87,8 @@ public function get_acme_command($domains, $key_file, $bundle_file, $cert_file,
8787 'R=0 ; C=0 ' ,
8888 $ acme_sh . ' --issue ' . $ domain_args . ' -w /usr/local/ispconfig/interface/acme --always-force-new-domain-key ' . $ conf_selection_arg . $ certificate_type_arg ,
8989 'R=$? ' ,
90- 'if [ $R -eq 0 -o $R -eq 2 ] ' ,
91- ' then ' . $ acme_sh . ' --install-cert ' . $ domain_args . $ conf_selection_arg . $ files_to_install . ' --reloadcmd ' . escapeshellarg ($ this ->get_reload_command ($ server_type )),
90+ 'if [ $R -eq 0 ] || [ $R -eq 2 ]; then : ' ,
91+ ' ' . $ acme_sh . ' --install-cert ' . $ domain_args . $ conf_selection_arg . $ files_to_install . ' --reloadcmd ' . escapeshellarg ($ this ->get_reload_command ($ server_type )),
9292 ' C=$? ' ,
9393 'fi ' ,
9494 'if [ $C -eq 0 ] ' ,
@@ -521,41 +521,71 @@ public function get_certificate_list() {
521521
522522 $ candidates = [];
523523 if ($ use_acme ) {
524- $ info = $ app ->system ->system_safe ($ shell_script . ' --info 2>/dev/null ' );
525- // try to auto-upgrade acme.sh when --info command is not there
526- if ($ app ->system ->last_exec_retcode () != 0 ) {
527- $ app ->system ->system_safe ($ shell_script . ' --upgrade 2>&1 ' );
528- $ info = $ app ->system ->system_safe ($ shell_script . ' --info 2>/dev/null ' );
529- }
530- if ($ app ->system ->last_exec_retcode () != 0 ) {
531- $ app ->log ('get_certificate_list: acme.sh --info failed ' , LOGLEVEL_ERROR );
532- return [];
533- }
534- $ info = $ this ->parse_env_file ($ info );
535- $ cert_dir = !empty ($ info ['CERT_HOME ' ]) ? $ info ['CERT_HOME ' ] : $ info ['LE_CONFIG_HOME ' ];
536- if (empty ($ cert_dir ) || !is_dir ($ cert_dir )) {
537- $ app ->log ('get_certificate_list: could not find certificate home ' . $ cert_dir , LOGLEVEL_ERROR );
524+ // Use an inline shell script to get the configured acme.sh certificate home.
525+ // We use a shell script because acme.sh config file is a shell script itself - to support even dynamic configs, we will evaluate the config file.
526+ // The used --info command was not always there, so we try to auto-upgrade acme.sh when the command fails
527+ $ home_extract_cmd = join (' ; ' , [
528+ '_info() { : ' ,
529+ ' _info_stdout=$( ' . escapeshellarg ($ shell_script ) . ' --info 2>/dev/null) ' ,
530+ ' _info_ret=$? ' ,
531+ '} ' ,
532+ '_echo_home() { : ' ,
533+ ' eval "$_info_stdout" ' ,
534+ ' _info_ret=$? ' ,
535+ ' if [ $_info_ret -eq 0 ]; then : ' ,
536+ ' if [ -z "$CERT_HOME" ] ' ,
537+ ' then echo "$LE_CONFIG_HOME" ' ,
538+ ' else echo "$CERT_HOME" ' ,
539+ ' fi ' ,
540+ ' else : ' ,
541+ ' echo "Error eval-ing --info output (exit code $_info_ret). stdout was: $_info_stdout" ' ,
542+ ' exit 1 ' ,
543+ ' fi ' ,
544+ '} ' ,
545+ '_info ' ,
546+ 'if [ $_info_ret -eq 0 ]; then : ' ,
547+ ' _echo_home ' ,
548+ 'else : ' ,
549+ ' if ' . escapeshellarg ($ shell_script ) . ' --upgrade 2>&1; then : ' ,
550+ ' _info ' ,
551+ ' if [ $_info_ret -eq 0 ]; then : ' ,
552+ ' _echo_home ' ,
553+ ' else : ' ,
554+ ' echo "--info failed (exit code $_info_ret). stdout was: $_info_stdout" ' ,
555+ ' exit 1 ' ,
556+ ' fi ' ,
557+ ' else : ' ,
558+ ' echo "--info failed (exit code $_info_ret) and auto-upgrade failed, too. Initial info stdout was: $_info_stdout" ' ,
559+ ' exit 1 ' ,
560+ ' fi ' ,
561+ 'fi ' ,
562+ ]);
563+ $ ret = 0 ;
564+ $ cert_home = [];
565+ exec ($ home_extract_cmd , $ cert_home , $ ret );
566+ $ cert_home = trim (implode ("\n" , $ cert_home ));
567+ if ($ ret != 0 || empty ($ cert_home ) || !is_dir ($ cert_home )) {
568+ $ app ->log ('get_certificate_list: could not find certificate home. Error: ' . $ cert_home . '. Command used: ' . $ home_extract_cmd , LOGLEVEL_ERROR );
538569 return [];
539570 }
540- $ dir = opendir ($ cert_dir );
571+ $ app ->log ('get_certificate_list: discovered cert home as ' . $ cert_home . '. Command used: ' . $ home_extract_cmd , LOGLEVEL_DEBUG );
572+ $ dir = opendir ($ cert_home );
541573 if (!$ dir ) {
542- $ app ->log ('get_certificate_list: could not open certificate home ' . $ cert_dir , LOGLEVEL_ERROR );
574+ $ app ->log ('get_certificate_list: could not open certificate home ' . $ cert_home , LOGLEVEL_ERROR );
543575 return [];
544576 }
545577 while ($ path = readdir ($ dir )) {
578+ $ full_path = $ cert_home . '/ ' . $ path ;
546579 // valid conf dirs have a . in them
547- if ($ path === '. ' || $ path === '.. ' || strpos ($ path , '. ' ) === false ) {
548- continue ;
549- }
550- $ full_path = $ cert_dir . '/ ' . $ path ;
551- if (!is_dir ($ full_path )) {
580+ if ($ path === '. ' || $ path === '.. ' || strpos ($ path , '. ' ) === false || !is_dir ($ full_path )) {
552581 continue ;
553582 }
554583 $ domain = $ path ;
555584 if (preg_match ('/_ecc$/ ' , $ path )) {
556585 $ domain = substr ($ path , 0 , -4 );
557586 }
558- if (!$ this ->is_readable_link_or_file ("$ full_path/ $ domain.conf " )) {
587+ if (!$ this ->is_readable_link_or_file ($ full_path . '/ ' . $ domain . '.conf ' )) {
588+ $ app ->log ('get_certificate_list: skip ' . $ full_path . '/ ' . $ domain . '.conf because it is not readable ' , LOGLEVEL_DEBUG );
559589 continue ;
560590 }
561591 $ candidates [] = [
@@ -666,7 +696,7 @@ public function remove_certificate($certificate) {
666696 }
667697 } else {
668698 if (is_dir ($ certificate ['conf ' ])) {
669- if (!$ app ->system ->rmdir ($ certificate ['conf ' ], false )) {
699+ if (!$ app ->system ->rmdir ($ certificate ['conf ' ], true )) {
670700 $ app ->log ('remove_certificate: could not delete config folder ' . $ certificate ['conf ' ], LOGLEVEL_WARN );
671701 return false ;
672702 }
@@ -738,7 +768,7 @@ public function extract_x509($cert_file, $chain_file = null) {
738768 $ signature_type = 'ECDSA ' ;
739769 }
740770 return [
741- 'serial_number ' => $ info ['serialNumber ' ],
771+ 'serial_number ' => $ info ['serialNumberHex ' ] ?: $ info [ ' serialNumber ' ],
742772 'signature_type ' => $ signature_type ,
743773 'subject ' => $ info ['subject ' ],
744774 'issuer ' => $ info ['issuer ' ],
@@ -762,29 +792,4 @@ private function is_domain_name_or_wildcard($input) {
762792 private function is_readable_link_or_file ($ path ) {
763793 return $ path && (@is_link ($ path ) || @is_file ($ path )) && @is_readable ($ path );
764794 }
765-
766- private function parse_env_file ($ lines ) {
767- $ variables = [];
768- foreach ($ lines as $ line ) {
769- $ line = trim ($ line );
770- // does only handle comment-only lines.
771- // lines like `KEY=Value # inline-comment` are not supported (and normally not used by acme.sh)
772- if (!$ line || substr ($ line , 0 , 1 ) == '# ' ) {
773- continue ;
774- }
775- $ parts = explode ('= ' , $ line , 2 );
776- if (count ($ parts ) < 2 ) {
777- continue ;
778- }
779- $ key = trim ($ parts [0 ]);
780- $ value = trim ($ parts [1 ]);
781- if (preg_match ('/^"(.*)"$/ ' , $ value , $ matches )) {
782- $ value = $ matches [1 ];
783- } elseif (preg_match ("/^'(.*)'$/ " , $ value , $ matches )) {
784- $ value = $ matches [1 ];
785- }
786- $ variables [$ key ] = $ value ;
787- }
788- return $ variables ;
789- }
790795}
0 commit comments