many fixed: proper searchBestCertificate() and selfSigned() in m_ssl, + new templates and generator

This commit is contained in:
Benjamin Sonntag 2015-02-12 21:17:59 +01:00
parent f7d8c4fe72
commit eab44df9db
12 changed files with 304 additions and 73 deletions

View File

@ -6,15 +6,25 @@
function convert {
src=$1
dst=$2
(cat ../etc/alternc/templates/apache2/url.conf | sed -e 's#%%redirect%%#https://%%fqdn%%#'
cat $src |
sed -e 's#:80#:443#' \
-e "s#</VirtualHost># SSLEngine On\n SSLCertificateFile %%CRT%%\n SSLCertificateKeyFile %%KEY%%\n %%CHAINLINE%%\n\n</VirtualHost>#i" \
>$dst
) >$dst
}
for template in panel url vhost
do
convert "../etc/alternc/templates/apache2/${template}.conf" "templates/${template}-ssl.conf"
done
# Those 3 are redirects from http://%%fqdn%% to https://%%fqdn%% PLUS the https://%%fqdn%% VHOST
convert "../roundcube/templates/apache2/roundcube.conf" "templates/roundcube-ssl.conf"
convert "../squirrelmail/templates/apache2/squirrelmail.conf" "templates/squirrelmail-ssl.conf"
convert "../etc/alternc/templates/apache2/panel.conf" "templates/panel-ssl.conf"
convert "../etc/alternc/templates/apache2/vhost.conf" "templates/vhost-ssl.conf"
# manual case : BOTH http and https are normal vhosts pointing to the same DocumentRoot
(cat ../etc/alternc/templates/apache2/vhost.conf
cat ../etc/alternc/templates/apache2/vhost.conf |
sed -e 's#:80#:443#' \
-e "s#</VirtualHost># SSLEngine On\n SSLCertificateFile %%CRT%%\n SSLCertificateKeyFile %%KEY%%\n %%CHAINLINE%%\n\n</VirtualHost>#i"
) >templates/vhost-mixssl.conf

View File

@ -2,9 +2,10 @@
<?php
/*
function called as a hook during alternc update_domains.sh as follow:
(launched by functions_hosting.sh in launch_hook() shell function)
create a host: launch_hooks "create" "$1" "$2" "$3" "$4" (type domain mail value)
at the end of host creation: launch_hooks "postinst" "$1" "$2" "$3" "$4"
enable or disable a sot: launch_hooks "enable|disable" "$1" "$2" "$3" (type domain value)
enable or disable a host: launch_hooks "enable|disable" "$1" "$2" "$3" (type domain value)
at host deletion: launch_hooks "delete" "$1" "$2" "$3" "$4" (type fqdn)
also, after reloading apache :
@ -16,8 +17,33 @@
before reloading a zone : run-parts --arg=dns_reload_zone --arg="$domain" /usr/lib/alternc/reload.d
*/
// Bootstraps
// Bootstrap
require_once("/usr/share/alternc/panel/class/config_nochk.php");
if (!isset($argv[1])) {
echo "FATAL: must be launched from functions_hosting.sh !\n";
exit();
}
if ( ($argv[1]=="create" || $argv[1]=="postinst" || $argv[1]=="delete") ) {
if (count($argv)<5) {
echo "FATAL: create/postinst/delete need 4 parameters: type domain mail value\n";
print_r($argv);
exit();
}
$ssl->update_domain($argv[1], $argv[2], $argv[3], $argv[4]);
exit();
}
if ( ($argv[1]=="enable" || $argv[1]=="disable") ) {
if (count($argv)<4) {
echo "FATAL: enable/disable need 3 parameters: type domain value\n";
print_r($argv);
exit();
}
$ssl->update_domain($argv[1], $argv[2], $argv[3] );
exit();
}
echo "FATAL: action unknown, must be launched from functions_hosting.sh !\n";
print_r($argv);
exit();

View File

@ -44,6 +44,10 @@ class m_ssl {
const FILTER_SHARED = 8;
const SSL_INCRON_FILE = "/var/run/alternc/ssl/generate_certif_alias";
var $myDomainesTypes = array("vhost-ssl", "url-ssl", "panel-ssl", "roundcube-ssl", "squirrelmail-ssl");
const KEY_REPOSITORY = "/var/lib/alternc/ssl/private/";
// -----------------------------------------------------------------
/**
* Constructor
@ -370,6 +374,97 @@ class m_ssl {
return $q;
}
// -----------------------------------------------------------------
/** Launched by hosting_functions.sh launched by update_domaines.sh
* Action may be create/postinst/delete/enable/disable
* Change the template for this domain name to have the proper CERTIFICATE
* An algorithm determine the best possible certificate, which may be a BAD one
* (like a generic admin-shared or self-signed for localhost as a last chance)
*/
public function update_domain($action, $type, $fqdn, $mail = 0, $value = "") {
global $db, $err;
$err->log("ssl", "update_domain($action,$type,$fqdn)");
if (!in_array($type, $this->myDomainesTypes)) {
return; // nothing to do : the type is not our to start with ;)
}
if ($action == "create") {
$err->log("ssl", "update_domain:CREATE($action,$type,$fqdn)");
$offset = 0;
$found = false;
do { // try each subdomain (strtok-style) and search them in sub_domaines table:
$db->query("SELECT * FROM sub_domaines WHERE "
. "sub='" . substr($fqdn, 0, $offset) . "' AND domaine='" . substr($fqdn, $offset + ($offset != 0)) . "' "
. "AND web_action NOT IN ('','OK') AND type='" . $type . "';");
if ($db->next_record()) {
$found = true;
break;
}
$offset = strpos($fqdn, ".", $offset);
} while (true);
if (!$found) {
echo "FATAL: didn't found fqdn $fqdn in sub_domaines table !\n";
return;
}
// found and $db point to it:
$subdom = $db->Record;
$TARGET_FILE = "/var/lib/alternc/apache-vhost/" . substr($subdom["compte"], -1) . "/" . $subdom["compte"] . "/" . $fqdn . ".conf";
// DEBUG
echo "TARGET_FILE:".$TARGET_FILE."\n";
$cert = $this->searchBestCert($subdom["compte"], $fqdn);
// DEBUG echo "Return from searchBestCert(" . $subdom["compte"] . "," . $fqdn . ") is "; print_r($cert);
// Save crt/key/chain into KEY_REPOSITORY
$CRTDIR = self::KEY_REPOSITORY . "/" . $subdom["compte"];
@mkdir($CRTDIR);
file_put_contents($CRTDIR . "/" . $fqdn . ".crt", $cert["sslcrt"]);
file_put_contents($CRTDIR . "/" . $fqdn . ".key", $cert["sslkey"]);
if (isset($cert["sslchain"]) && $cert["sslchain"]) {
file_put_contents($CRTDIR . "/" . $fqdn . ".chain", $cert["sslchain"]);
}
// edit apache conf file to set the certificate:
$s = file_get_contents($TARGET_FILE);
$s = str_replace("%%CRT%%", $CRTDIR . "/" . $fqdn . ".crt", $s);
$s = str_replace("%%KEY%%", $CRTDIR . "/" . $fqdn . ".key", $s);
if (isset($cert["sslchain"]) && $cert["sslchain"]) {
$s = str_replace("%%CHAINLINE%%", "SSLCertificateChainFile " . $CRTDIR . "/" . $fqdn . ".crt", $s);
} else {
$s = str_replace("%%CHAINLINE%%", "", $s);
}
file_put_contents($TARGET_FILE, $s);
} // action==create
}
// --------------op---------------------------------------------------
/** Search for the best certificate for a user and a fqdn
* Return a hash with sslcrt, sslkey and maybe sslchain.
* return ANYWAY : if necessary, return a newly created (and stored in KEY_REPOSITORY localhost self-signed certificate...
*/
public function searchBestCert($uid, $fqdn) {
global $db;
$uid = intval($uid);
// 1st search for a valid certificate in my account or shared by the admin:
// the ORDER BY make it so that we try VALID then EXPIRED one (sad)
$wildcard = "*" . substr($fqdn, strpos($fqdn, ".") + 1);
$db->query("SELECT * FROM certificates WHERE status=1 "
. "AND (uid=" . $uid . " OR shared=1) "
. "AND (fqdn='" . $fqdn . "' OR fqdn='" . $wildcard . "' OR altnames LIKE '%" . $fqdn . "%') "
. "ORDER BY (validstart<=NOW() AND validend>=NOW()) DESC, validstart DESC ");
while ($db->next_record()) {
if ($db->fqdn == $fqdn) {
return $db->Record;
}
$altnames = explode("\n", $db->Record["altnames"]);
foreach ($altnames as $altname) {
if (trim($altname) == $fqdn) {
return $db->Record;
}
}
}
// not found, we generate a one-time self-signed certificate for this host.
$crt = $this->selfSigned($fqdn);
$crt["uid"] = $uid;
return $crt;
}
// -----------------------------------------------------------------
/** Export every information for an AlternC's account
* @access private
@ -565,7 +660,40 @@ class m_ssl {
return array($crt, $chain, $key, $crtdata);
}
// check_cert
// -----------------------------------------------------------------
/** Generate a self-signed certificate
*
* @param string $fqdn the fully qualified domain name to set as commonName for the certificate
* @return hash an array similar to a certificate DB row containing everything (sslcrt, sslcsr, sslkey, sslchain)
*/
private function selfSigned($fqdn) {
global $err;
putenv("OPENSSL_CONF=/etc/alternc/openssl.cnf");
$pkey = openssl_pkey_new();
if (!$pkey) {
$err->raise("ssl", _("Can't generate a private key (1)"));
return false;
}
$privKey = "";
if (!openssl_pkey_export($pkey, $privKey)) {
$err->raise("ssl", _("Can't generate a private key (2)"));
return false;
}
$dn = array("commonName" => $fqdn);
// override the (not taken from openssl.cnf) digest to use SHA-2 / SHA256 and not SHA-1 or MD5 :
$config = array("digest_alg" => "sha256");
$csr = openssl_csr_new($dn, $pkey, $config);
$csrout = "";
openssl_csr_export($csr, $csrout);
$crt = openssl_csr_sign($csr, null, $pkey, 3650, $config);
$crtout = "";
openssl_x509_export($crt, $crtout);
return array("id" => 0, "status" => 1, "shared" => 0, "fqdn" => $fqdn, "altnames" => "",
"validstart" => date("Y-m-d H:i:s"), "validend" => date("Y-m-d H:i:s", time() + 86400 * 10 * 365.249),
"sslcsr" => $csrout, "sslcrt" => $crtout, "sslkey" => $privKey, "sslchain" => ""
);
}
}
/* Class m_ssl */

View File

@ -29,9 +29,14 @@ CREATE TABLE IF NOT EXISTS `certif_alias` (
KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Global aliases defined for SSL certificates FILE validation processes';
INSERT IGNORE INTO `domaines_type` (name ,description ,target ,entry ,compatibility ,enable ,only_dns ,need_dns ,advanced ) VALUES ('vhost-ssl','Locally hosted WITH SSL','DIRECTORY','%SUB% IN A @@PUBLIC_IP@@','vhost,url,txt,defmx,defmx2,mx,mx2','ALL',0,0,0,1,1);
INSERT IGNORE INTO `domaines_type` (name ,description ,target ,entry ,compatibility ,enable ,only_dns ,need_dns ,advanced ) VALUES ('url-ssl','URL redirection WITH SSL','URL','%SUB% IN A @@PUBLIC_IP@@','vhost,url,txt,defmx,defmx2','ALL',0,0,0,0,0);
INSERT IGNORE INTO `domaines_type` (name ,description ,target ,entry ,compatibility ,enable ,only_dns ,need_dns ,advanced ) VALUES ('panel-ssl','AlternC panel access WITH SSL','NONE','%SUB% IN A @@PUBLIC_IP@@','panel,ip,ipv6,cname,txt,mx,mx2,defmx,defmx2','ALL',0,0,1,0,0);
INSERT IGNORE INTO `domaines_type` (name ,description ,target ,entry ,compatibility ,enable ,only_dns ,need_dns ,advanced ) VALUES ('roundcube-ssl','Roundcube Webmail access WITH SSL', 'NONE', '%SUB% IN A @@PUBLIC_IP@@', 'mx,mx2,defmx,defmx2,roundcube,txt', 'ALL', '0', '0', '0');
INSERT IGNORE INTO `domaines_type` (name ,description ,target ,entry ,compatibility ,enable ,only_dns ,need_dns ,advanced ) VALUES ('squirrelmail-ssl','Squirrelmail Webmail access WITH SSL', 'NONE', '%SUB% IN A @@PUBLIC_IP@@', 'mx,mx2,defmx,defmx2,squirrelmail,txt', 'ALL', '0', '0', '0');
INSERT IGNORE INTO `domaines_type` (name ,description ,target ,entry ,compatibility ,enable ,only_dns ,need_dns ,advanced ) VALUES
('vhost-ssl','Locally hosted forcing HTTPS','DIRECTORY','%SUB% IN A @@PUBLIC_IP@@','vhost,url,txt,defmx,defmx2,mx,mx2','ALL',0,0,0);
INSERT IGNORE INTO `domaines_type` (name ,description ,target ,entry ,compatibility ,enable ,only_dns ,need_dns ,advanced ) VALUES
('vhost-mixssl','Locally hosted HTTP and HTTPS','DIRECTORY','%SUB% IN A @@PUBLIC_IP@@','vhost,url,txt,defmx,defmx2,mx,mx2','ALL',0,0,0);
INSERT IGNORE INTO `domaines_type` (name ,description ,target ,entry ,compatibility ,enable ,only_dns ,need_dns ,advanced ) VALUES
('panel-ssl','AlternC panel access WITH SSL','NONE','%SUB% IN A @@PUBLIC_IP@@','ip,ipv6,cname,txt,mx,mx2,defmx,defmx2','ALL',0,0,1);
INSERT IGNORE INTO `domaines_type` (name ,description ,target ,entry ,compatibility ,enable ,only_dns ,need_dns ,advanced ) VALUES
('roundcube-ssl','Roundcube Webmail access WITH SSL', 'NONE', '%SUB% IN A @@PUBLIC_IP@@', 'mx,mx2,defmx,defmx2,txt', 'ALL',0,0,1;
INSERT IGNORE INTO `domaines_type` (name ,description ,target ,entry ,compatibility ,enable ,only_dns ,need_dns ,advanced ) VALUES
('squirrelmail-ssl','Squirrelmail Webmail access WITH SSL', 'NONE', '%SUB% IN A @@PUBLIC_IP@@', 'mx,mx2,defmx,defmx2,txt','ALL',0,0,1);

0
ssl/ssl_alias_manager.sh Normal file → Executable file
View File

View File

@ -1,3 +1,15 @@
<Virtualhost *:80>
ServerName %%fqdn%%
AssignUserId #%%UID%% #%%GID%%
SetEnv LOGIN "%%UID%%-%%LOGIN%%"
KeepAlive Off
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !/cgi-bin/
RewriteRule ^/(.*)$ https://%%fqdn%%/$1 [R=301,L]
</Virtualhost>
<VirtualHost *:443>
DocumentRoot /usr/share/alternc/panel/admin
AssignUserId alterncpanel alterncpanel

View File

@ -1,3 +1,15 @@
<Virtualhost *:80>
ServerName %%fqdn%%
AssignUserId #%%UID%% #%%GID%%
SetEnv LOGIN "%%UID%%-%%LOGIN%%"
KeepAlive Off
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !/cgi-bin/
RewriteRule ^/(.*)$ https://%%fqdn%%/$1 [R=301,L]
</Virtualhost>
<VirtualHost *:443>
ServerName %%fqdn%%
AssignUserId www-data www-data

View File

@ -1,3 +1,15 @@
<Virtualhost *:80>
ServerName %%fqdn%%
AssignUserId #%%UID%% #%%GID%%
SetEnv LOGIN "%%UID%%-%%LOGIN%%"
KeepAlive Off
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !/cgi-bin/
RewriteRule ^/(.*)$ https://%%fqdn%%/$1 [R=301,L]
</Virtualhost>
<VirtualHost *:443>
ServerName %%fqdn%%
AssignUserId alternc-squirrelmail nogroup

View File

@ -1,17 +0,0 @@
<Virtualhost *:443>
ServerName %%fqdn%%
AssignUserId #%%UID%% #%%GID%%
SetEnv LOGIN "%%UID%%-%%LOGIN%%"
KeepAlive Off
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !/cgi-bin/
RewriteRule ^/(.*)$ %%redirect%%/$1 [R=301,L]
SSLEngine On
SSLCertificateFile %%CRT%%
SSLCertificateKeyFile %%KEY%%
%%CHAINLINE%%
</VirtualHost>

View File

@ -0,0 +1,39 @@
<VirtualHost *:80>
ServerName %%fqdn%%
DocumentRoot "%%document_root%%"
AssignUserId #%%UID%% #%%GID%%
SetEnv LOGIN "%%UID%%-%%LOGIN%%"
<Directory "%%document_root%%">
php_admin_value open_basedir "%%account_root%%:/usr/share/php/"
php_admin_value upload_tmp_dir %%account_root%%/tmp
php_admin_value sendmail_path '/usr/lib/alternc/sendmail "%%mail_account%%" '
php_admin_flag mail.add_x_header on
Options +MultiViews -FollowSymLinks +SymLinksIfOwnerMatch
AllowOverride AuthConfig FileInfo Limit Options Indexes
</Directory>
</VirtualHost>
<VirtualHost *:443>
ServerName %%fqdn%%
DocumentRoot "%%document_root%%"
AssignUserId #%%UID%% #%%GID%%
SetEnv LOGIN "%%UID%%-%%LOGIN%%"
<Directory "%%document_root%%">
php_admin_value open_basedir "%%account_root%%:/usr/share/php/"
php_admin_value upload_tmp_dir %%account_root%%/tmp
php_admin_value sendmail_path '/usr/lib/alternc/sendmail "%%mail_account%%" '
php_admin_flag mail.add_x_header on
Options +MultiViews -FollowSymLinks +SymLinksIfOwnerMatch
AllowOverride AuthConfig FileInfo Limit Options Indexes
</Directory>
SSLEngine On
SSLCertificateFile %%CRT%%
SSLCertificateKeyFile %%KEY%%
%%CHAINLINE%%
</VirtualHost>

View File

@ -1,3 +1,15 @@
<Virtualhost *:80>
ServerName %%fqdn%%
AssignUserId #%%UID%% #%%GID%%
SetEnv LOGIN "%%UID%%-%%LOGIN%%"
KeepAlive Off
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !/cgi-bin/
RewriteRule ^/(.*)$ https://%%fqdn%%/$1 [R=301,L]
</Virtualhost>
<VirtualHost *:443>
ServerName %%fqdn%%
DocumentRoot "%%document_root%%"

View File

@ -1,8 +0,0 @@
#!/usr/bin/php
<?php
/* This script is launched as root through a cron to update automagically new certificates
and deploy them if required
*/