|
1 | 1 | <?php |
2 | 2 |
|
3 | 3 | /* |
4 | | -Copyright (c) 2007-2010, Till Brehm, projektfarm Gmbh |
| 4 | +Copyright (c) 2007-2019, Till Brehm, projektfarm GmbH |
5 | 5 | All rights reserved. |
6 | 6 |
|
7 | 7 | Redistribution and use in source and binary forms, with or without modification, |
@@ -2684,34 +2684,254 @@ public function configure_apps_vhost() { |
2684 | 2684 | if(!@is_link($vhost_conf_enabled_dir.'/000-apps.vhost')) { |
2685 | 2685 | symlink($vhost_conf_dir.'/apps.vhost', $vhost_conf_enabled_dir.'/000-apps.vhost'); |
2686 | 2686 | } |
| 2687 | + } |
| 2688 | + } |
2687 | 2689 |
|
| 2690 | + private function curl_request($url, $use_ipv6 = false) { |
| 2691 | + $set_headers = [ |
| 2692 | + 'Connection: Close', |
| 2693 | + 'User-Agent: ISPConfig/3', |
| 2694 | + 'Accept: */*' |
| 2695 | + ]; |
| 2696 | + |
| 2697 | + $ch = curl_init($url); |
| 2698 | + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); |
| 2699 | + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); |
| 2700 | + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); |
| 2701 | + curl_setopt($ch, CURLOPT_FORBID_REUSE, 1); |
| 2702 | + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); |
| 2703 | + curl_setopt($ch, CURLOPT_HTTPHEADER, $set_headers); |
| 2704 | + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); |
| 2705 | + curl_setopt($ch, CURLOPT_MAXREDIRS, 5); |
| 2706 | + |
| 2707 | + if($use_ipv6) { |
| 2708 | + if(defined('CURLOPT_IPRESOLVE') && defined('CURL_IPRESOLVE_V6')) { |
| 2709 | + curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); |
| 2710 | + } |
| 2711 | + } else { |
| 2712 | + if(defined('CURLOPT_IPRESOLVE') && defined('CURL_IPRESOLVE_V4')) { |
| 2713 | + curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); |
| 2714 | + } |
2688 | 2715 | } |
| 2716 | + |
| 2717 | + $response = curl_exec($ch); |
| 2718 | + curl_close($ch); |
| 2719 | + |
| 2720 | + return $response; |
2689 | 2721 | } |
2690 | 2722 |
|
2691 | 2723 | public function make_ispconfig_ssl_cert() { |
2692 | | - global $conf,$autoinstall; |
| 2724 | + global $conf, $autoinstall; |
2693 | 2725 |
|
2694 | | - $install_dir = $conf['ispconfig_install_dir']; |
| 2726 | + //* Get hostname from user entry or shell command */ |
| 2727 | + if($conf['hostname'] !== 'localhost' && $conf['hostname'] !== '') { |
| 2728 | + $hostname = $conf['hostname']; |
| 2729 | + } else { |
| 2730 | + $hostname = exec('hostname -f'); |
| 2731 | + } |
| 2732 | + |
| 2733 | + // Check dns a record exist and its ip equal to server public ip |
| 2734 | + $svr_ip4 = $this->curl_request('https://ispconfig.org/remoteip.php', false); |
| 2735 | + $svr_ip6 = $this->curl_request('https://ispconfig.org/remoteip.php', true); |
| 2736 | + |
| 2737 | + if(function_exists('idn_to_ascii')) { |
| 2738 | + if(defined('IDNA_NONTRANSITIONAL_TO_ASCII') && defined('INTL_IDNA_VARIANT_UTS46') && constant('IDNA_NONTRANSITIONAL_TO_ASCII')) { |
| 2739 | + $hostname = idn_to_ascii($hostname, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46); |
| 2740 | + } else { |
| 2741 | + $hostname = idn_to_ascii($hostname); |
| 2742 | + } |
| 2743 | + } |
| 2744 | + $dns_ips = array(); |
| 2745 | + if (checkdnsrr($hostname, 'A')) { |
| 2746 | + $dnsa=dns_get_record($hostname, DNS_A); |
| 2747 | + if($dnsa) { |
| 2748 | + foreach ($dnsa as $rec) { |
| 2749 | + $dns_ips[] = $rec['ip']; |
| 2750 | + } |
| 2751 | + } |
| 2752 | + } |
| 2753 | + if (checkdnsrr($hostname, 'AAAA')) { |
| 2754 | + $dnsaaaa=dns_get_record($hostname, DNS_AAAA); |
| 2755 | + if($dnsaaaa) { |
| 2756 | + foreach ($dnsaaaa as $rec) { |
| 2757 | + $dns_ips[] = $rec['ip']; |
| 2758 | + } |
| 2759 | + } |
| 2760 | + } |
| 2761 | + |
| 2762 | + // Request for certs if no LE SSL folder for server fqdn exist |
| 2763 | + $le_live_dir = '/etc/letsencrypt/live/' . $hostname; |
| 2764 | + if (!@is_dir($le_live_dir) && ( |
| 2765 | + ($svr_ip4 && in_array($svr_ip4, $dns_ips)) || ($svr_ip6 && in_array($svr_ip6, $dns_ips)) |
| 2766 | + )) { |
| 2767 | + |
| 2768 | + // This script is needed earlier to check and open http port 80 or standalone might fail |
| 2769 | + // Make executable and temporary symlink latest letsencrypt pre, post and renew hook script before install |
| 2770 | + if(file_exists(dirname(getcwd()) . '/server/scripts/letsencrypt_pre_hook.sh')) { |
| 2771 | + symlink(dirname(getcwd()) . '/server/scripts/letsencrypt_pre_hook.sh', '/usr/local/bin/letsencrypt_pre_hook.sh'); |
| 2772 | + } |
| 2773 | + if(file_exists(dirname(getcwd()) . '/server/scripts/letsencrypt_post_hook.sh')) { |
| 2774 | + symlink(dirname(getcwd()) . '/server/scripts/letsencrypt_post_hook.sh', '/usr/local/bin/letsencrypt_post_hook.sh'); |
| 2775 | + } |
| 2776 | + if(file_exists(dirname(getcwd()) . '/server/scripts/letsencrypt_renew_hook.sh')) { |
| 2777 | + symlink(dirname(getcwd()) . '/server/scripts/letsencrypt_renew_hook.sh', '/usr/local/bin/letsencrypt_renew_hook.sh'); |
| 2778 | + } |
| 2779 | + chown('/usr/local/bin/letsencrypt_pre_hook.sh', 'root'); |
| 2780 | + chown('/usr/local/bin/letsencrypt_post_hook.sh', 'root'); |
| 2781 | + chown('/usr/local/bin/letsencrypt_renew_hook.sh', 'root'); |
| 2782 | + chmod('/usr/local/bin/letsencrypt_pre_hook.sh', 0700); |
| 2783 | + chmod('/usr/local/bin/letsencrypt_post_hook.sh', 0700); |
| 2784 | + chmod('/usr/local/bin/letsencrypt_renew_hook.sh', 0700); |
2695 | 2785 |
|
2696 | | - $ssl_crt_file = $install_dir.'/interface/ssl/ispserver.crt'; |
2697 | | - $ssl_csr_file = $install_dir.'/interface/ssl/ispserver.csr'; |
2698 | | - $ssl_key_file = $install_dir.'/interface/ssl/ispserver.key'; |
| 2786 | + // Check http port 80 status as it cannot be determined at post hook stage |
| 2787 | + $port80_status=exec('true &>/dev/null </dev/tcp/127.0.0.1/80 && echo open || echo close'); |
| 2788 | + |
| 2789 | + // Set pre-, post- and renew hook |
| 2790 | + $pre_hook = "--pre-hook \"letsencrypt_pre_hook.sh\""; |
| 2791 | + $renew_hook = " --renew-hook \"letsencrypt_renew_hook.sh\""; |
| 2792 | + if($port80_status == 'close') { |
| 2793 | + $post_hook = " --post-hook \"letsencrypt_post_hook.sh\""; |
| 2794 | + $hook = $pre_hook . $post_hook . $renew_hook; |
| 2795 | + } else { |
| 2796 | + $hook = $pre_hook . $renew_hook; |
| 2797 | + } |
| 2798 | + |
| 2799 | + // Get the default LE client name and version |
| 2800 | + $le_client = explode("\n", shell_exec('which letsencrypt certbot /root/.local/share/letsencrypt/bin/letsencrypt /opt/eff.org/certbot/venv/bin/certbot')); |
| 2801 | + $le_client = reset($le_client); |
| 2802 | + |
| 2803 | + // Check for Neilpang acme.sh as well |
| 2804 | + $acme = explode("\n", shell_exec('which /usr/local/ispconfig/server/scripts/acme.sh /root/.acme.sh/acme.sh')); |
| 2805 | + $acme = reset($acme); |
| 2806 | + |
| 2807 | + // Attempt to use Neilpang acme.sh first, as it is now the preferred LE client |
| 2808 | + if (is_executable($acme)) { |
| 2809 | + |
| 2810 | + if($conf['nginx']['installed'] == true) { |
| 2811 | + exec("$acme --issue --nginx -d $hostname $renew_hook"); |
| 2812 | + } elseif($conf['apache']['installed'] == true) { |
| 2813 | + exec("$acme --issue --apache -d $hostname $renew_hook"); |
| 2814 | + } |
| 2815 | + // Else, it is not webserver, so we use standalone |
| 2816 | + else { |
| 2817 | + exec("$acme --issue --standalone -d $hostname $hook"); |
| 2818 | + } |
2699 | 2819 |
|
2700 | | - if(!@is_dir($install_dir.'/interface/ssl')) mkdir($install_dir.'/interface/ssl', 0755, true); |
| 2820 | + // Define LE certs name and path, then install them |
| 2821 | + if (!@is_dir($le_live_dir)) mkdir($le_live_dir, 0755, true); |
| 2822 | + $acme_cert = "--cert-file $le_live_dir/cert.pem"; |
| 2823 | + $acme_key = "--key-file $le_live_dir/privkey.pem"; |
| 2824 | + $acme_ca = "--ca-file $le_live_dir/chain.pem"; |
| 2825 | + $acme_chain = "--fullchain-file $le_live_dir/fullchain.pem"; |
| 2826 | + exec("$acme --install-cert -d $hostname $acme_cert $acme_key $acme_ca $acme_chain"); |
| 2827 | + |
| 2828 | + // Else, we attempt to use the official LE certbot client certbot |
| 2829 | + } else { |
| 2830 | + |
| 2831 | + // But only if it is otherwise available |
| 2832 | + if(is_executable($le_client)) { |
| 2833 | + |
| 2834 | + // Get its version info due to be used for webroot arguement issues |
| 2835 | + $le_info = exec($le_client . ' --version 2>&1', $ret, $val); |
| 2836 | + if(preg_match('/^(\S+|\w+)\s+(\d+(\.\d+)+)$/', $le_info, $matches)) { |
| 2837 | + $le_version = $matches[2]; |
| 2838 | + } |
| 2839 | + |
| 2840 | + // Define certbot commands |
| 2841 | + $acme_version = '--server https://acme-v0' . (($le_version >=0.22) ? '2' : '1') . '.api.letsencrypt.org/directory'; |
| 2842 | + $certonly = 'certonly --agree-tos --non-interactive --expand --rsa-key-size 4096'; |
| 2843 | + |
| 2844 | + // If this is a webserver |
| 2845 | + if($conf['nginx']['installed'] == true) |
| 2846 | + exec("$le_client $certonly $acme_version --nginx --email postmaster@$hostname $renew_hook"); |
| 2847 | + elseif($conf['apache']['installed'] == true) |
| 2848 | + exec("$le_client $certonly $acme_version --apache --email postmaster@$hostname $renew_hook"); |
| 2849 | + // Else, it is not webserver, so we use standalone |
| 2850 | + else |
| 2851 | + exec("$le_client $certonly $acme_version --standalone --email postmaster@$hostname -d $hostname $hook"); |
| 2852 | + } |
| 2853 | + } |
| 2854 | + } |
| 2855 | + |
| 2856 | + //* Define and check ISPConfig SSL folder */ |
| 2857 | + $ssl_dir = $conf['ispconfig_install_dir'].'/interface/ssl'; |
| 2858 | + if(!@is_dir($ssl_dir)) mkdir($ssl_dir, 0755, true); |
| 2859 | + |
| 2860 | + $ssl_crt_file = $ssl_dir.'/ispserver.crt'; |
| 2861 | + $ssl_csr_file = $ssl_dir.'/ispserver.csr'; |
| 2862 | + $ssl_key_file = $ssl_dir.'/ispserver.key'; |
| 2863 | + $ssl_pem_file = $ssl_dir.'/ispserver.pem'; |
| 2864 | + |
| 2865 | + $date = new DateTime(); |
| 2866 | + |
| 2867 | + // If the LE SSL certs for this hostname exists |
| 2868 | + if (is_dir($le_live_dir) && in_array($svr_ip, $dns_ips)) { |
| 2869 | + |
| 2870 | + // Backup existing ispserver ssl files |
| 2871 | + if (file_exists($ssl_crt_file)) rename($ssl_crt_file, $ssl_crt_file . '-' .$date->format('YmdHis') . '.bak'); |
| 2872 | + if (file_exists($ssl_key_file)) rename($ssl_key_file, $ssl_key_file . '-' .$date->format('YmdHis') . '.bak'); |
| 2873 | + if (file_exists($ssl_pem_file)) rename($ssl_pem_file, $ssl_pem_file . '-' .$date->format('YmdHis') . '.bak'); |
| 2874 | + |
| 2875 | + // Create symlink to LE fullchain and key for ISPConfig |
| 2876 | + symlink($le_live_dir.'/fullchain.pem', $ssl_crt_file); |
| 2877 | + symlink($le_live_dir.'/privkey.pem', $ssl_key_file); |
2701 | 2878 |
|
2702 | | - $ssl_pw = substr(md5(mt_rand()), 0, 6); |
2703 | | - exec("openssl genrsa -des3 -passout pass:$ssl_pw -out $ssl_key_file 4096"); |
2704 | | - if(AUTOINSTALL){ |
2705 | | - exec("openssl req -new -passin pass:$ssl_pw -passout pass:$ssl_pw -subj '/C=".escapeshellcmd($autoinstall['ssl_cert_country'])."/ST=".escapeshellcmd($autoinstall['ssl_cert_state'])."/L=".escapeshellcmd($autoinstall['ssl_cert_locality'])."/O=".escapeshellcmd($autoinstall['ssl_cert_organisation'])."/OU=".escapeshellcmd($autoinstall['ssl_cert_organisation_unit'])."/CN=".escapeshellcmd($autoinstall['ssl_cert_common_name'])."' -key $ssl_key_file -out $ssl_csr_file"); |
2706 | 2879 | } else { |
2707 | | - exec("openssl req -new -passin pass:$ssl_pw -passout pass:$ssl_pw -key $ssl_key_file -out $ssl_csr_file"); |
| 2880 | + |
| 2881 | + // We can still use the old self-signed method |
| 2882 | + $ssl_pw = substr(md5(mt_rand()), 0, 6); |
| 2883 | + exec("openssl genrsa -des3 -passout pass:$ssl_pw -out $ssl_key_file 4096"); |
| 2884 | + if(AUTOINSTALL){ |
| 2885 | + exec("openssl req -new -passin pass:$ssl_pw -passout pass:$ssl_pw -subj '/C=".escapeshellcmd($autoinstall['ssl_cert_country'])."/ST=".escapeshellcmd($autoinstall['ssl_cert_state'])."/L=".escapeshellcmd($autoinstall['ssl_cert_locality'])."/O=".escapeshellcmd($autoinstall['ssl_cert_organisation'])."/OU=".escapeshellcmd($autoinstall['ssl_cert_organisation_unit'])."/CN=".escapeshellcmd($autoinstall['ssl_cert_common_name'])."' -key $ssl_key_file -out $ssl_csr_file"); |
| 2886 | + } else { |
| 2887 | + exec("openssl req -new -passin pass:$ssl_pw -passout pass:$ssl_pw -key $ssl_key_file -out $ssl_csr_file"); |
| 2888 | + } |
| 2889 | + exec("openssl req -x509 -passin pass:$ssl_pw -passout pass:$ssl_pw -key $ssl_key_file -in $ssl_csr_file -out $ssl_crt_file -days 3650"); |
| 2890 | + exec("openssl rsa -passin pass:$ssl_pw -in $ssl_key_file -out $ssl_key_file.insecure"); |
| 2891 | + rename($ssl_key_file, $ssl_key_file.'.secure'); |
| 2892 | + rename($ssl_key_file.'.insecure', $ssl_key_file); |
2708 | 2893 | } |
2709 | | - exec("openssl req -x509 -passin pass:$ssl_pw -passout pass:$ssl_pw -key $ssl_key_file -in $ssl_csr_file -out $ssl_crt_file -days 3650"); |
2710 | | - exec("openssl rsa -passin pass:$ssl_pw -in $ssl_key_file -out $ssl_key_file.insecure"); |
2711 | | - rename($ssl_key_file, $ssl_key_file.'.secure'); |
2712 | | - rename($ssl_key_file.'.insecure', $ssl_key_file); |
2713 | 2894 |
|
2714 | | - exec('chown -R root:root /usr/local/ispconfig/interface/ssl'); |
| 2895 | + // Build ispserver.pem file and chmod it |
| 2896 | + exec("cat $ssl_key_file $ssl_crt_file > $ssl_pem_file; chmod 600 $ssl_pem_file"); |
| 2897 | + |
| 2898 | + // Extend LE SSL certs to postfix |
| 2899 | + if ($conf['postfix']['installed'] == true && strtolower($this->simple_query('Symlink ISPConfig LE SSL certs to postfix?', array('y', 'n'), 'y')) == 'y') { |
| 2900 | + |
| 2901 | + // Define folder, file(s) |
| 2902 | + $cf = $conf['postfix']; |
| 2903 | + $postfix_dir = $cf['config_dir']; |
| 2904 | + if(!is_dir($postfix_dir)) $this->error("The postfix configuration directory '$postfix_dir' does not exist."); |
| 2905 | + $smtpd_crt = $postfix_dir.'/smtpd.cert'; |
| 2906 | + $smtpd_key = $postfix_dir.'/smtpd.key'; |
| 2907 | + |
| 2908 | + // Backup existing postfix ssl files |
| 2909 | + if (file_exists($smtpd_crt)) rename($smtpd_crt, $smtpd_crt . '-' .$date->format('YmdHis') . '.bak'); |
| 2910 | + if (file_exists($smtpd_key)) rename($smtpd_key, $smtpd_key . '-' .$date->format('YmdHis') . '.bak'); |
| 2911 | + |
| 2912 | + // Create symlink to ISPConfig SSL files |
| 2913 | + symlink($ssl_crt_file, $smtpd_crt); |
| 2914 | + symlink($ssl_key_file, $smtpd_key); |
| 2915 | + } |
| 2916 | + |
| 2917 | + // Extend LE SSL certs to pureftpd |
| 2918 | + if ($conf['pureftpd']['installed'] == true && strtolower($this->simple_query('Symlink ISPConfig LE SSL certs to pureftpd? Creating dhparam file takes some times.', array('y', 'n'), 'y')) == 'y') { |
| 2919 | + |
| 2920 | + // Define folder, file(s) |
| 2921 | + $pureftpd_dir = '/etc/ssl/private'; |
| 2922 | + if(!is_dir($pureftpd_dir)) mkdir($pureftpd_dir, 0755, true); |
| 2923 | + $pureftpd_pem = $pureftpd_dir.'/pure-ftpd.pem'; |
| 2924 | + |
| 2925 | + // Backup existing pureftpd ssl files |
| 2926 | + if (file_exists($pureftpd_pem)) rename($pureftpd_pem, $pureftpd_pem . '-' .$date->format('YmdHis') . '.bak'); |
| 2927 | + |
| 2928 | + // Create symlink to ISPConfig SSL files |
| 2929 | + symlink($ssl_pem_file, $pureftpd_pem); |
| 2930 | + if (!file_exists("$pureftpd_dir/pure-ftpd-dhparams.pem")) |
| 2931 | + exec("cd $pureftpd_dir; openssl dhparam -out dhparam2048.pem 2048; ln -sf dhparam2048.pem pure-ftpd-dhparams.pem"); |
| 2932 | + } |
| 2933 | + |
| 2934 | + exec("chown -R root:root $ssl_dir"); |
2715 | 2935 |
|
2716 | 2936 | } |
2717 | 2937 |
|
@@ -3134,6 +3354,20 @@ public function install_ispconfig() { |
3134 | 3354 | if(!is_link('/usr/local/bin/ispconfig_update_from_dev.sh')) symlink($install_dir.'/server/scripts/ispconfig_update.sh', '/usr/local/bin/ispconfig_update_from_dev.sh'); |
3135 | 3355 | if(!is_link('/usr/local/bin/ispconfig_update.sh')) symlink($install_dir.'/server/scripts/ispconfig_update.sh', '/usr/local/bin/ispconfig_update.sh'); |
3136 | 3356 |
|
| 3357 | + // Make executable then unlink and symlink letsencrypt pre, post and renew hook scripts |
| 3358 | + chown($install_dir.'/server/scripts/letsencrypt_pre_hook.sh', 'root'); |
| 3359 | + chown($install_dir.'/server/scripts/letsencrypt_post_hook.sh', 'root'); |
| 3360 | + chown($install_dir.'/server/scripts/letsencrypt_renew_hook.sh', 'root'); |
| 3361 | + chmod($install_dir.'/server/scripts/letsencrypt_pre_hook.sh', 0700); |
| 3362 | + chmod($install_dir.'/server/scripts/letsencrypt_post_hook.sh', 0700); |
| 3363 | + chmod($install_dir.'/server/scripts/letsencrypt_renew_hook.sh', 0700); |
| 3364 | + if(is_link('/usr/local/bin/letsencrypt_pre_hook.sh')) unlink('/usr/local/bin/letsencrypt_pre_hook.sh'); |
| 3365 | + if(is_link('/usr/local/bin/letsencrypt_post_hook.sh')) unlink('/usr/local/bin/letsencrypt_post_hook.sh'); |
| 3366 | + if(is_link('/usr/local/bin/letsencrypt_renew_hook.sh')) unlink('/usr/local/bin/letsencrypt_renew_hook.sh'); |
| 3367 | + symlink($install_dir.'/server/scripts/letsencrypt_pre_hook.sh', '/usr/local/bin/letsencrypt_pre_hook.sh'); |
| 3368 | + symlink($install_dir.'/server/scripts/letsencrypt_post_hook.sh', '/usr/local/bin/letsencrypt_post_hook.sh'); |
| 3369 | + symlink($install_dir.'/server/scripts/letsencrypt_renew_hook.sh', '/usr/local/bin/letsencrypt_renew_hook.sh'); |
| 3370 | + |
3137 | 3371 | //* Make the logs readable for the ispconfig user |
3138 | 3372 | if(@is_file('/var/log/mail.log')) exec('chmod +r /var/log/mail.log'); |
3139 | 3373 | if(@is_file('/var/log/mail.warn')) exec('chmod +r /var/log/mail.warn'); |
@@ -3474,5 +3708,3 @@ protected function insert_db_credentials($tContents) { |
3474 | 3708 | } |
3475 | 3709 |
|
3476 | 3710 | } |
3477 | | - |
3478 | | -?> |
|
0 commit comments