Support de dkim et de autoconfig/autodiscover

Reste la retrocompatibilité des hooks a faire
This commit is contained in:
Alan Garcia 2014-02-04 16:14:01 +00:00
parent 6726806d38
commit 77b22d46dd
1 changed files with 167 additions and 14 deletions

View File

@ -20,7 +20,12 @@ class m_bind_regenerate {
var $NAMED_CONF ="/var/lib/alternc/bind/automatic.conf"; var $NAMED_CONF ="/var/lib/alternc/bind/automatic.conf";
var $RNDC ="/usr/sbin/rndc"; var $RNDC ="/usr/sbin/rndc";
var $dkim_trusted_host_file = "/etc/opendkim/TrustedHosts";
var $dkim_keytable_file = "/etc/opendkim/KeyTable";
var $dkim_signingtable_file = "/etc/opendkim/SigningTable";
var $cache_conf_db = array(); var $cache_conf_db = array();
var $cache_get_persistent = array();
var $cache_zone_file = array(); var $cache_zone_file = array();
var $cache_domain_summary = array(); var $cache_domain_summary = array();
var $zone_file_directory = '/var/lib/alternc/bind/zones/'; var $zone_file_directory = '/var/lib/alternc/bind/zones/';
@ -32,6 +37,7 @@ class m_bind_regenerate {
// Return the part of the conf we got from the database // Return the part of the conf we got from the database
function conf_from_db($domain=false) { function conf_from_db($domain=false) {
global $db; global $db;
// Use cache, fill cache if empty
if (empty($this->cache_conf_db)) { if (empty($this->cache_conf_db)) {
$db->query(" $db->query("
select select
@ -59,11 +65,13 @@ class m_bind_regenerate {
return $this->cache_conf_db; return $this->cache_conf_db;
} }
// Return full path of the zone configuration file
function get_zone_file_uri($domain) { function get_zone_file_uri($domain) {
return $this->zone_file_directory.$domain; return $this->zone_file_directory.$domain;
} }
function get_zone_file($domain) { function get_zone_file($domain) {
// Use cache, fill cache if empty
if (!isset($this->cache_zone_file[$domain]) ) { if (!isset($this->cache_zone_file[$domain]) ) {
if (file_exists($this->get_zone_file_uri($domain))) { if (file_exists($this->get_zone_file_uri($domain))) {
$this->cache_zone_file[$domain] = @file_get_contents($this->get_zone_file_uri($domain)); $this->cache_zone_file[$domain] = @file_get_contents($this->get_zone_file_uri($domain));
@ -89,16 +97,21 @@ class m_bind_regenerate {
$old = $output_array[1][0]; $old = $output_array[1][0];
} }
// Return max between newly calculated, and old one incremented
return max(array($calc,$old)) + 1 ; return max(array($calc,$old)) + 1 ;
} }
// Return lines that are after ;;;END ALTERNC AUTOGENERATE CONFIGURATION // Return lines that are after ;;;END ALTERNC AUTOGENERATE CONFIGURATION
function get_persistent($domain) { function get_persistent($domain) {
preg_match_all('/\;\sEND\sALTERNC\sAUTOGENERATE\sCONFIGURATION(.*)/s', $this->get_zone_file($domain), $output_array); if ( ! isset($this->cache_get_persistent[$domain] )) {
if (isset($output_array[1][0]) && !empty($output_array[1][0])) { preg_match_all('/\;\s*END\sALTERNC\sAUTOGENERATE\sCONFIGURATION(.*)/s', $this->get_zone_file($domain), $output_array);
return $output_array[1][0]; if (isset($output_array[1][0]) && !empty($output_array[1][0])) {
} $this->cache_get_persistent[$domain] = $output_array[1][0];
return; } else {
$this->cache_get_persistent[$domain] = false;
}
} // isset
return $this->cache_get_persistent[$domain];
} }
function get_zone_header($domain) { function get_zone_header($domain) {
@ -107,23 +120,144 @@ class m_bind_regenerate {
function get_domain_summary($domain=false) { function get_domain_summary($domain=false) {
global $dom; global $dom;
// Use cache if is filled, if not, fill it
if (empty($this->cache_domain_summary)) { if (empty($this->cache_domain_summary)) {
$this->cache_domain_summary = $dom->get_domain_all_summary(); $this->cache_domain_summary = $dom->get_domain_all_summary();
} }
if ($domain) return $this->cache_domain_summary[$domain]; if ($domain) return $this->cache_domain_summary[$domain];
else return $this->cache_domain_summary; else return $this->cache_domain_summary;
} }
function dkim_delete($domain) {
$target_dir = "/etc/opendkim/keys/$domain";
@unlink("$target_dir/alternc_private");
@unlink("$target_dir/alternc.txt");
@rmdir($target_dir);
return true;
}
// Generate the domain DKIM key
function dkim_generate_key($domain) {
// Stop here if we do not manage the mail
if ( ! $this->get_domain_summary($domain)['gesmx'] ) return;
$target_dir = "/etc/opendkim/keys/$domain";
if (file_exists($target_dir.'/alternc.txt')) return; // Do not generate if exist
if (! is_dir($target_dir)) mkdir($target_dir); // create dir
// Generate the key
$old_dir=getcwd();
chdir($target_dir);
exec('opendkim-genkey -r -d "'.escapeshellarg($domain).'" -s "alternc" ');
chdir($old_dir);
// opendkim must be owner of the key
chown("$target_dir/alternc.private", 'opendkim');
chgrp("$target_dir/alternc.private", 'opendkim');
return true; // FIXME handle error
}
// Refresh DKIM configuration: be sure to list the domain having a private key (and only them)
function dkim_refresh_list() { // so ugly... but there is only 1 pass, not 3. Still ugly.
$trusted_host_new = "# WARNING: this file is auto generated by AlternC.\n# Add your changes after the last line\n";
$keytable_new = "# WARNING: this file is auto generated by AlternC.\n# Add your changes after the last line\n";
$signingtable_new = "# WARNING: this file is auto generated by AlternC.\n# Add your changes after the last line\n";
# Generate automatic entry
foreach ($this->get_domain_summary() as $domain => $ds ) {
// Skip if delete in progress, or if we do not manage dns or mail
if ( ! $ds['gesdns'] || ! $ds['gesmx'] || strtoupper($ds['dns_action']) == 'DELETE' ) continue;
// Skip if there is no key generated
if (! file_exists("/etc/opendkim/keys/$domain/alternc.txt")) continue;
// Modif the files.
$trusted_host_new.="$domain\n";
$keytable_new .="alternc._domainkey.$domain $domain:alternc:/etc/opendkim/keys/$domain/alternc.private\n";
$signingtable_new.="$domain alternc._domainkey.$domain\n";
}
$trusted_host_new.="# END AUTOMATIC FILE. ADD YOUR CHANGES AFTER THIS LINE";
$keytable_new .="# END AUTOMATIC FILE. ADD YOUR CHANGES AFTER THIS LINE";
$signingtable_new.="# END AUTOMATIC FILE. ADD YOUR CHANGES AFTER THIS LINE";
# Get old files
$trusted_host_old=@file_get_contents($this->dkim_trusted_host_file);
$keytable_old =@file_get_contents($this->dkim_keytable_file);
$signingtable_old=@file_get_contents($this->dkim_signingtable_file);
# Keep manuel entry
preg_match_all('/\#\s*END\ AUTOMATIC\ FILE\.\ ADD\ YOUR\ CHANGES\ AFTER\ THIS\ LINE(.*)/s', $trusted_host_old, $output_array);
if (isset($output_array[1][0]) && !empty($output_array[1][0])) {
$trusted_host_new.=$output_array[1][0];
}
preg_match_all('/\#\s*END\ AUTOMATIC\ FILE\.\ ADD\ YOUR\ CHANGES\ AFTER\ THIS\ LINE(.*)/s', $keytable_old, $output_array);
if (isset($output_array[1][0]) && !empty($output_array[1][0])) {
$keytable_new.=$output_array[1][0];
}
preg_match_all('/\#\s*END\ AUTOMATIC\ FILE\.\ ADD\ YOUR\ CHANGES\ AFTER\ THIS\ LINE(.*)/s', $signingtable_old, $output_array);
if (isset($output_array[1][0]) && !empty($output_array[1][0])) {
$signingtable_new.=$output_array[1][0];
}
// Save if there are some diff
if ( $trusted_host_new != $trusted_host_old ) {
file_put_contents($this->dkim_trusted_host_file, $trusted_host_new);
}
if ( $keytable_new != $keytable_old ) {
file_put_contents($this->dkim_keytable_file, $keytable_new);
}
if ( $signingtable_new != $signingtable_old ) {
file_put_contents($this->dkim_signingtable_file, $signingtable_new);
}
}
function dkim_entry($domain) {
$keyfile="/etc/opendkim/keys/$domain/alternc.txt";
if (! file_exists($keyfile) && $this->get_domain_summary($domain)['gesmx'] ) {
$this->dkim_generate_key($domain);
}
return @file_get_contents($keyfile);
}
// Conditionnal generation autoconfig entry for outlook / thunderbird
// If entry with the same name allready exist, skip it.
function mail_autoconfig_entry($domain) {
$zone= implode("\n",$this->conf_from_db($domain))."\n".$this->get_persistent($domain);
$entry='';
if ( $this->get_domain_summary($domain)['gesmx'] ) {
// If we manage the mail
// Check if there is no the same entry (defined or manual)
// can be toto IN A or toto.fqdn.tld. IN A
if (! preg_match("/autoconfig(\s|\.".str_replace('.','\.',$domain)."\.)/", $zone )) {
$entry.="autoconfig IN CNAME %%fqdn%%.\n";
}
if (! preg_match("/autodiscover(\s|\.".str_replace('.','\.',$domain)."\.)/", $zone )) {
$entry.="autodiscover IN CNAME %%fqdn%%.\n";
}
} // if gesmx
return $entry;
}
// Return a fully generated zone // Return a fully generated zone
function get_zone($domain) { function get_zone($domain) {
global $L_FQDN, $L_NS1_HOSTNAME, $L_NS2_HOSTNAME, $L_DEFAULT_MX, $L_DEFAULT_SECONDARY_MX, $L_PUBLIC_IP; global $L_FQDN, $L_NS1_HOSTNAME, $L_NS2_HOSTNAME, $L_DEFAULT_MX, $L_DEFAULT_SECONDARY_MX, $L_PUBLIC_IP;
$zone=''; $zone =$this->get_zone_header($domain);
$zone.=$this->get_zone_header($domain);
$zone.=implode("\n",$this->conf_from_db($domain)); $zone.=implode("\n",$this->conf_from_db($domain));
$zone.="\n;;;HOOKED ENTRY\n"; $zone.="\n;;;HOOKED ENTRY\n";
// FIXME ADD HOOKS opendkim, autoconfig toussa....
$zone.="\n;;;END ALTERNC AUTOGENERATE CONFIGURATION\n"; $zone.= $this->dkim_entry($domain);
$zone.= $this->mail_autoconfig_entry($domain);
$zone.="\n;;;END ALTERNC AUTOGENERATE CONFIGURATION";
$zone.=$this->get_persistent($domain); $zone.=$this->get_persistent($domain);
// FIXME check those vars // FIXME check those vars
@ -161,32 +295,44 @@ class m_bind_regenerate {
} }
function save_zone($domain) { function save_zone($domain) {
global $db;
// Do not save if the zone is LOCKED
if ( $this->is_locked($domain)) return false; if ( $this->is_locked($domain)) return false;
// Save file, and apply chmod/chown
$file=$this->get_zone_file_uri($domain); $file=$this->get_zone_file_uri($domain);
file_put_contents($file, $this->get_zone($domain)); file_put_contents($file, $this->get_zone($domain));
chown($file, 'bind'); chown($file, 'bind');
chmod($file, 640); chmod($file, 640);
$db->query("UPDATE domaines SET dns_action = 'OK' WHERE domaine = '".mysql_escape_string($domain)."';");
return true; // fixme add tests return true; // fixme add tests
} }
// Delete the zone configuration file
function delete_zone($domain) { function delete_zone($domain) {
$file=$this->get_zone_file_uri($domain); $file=$this->get_zone_file_uri($domain);
if (file_exists($file)) { if (file_exists($file)) {
unlink($file); unlink($file);
} }
$this->dkim_delete($domain);
return; return;
} }
function reload_named() { function reload_named() {
// Generate the new conf file
$new_named_conf="// DO NOT EDIT\n// This file is generated by Alternc.\n// Every changes you'll make will be overwrited.\n"; $new_named_conf="// DO NOT EDIT\n// This file is generated by Alternc.\n// Every changes you'll make will be overwrited.\n";
$tpl=file_get_contents($this->NAMED_TEMPLATE); $tpl=file_get_contents($this->NAMED_TEMPLATE);
foreach ($this->get_domain_summary() as $domain => $ds ) { foreach ($this->get_domain_summary() as $domain => $ds ) {
if ( ! $ds['gesdns'] || strtoupper($ds['dns_action']) == 'DELETE' ) continue; if ( ! $ds['gesdns'] || strtoupper($ds['dns_action']) == 'DELETE' ) continue;
$new_named_conf.=strtr($tpl, array("@@DOMAINE@@"=>$domain, "@@ZONE_FILE@@"=>$this->get_zone_file_uri($domain))); $new_named_conf.=strtr($tpl, array("@@DOMAINE@@"=>$domain, "@@ZONE_FILE@@"=>$this->get_zone_file_uri($domain)));
} }
// Get the actual conf file
$old_named_conf = @file_get_contents($this->NAMED_CONF); $old_named_conf = @file_get_contents($this->NAMED_CONF);
// Apply new configuration only if there are some differences
if ($old_named_conf != $new_named_conf ) { if ($old_named_conf != $new_named_conf ) {
file_put_contents($this->NAMED_CONF,$new_named_conf); file_put_contents($this->NAMED_CONF,$new_named_conf);
chown($this->NAMED_CONF, 'bind'); chown($this->NAMED_CONF, 'bind');
@ -195,28 +341,35 @@ class m_bind_regenerate {
} }
} }
// Regenerate bind configuration and load it
function regenerate_conf($all=false) { function regenerate_conf($all=false) {
foreach ($this->get_domain_summary() as $domain => $ds ) { foreach ($this->get_domain_summary() as $domain => $ds ) {
if ( ! $ds['gesdns'] ) continue; if ( ! $ds['gesdns'] ) continue; // Skip if we do not manage DNS for this domain
if ( strtoupper($ds['dns_action']) == 'DELETE' ) {
if ( strtoupper($ds['dns_action']) == 'DELETE' ) {
$this->delete_zone($domain); $this->delete_zone($domain);
} }
if ($all || strtoupper($ds['dns_action']) == 'UPDATE' ) { if ($all || strtoupper($ds['dns_action']) == 'UPDATE' ) {
$this->save_zone($domain); $this->save_zone($domain);
$this->reload_zone($domain); $this->reload_zone($domain);
// FIXME reload zone hooks // FIXME reload zone hooks
} }
} } // end foreach domain
$this->dkim_refresh_list();
$this->reload_named(); $this->reload_named();
// FIXME reload named hooks // FIXME reload named hooks
return;
} }
} // class } // class
$bind = new m_bind_regenerate(); $bind = new m_bind_regenerate();
#echo $bind->get_zone('coin.fr'); $bind->regenerate_conf(true);
echo $bind->regenerate_conf(true); echo "Forced regeneration of DNS: done.";