Skip to content
Commits on Source (8)
......@@ -309,6 +309,7 @@ class remoting_lib extends tform_base {
$username = $params["username"];
$clear_password = $params["password"];
$language = $params['language'];
$modules = $params['modules'];
$client_id = $app->functions->intval($client_id);
if(!isset($params['_ispconfig_pw_crypted']) || $params['_ispconfig_pw_crypted'] != 1) $password = $app->auth->crypt_password(stripslashes($clear_password));
......@@ -327,8 +328,14 @@ class remoting_lib extends tform_base {
$params[] = $language;
}
$modulesstring = '';
if (!empty($modules)) {
$modulesstring = ', modules = ?';
$params[] = $modules;
}
$params[] = $client_id;
$sql = "UPDATE sys_user set username = ? $pwstring $langstring WHERE client_id = ?";
$sql = "UPDATE sys_user set username = ? $pwstring $langstring $modulesstring WHERE client_id = ?";
$app->db->query($sql, true, $params);
}
......
#!/usr/bin/php
<?php
#
# ispc-import-csv-email.php: import email accounts from csv into ispconfig
#
# ISPConfig remote api params
$remote_user = 'importer';
$remote_pass = 'apipassword';
$remote_url = 'https://yourserver.com:8080/remote/json.php';
# CSV file
$csv_file="/home/migrations/test.csv";
# csv file format (first line is header names, column order does not matter):
#
# "email","password","quota","name","cc","bcc","move_junk","autoresponder","autoresponder_text","virus_lover","spam_lover"
# "api_standard@apitest.com","insecure","150","API User Insert: Standard Mailbox","","","yes","no","this is vacation text, although vacation is not enabled","N","N"
# "api_no_spambox@apitest.com","insecure","150","API User Insert: Mailbox with move_junk off","","","no","no","this is vacation text, although vacation is not enabled","N","N"
# "api_vacation@apitest.com","insecure","150","API User Insert: Mailbox with vacation","","","yes","yes","this is vacation text, with vacation enabled","N","N"
# "api_forward@apitest.com","insecure","150","API User Insert: Mail Forward","your-test-addr@test.com","","no","no","this is vacation text, although vacation is not enabled","N","N"
# "api_both1@apitest.com","insecure","150","API User Insert: Mailbox with forward via cc","your-test-addr@test.com","","yes","no","this is vacation text, although vacation is not enabled","N","N"
# "api_both2@apitest.com","insecure","150","API User Insert: Mailbox with forward via bcc","","your-test-addr@test.com","yes","no","this is vacation text, although vacation is not enabled","N","N"
# "api_virus_lover@apitest.com","insecure","150","API User Insert: Mailbox with virus_lover","","","yes","no","","Y","N"
# "api_spam_lover@apitest.com","insecure","150","API User Insert: Mailbox with spam_lover","","","yes","no","","N","Y"
# "api_both_lover@apitest.com","insecure","150","API User Insert: Mailbox with virus_lover and spam_lover","","","yes","no","","Y","Y"
/**
* Call REST endpoint.
*/
function restCall( $method, $data ) {
global $remote_url;
if(!is_array($data)) return false;
$json = json_encode($data);
$curl = curl_init();
curl_setopt($curl, CURLOPT_POST, 1);
if($data) curl_setopt($curl, CURLOPT_POSTFIELDS, $json);
// needed for self-signed cert
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
//curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
// end of needed for self-signed cert
curl_setopt($curl, CURLOPT_URL, $remote_url . '?' . $method);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
curl_close($curl);
return $result;
}
$session_id = '';
/**
* Logout of active session and die with message.
*/
function session_die( $msg ) {
global $session_id;
if ( isset( $session_id ) && $session_id ) {
$result = restCall( 'logout', [ 'session_id' => $session_id ] );
$result || die( "$msg\nAdditionally, could not get logout result, session id $session_id may now be abandoned.\n" );
}
die( "$msg\n" );
}
/**
* Make api call, checking for errors and return 'response' from the decoded data. Opens session if required.
*/
function apiCall( ...$args ) {
global $remote_user, $remote_pass, $session_id;
// login to remote api and obtain session id if needed
if ( ! ( isset( $session_id ) && $session_id ) ) {
$result = restCall( 'login', [ 'username' => $remote_user, 'password' => $remote_pass, 'client_login' => false, ] );
if ( $result ) {
$result = json_decode( $result, true );
if ( ! $result ) {
die( "Error: unable to login to remote api (json_decode failed)\n" );
}
if ( isset( $result['response'] ) ) {
$session_id = $result['response'];
} else {
die( "Error: failed to obtain session id from remote api login\n" );
}
}
}
$rest_args = func_get_args();
$method = array_shift( $rest_args );
$result = restCall( $method, array_merge( [ 'session_id' => $session_id, ], ...$rest_args ) );
if ( $result ) $data = json_decode( $result, true );
else session_die( "Could not get $method result" );
if ( isset( $data['code'] ) && 'ok' != $data['code'] ) {
$msg = "$method returned " . $data['code']
. ( isset( $data['message'] ) ? ": " . $data['message'] . "\n" : "\n" );
session_die( $msg );
}
return ( isset( $data['response'] ) ? $data['response'] : $data );
}
if ( ! file_exists( "$csv_file" ) ) {
die( "CSV file ($csv_file) not found.\n" );
}
// get all mail policies
$mail_policies = apiCall( 'mail_policy_get', [ 'primary_id' => [] ] );
if ( ! $mail_policies ) {
session_die( "Error: could not look up mail policies\n" );
}
// get all spamfilter_user settings
$mail_spamfilter_users = apiCall( 'mail_spamfilter_user_get', [ 'primary_id' => [] ] );
if ( ! $mail_spamfilter_users ) {
session_die( "Error: could not look up mail spamfilter users\n" );
}
$mail_domains = [];
// Read csv file, map rows and loop through them
$rows = array_map( 'str_getcsv', file( $csv_file ) );
$header = array_shift( $rows );
$email_idx = array_search( 'email', $header );
if ( $email_idx === FALSE ) {
session_die( "Error in csv file: 'email' field not found.\n" );
}
$csv = [];
foreach( $rows as $row ) {
$email = $row[$email_idx];
$domain = substr( $email, strpos( $email, '@' ) + 1 );
if ( is_array( $row ) && count( $header ) == count( $row ) ) {
$csv[$email] = array_combine( $header, $row );
} else {
print "Error in csv file: problem parsing email '$email'\n";
continue;
}
// look up mail_domain record for this domain
if ( ! isset( $mail_domains[$domain] ) ) {
$data = apiCall( 'mail_domain_get_by_domain', [ 'domain' => $domain ] );
if ( is_array( $data ) && isset( $data[0] ) ) {
// unset these (large and don't need them)
unset( $data[0]['dkim'] );
unset( $data[0]['dkim_selector'] );
unset( $data[0]['dkim_public'] );
unset( $data[0]['dkim_private'] );
$mail_domains[$domain] = $data[0];
foreach ( $mail_spamfilter_users as $msu ) {
if ( $msu['email'] == "@$domain" && $msu['server_id'] == $mail_domains[$domain]['server_id'] ) {
$mail_domains[$domain]['spamfilter_policy_id'] = $msu['policy_id'];
}
}
} else {
$mail_domains[$domain] = [ 'domain_id' => -1, 'domain' => $domain, ];
print( "Error: mail_domain $domain does not exist, you must create it first.\n" );
}
}
}
// dump manually created account to compare values
//$data = apiCall( 'mail_user_get', [ 'primary_id' => [ 'email' => 'manual@apitest.com' ] ] );
//var_dump( $data, true );
foreach ( $csv as $record ) {
$email = $record['email'];
$addr = substr( $email, 0, strpos( $email, '@' ) );
$domain = substr( $email, strpos( $email, '@' ) + 1 );
// ensure we have mail_domain info
if ( ! isset( $mail_domains[$domain] ) || -1 == $mail_domains[$domain]['domain_id'] ) {
print "Config for domain $domain not available, cannot add email $email.\n";
continue;
}
// skip if email already exists
$data = apiCall( 'mail_user_get', [ 'primary_id' => [ 'email' => $email ] ] );
if ( is_array( $data ) && isset( $data[0] ) && isset( $data[0]['mailuser_id'] ) ) {
print "Email $email already exists, skipping.\n";
continue;
}
// get client_id for this sys_userid
if ( isset( $mail_domains[$domain]['client_id'] ) ) {
$client_id = $mail_domains[$domain]['client_id'];
} else {
$client_id = apiCall( 'client_get_id', [ 'sys_userid' => $mail_domains[$domain]['sys_userid'] ] );
if ( ! $client_id ) {
print "Error: unable to determine client_id for $domain (sys_userid is " . $mail_domains[$domain]['sys_userid'] . "),\n";
print "cannot create mailbox for Email $email\n";
continue;
}
$mail_domains[$domain]['client_id'] = $client_id;
}
// mail_user_add parameters for this email
$params = [ 'params' => [
'server_id' => $mail_domains[$domain]['server_id'],
'email' => $email,
'login' => $email,
'password' => $record['password'],
'name' => $record['name'],
'uid' => 5000,
'gid' => 5000,
'maildir' => "/var/vmail/$domain/$addr",
'quota' => $record['quota'] * 1024 * 1024,
'cc' => implode( ',', array_filter( [ $record['cc'], $record['bcc'] ] ) ),
'homedir' => "/var/vmail/",
'autoresponder' => ( preg_match( '/^y/i', $record['autoresponder'] ) ? 'y' : 'n' ),
'autoresponder_start_date' => date( 'Y-m-d H:i:s' ),
'autoresponder_end_date' => date( '2024-m-d H:i:s' ),
'autoresponder_text' => $record['autoresponder_text'],
'move_junk' => ( preg_match( '/^y/i', $record['move_junk'] ) ? 'y' : 'n' ),
'custom_mailfilter' => "",
'postfix' => 'y',
'access' => 'y',
// 'disableimap' => 'n',
// 'disablepop3' => 'n',
// 'disabledeliver' => 'n',
// 'disablesmtp' => 'n',
],
];
// add mail user
$data = apiCall( 'mail_user_add', [ 'client_id' => $client_id ], $params );
if ( ! $data ) {
print "mail_user_add may have a problem inserting $email\n";
continue;
}
//$data = apiCall( 'mail_user_get', [ 'primary_id' => [ 'email' => $email ] ] );
//var_dump( $data, true );
// determine mail policy
$spam_lover = ( preg_match( '/^y/i', $record['move_junk'] ) ? $record['spam_lover'] : 'N' );
$virus_lover = $record['virus_lover'];
$spamfilter_policy_id = null;
// check domain's policy settings for bypass_spam_checks == 'N' and matching spam_lover/virus_lover,
// if a match, we're done
if ( isset( $mail_domains[$domain]['spamfilter_policy_id'] ) ) {
foreach ( $mail_policies as $policy ) {
if ( $policy['id'] == $mail_domains[$domain]['spamfilter_policy_id'] ) {
if ( 'N' == $policy['bypass_spam_checks'] && $policy['spam_lover'] == $spam_lover && $policy['virus_lover'] == $virus_lover ) {
$spamfilter_policy_id = $policy['id'];
}
}
}
}
// if domain's policy doesn't match, loop through all policies to find a match and insert it
if ( null === $spamfilter_policy_id ) {
foreach ( $mail_policies as $policy ) {
if ( 'Y' == $policy['bypass_spam_checks'] ) {
continue;
}
if ( $policy['spam_lover'] == $spam_lover && $policy['virus_lover'] == $virus_lover ) {
$spamfilter_policy_id = $policy['id'];
// mail_spamfilter_user entry for this user / policy_id
$params = [ 'params' => [
'server_id' => $mail_domains[$domain]['server_id'],
'priority' => "10",
'policy_id' => $policy['id'],
'email' => $email,
'fullname' => $email,
'local' => "Y",
],
];
$data = apiCall( 'mail_spamfilter_user_add', [ 'client_id' => $client_id ], $params );
// either we inserted a spamfilter_user or it failed,
// either way, on to the next email
continue 2;
}
}
}
}
// logout so session id is cleaned up
if ( isset( $session_id ) && $session_id ) {
$result = restCall( 'logout', [ 'session_id' => $session_id ] );
$result || die( "Could not get logout result, session id $session_id may now be abandoned.\n" );
}
exit();
......@@ -30,7 +30,6 @@ chdir = /
<tmpl_if name='php_fpm_chroot'>
chroot = <tmpl_var name='php_fpm_chroot_dir'>
php_admin_value[doc_root] = <tmpl_var name='php_fpm_chroot_web_folder'>
php_admin_value[cgi.fix_pathinfo] = 0
</tmpl_if>
env[HOSTNAME] = $HOSTNAME
......
......@@ -42,7 +42,7 @@ class letsencrypt {
public function __construct(){
}
public function get_acme_script() {
$acme = explode("\n", shell_exec('which /usr/local/ispconfig/server/scripts/acme.sh /root/.acme.sh/acme.sh'));
$acme = reset($acme);
......@@ -52,18 +52,18 @@ class letsencrypt {
return false;
}
}
public function get_acme_command($domains, $key_file, $bundle_file, $cert_file, $server_type = 'apache') {
global $app;
$letsencrypt = $this->get_acme_script();
$cmd = '';
// generate cli format
foreach($domains as $domain) {
$cmd .= (string) " -d " . $domain;
}
if($cmd == '') {
return false;
}
......@@ -73,12 +73,12 @@ class letsencrypt {
} else {
$cert_arg = '--fullchain-file ' . escapeshellarg($bundle_file) . ' --cert-file ' . escapeshellarg($cert_file);
}
$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) . ' ' . $cert_arg . ' --reloadcmd ' . escapeshellarg($this->get_reload_command()) . '; C=$? ; fi ; if [[ $C -eq 0 ]] ; then exit $R ; else exit $C ; fi';
return $cmd;
}
public function get_certbot_script() {
$letsencrypt = explode("\n", shell_exec('which letsencrypt certbot /root/.local/share/letsencrypt/bin/letsencrypt /opt/eff.org/certbot/venv/bin/certbot'));
$letsencrypt = reset($letsencrypt);
......@@ -94,13 +94,13 @@ class letsencrypt {
$ret = null;
$val = 0;
exec($install_cmd . ' 2>&1', $ret, $val);
return ($val == 0 ? true : false);
}
private function get_reload_command() {
global $app, $conf;
$web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
$daemon = '';
......@@ -121,26 +121,26 @@ class letsencrypt {
$cmd = $app->system->getinitcommand($daemon, 'force-reload');
return $cmd;
}
public function get_certbot_command($domains) {
global $app;
$letsencrypt = $this->get_certbot_script();
$cmd = '';
// generate cli format
foreach($domains as $domain) {
$cmd .= (string) " --domains " . $domain;
}
if($cmd == '') {
return false;
}
$matches = array();
$ret = null;
$val = 0;
$letsencrypt_version = exec($letsencrypt . ' --version 2>&1', $ret, $val);
if(preg_match('/^(\S+|\w+)\s+(\d+(\.\d+)+)$/', $letsencrypt_version, $matches)) {
$letsencrypt_version = $matches[2];
......@@ -161,39 +161,39 @@ class letsencrypt {
} else {
$webroot_args = "$cmd --webroot-path /usr/local/ispconfig/interface/acme";
}
$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";
return $cmd;
}
public function get_letsencrypt_certificate_paths($domains = array()) {
global $app;
if($this->get_acme_script()) {
return false;
}
if(empty($domains)) return false;
if(!is_dir($this->renew_config_path)) return false;
$dir = opendir($this->renew_config_path);
if(!$dir) return false;
$path_scores = array();
$main_domain = reset($domains);
sort($domains);
$min_diff = false;
while($file = readdir($dir)) {
if($file === '.' || $file === '..' || substr($file, -5) !== '.conf') continue;
$file_path = $this->renew_config_path . '/' . $file;
if(!is_file($file_path) || !is_readable($file_path)) continue;
$fp = fopen($file_path, 'r');
if(!$fp) continue;
$path_scores[$file_path] = array(
'domains' => array(),
'diff' => 0,
......@@ -211,26 +211,26 @@ class letsencrypt {
if($line === '') continue;
elseif(!$in_list) {
if($line == '[[webroot_map]]') $in_list = true;
$tmp = explode('=', $line, 2);
if(count($tmp) != 2) continue;
$key = trim($tmp[0]);
if($key == 'cert' || $key == 'privkey' || $key == 'chain' || $key == 'fullchain') {
$path_scores[$file_path]['cert_paths'][$key] = trim($tmp[1]);
}
continue;
}
$tmp = explode('=', $line, 2);
if(count($tmp) != 2) continue;
$domain = trim($tmp[0]);
if($domain == $main_domain) $path_scores[$file_path]['has_main_domain'] = true;
$path_scores[$file_path]['domains'][] = $domain;
}
fclose($fp);
sort($path_scores[$file_path]['domains']);
if(count(array_intersect($domains, $path_scores[$file_path]['domains'])) < 1) {
$path_scores[$file_path]['diff'] = false;
......@@ -238,13 +238,13 @@ class letsencrypt {
// give higher diff value to missing domains than to those that are too much in there
$path_scores[$file_path]['diff'] = (count(array_diff($domains, $path_scores[$file_path]['domains'])) * 1.5) + count(array_diff($path_scores[$file_path]['domains'], $domains));
}
if($min_diff === false || $path_scores[$file_path]['diff'] < $min_diff) $min_diff = $path_scores[$file_path]['diff'];
}
closedir($dir);
if($min_diff === false) return false;
$cert_paths = false;
$used_path = false;
foreach($path_scores as $path => $data) {
......@@ -254,15 +254,15 @@ class letsencrypt {
if($data['has_main_domain'] == true) break;
}
}
$app->log("Let's Encrypt Cert config path is: " . ($used_path ? $used_path : "not found") . ".", LOGLEVEL_DEBUG);
return $cert_paths;
}
private function get_ssl_domain($data) {
global $app;
$domain = $data['new']['ssl_domain'];
if(!$domain) {
$domain = $data['new']['domain'];
......@@ -276,14 +276,14 @@ class letsencrypt {
$domain = substr($domain, 2);
}
}
return $domain;
}
public function get_website_certificate_paths($data) {
$ssl_dir = $data['new']['document_root'].'/ssl';
$domain = $this->get_ssl_domain($data);
$cert_paths = array(
'domain' => $domain,
'key' => $ssl_dir.'/'.$domain.'.key',
......@@ -292,7 +292,7 @@ class letsencrypt {
'crt' => $ssl_dir.'/'.$domain.'.crt',
'bundle' => $ssl_dir.'/'.$domain.'.bundle'
);
if($data['new']['ssl'] == 'y' && $data['new']['ssl_letsencrypt'] == 'y') {
$cert_paths = array(
'domain' => $domain,
......@@ -302,17 +302,17 @@ class letsencrypt {
'bundle' => $ssl_dir.'/'.$domain.'-le.bundle'
);
}
return $cert_paths;
}
public function request_certificates($data, $server_type = 'apache') {
global $app, $conf;
$app->uses('getconf');
$web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
$server_config = $app->getconf->get_server_config($conf['server_id'], 'server');
$use_acme = false;
if($this->get_acme_script()) {
$use_acme = true;
......@@ -320,13 +320,13 @@ class letsencrypt {
// acme and le missing
$this->install_acme();
}
$tmp = $app->letsencrypt->get_website_certificate_paths($data);
$domain = $tmp['domain'];
$key_file = $tmp['key'];
$crt_file = $tmp['crt'];
$bundle_file = $tmp['bundle'];
// default values
$temp_domains = array($domain);
$cli_domain_arg = '';
......@@ -345,7 +345,7 @@ class letsencrypt {
$temp_domains[] = $subdomain['domain'];
}
}
//* then, add alias domain if we have
$aliasdomains = $app->db->queryAllRecords('SELECT domain,subdomain FROM web_domain WHERE parent_domain_id = '.intval($data['new']['domain_id'])." AND active = 'y' AND type = 'alias' AND ssl_letsencrypt_exclude != 'y'");
if(is_array($aliasdomains)) {
......@@ -395,7 +395,7 @@ class letsencrypt {
// unset useless data
unset($subdomains);
unset($aliasdomains);
$this->certbot_use_certcommand = false;
$letsencrypt_cmd = '';
$allow_return_codes = null;
......@@ -405,7 +405,7 @@ class letsencrypt {
} else {
$letsencrypt_cmd = $this->get_certbot_command($temp_domains);
}
$success = false;
if($letsencrypt_cmd) {
if(!isset($server_config['migration_mode']) || $server_config['migration_mode'] != 'y') {
......@@ -428,10 +428,17 @@ class letsencrypt {
return true;
}
}
$le_files = array();
if($this->certbot_use_certcommand === true && $letsencrypt_cmd) {
$letsencrypt_cmd = $letsencrypt_cmd . " certificates " . $cli_domain_arg;
$cli_domain_arg = '';
// generate cli format
foreach($temp_domains as $temp_domain) {
$cli_domain_arg .= (string) " --domains " . $temp_domain;
}
$letsencrypt_cmd = $this->get_certbot_script() . " certificates " . $cli_domain_arg;
$output = explode("\n", shell_exec($letsencrypt_cmd . " 2>/dev/null | grep -v '^\$'"));
$le_path = '';
$skip_to_next = true;
......@@ -439,18 +446,18 @@ class letsencrypt {
foreach($output as $outline) {
$outline = trim($outline);
$app->log("LE CERT OUTPUT: " . $outline, LOGLEVEL_DEBUG);
if($skip_to_next === true && !preg_match('/^\s*Certificate Name/', $outline)) {
continue;
}
$skip_to_next = false;
if(preg_match('/^\s*Expiry.*?VALID:\s+\D/', $outline)) {
$app->log("Found LE path is expired or invalid: " . $matches[1], LOGLEVEL_DEBUG);
$skip_to_next = true;
continue;
}
if(preg_match('/^\s*Certificate Path:\s*(\/.*?)\s*$/', $outline, $matches)) {
$app->log("Found LE path: " . $matches[1], LOGLEVEL_DEBUG);
$le_path = dirname($matches[1]);
......@@ -461,7 +468,7 @@ class letsencrypt {
}
}
}
if($le_path) {
$le_files = array(
'privkey' => $le_path . '/privkey.pem',
......@@ -475,32 +482,32 @@ class letsencrypt {
$le_files = $this->get_letsencrypt_certificate_paths($temp_domains);
}
unset($temp_domains);
if($server_type != 'apache' || version_compare($app->system->getapacheversion(true), '2.4.8', '>=')) {
$crt_tmp_file = $le_files['fullchain'];
} else {
$crt_tmp_file = $le_files['cert'];
}
$key_tmp_file = $le_files['privkey'];
$bundle_tmp_file = $le_files['chain'];
if(!$success) {
// error issuing cert
$app->log('Let\'s Encrypt SSL Cert for: ' . $domain . ' could not be issued.', LOGLEVEL_WARN);
$app->log($letsencrypt_cmd, LOGLEVEL_WARN);
// if cert already exists, dont remove it. Ex. expired/misstyped/noDnsYet alias domain, api down...
if(!file_exists($crt_tmp_file)) {
return false;
}
}
//* check is been correctly created
if(file_exists($crt_tmp_file)) {
$app->log("Let's Encrypt Cert file: $crt_tmp_file exists.", LOGLEVEL_DEBUG);
$date = date("YmdHis");
//* TODO: check if is a symlink, if target same keep it, either remove it
if(is_file($key_file)) {
$app->system->copy($key_file, $key_file.'.old.'.$date);
......@@ -528,7 +535,7 @@ class letsencrypt {
if(@is_link($bundle_file)) $app->system->unlink($bundle_file);
if(@file_exists($bundle_tmp_file)) $app->system->exec_safe("ln -s ? ?", $bundle_tmp_file, $bundle_file);
return true;
} else {
$app->log("Let's Encrypt Cert file: $crt_tmp_file does not exist.", LOGLEVEL_DEBUG);
......