Commit a7382a30 authored by Florian Schaal's avatar Florian Schaal
Browse files

Merge branch 'develop' of git.ispconfig.org:ispconfig/ispconfig3 into develop

parents 81f8237f 509a4aa0
......@@ -162,6 +162,7 @@ class installer_dist extends installer_base {
touch($config_dir.'/mime_header_checks');
touch($config_dir.'/nested_header_checks');
touch($config_dir.'/body_checks');
touch($config_dir.'/sasl_passwd');
//* Create the mailman files
if(!is_dir('/var/lib/mailman/data')) exec('mkdir -p /var/lib/mailman/data');
......
......@@ -160,6 +160,7 @@ class installer extends installer_base
touch($config_dir.'/mime_header_checks');
touch($config_dir.'/nested_header_checks');
touch($config_dir.'/body_checks');
touch($config_dir.'/sasl_passwd');
//* Create auxillary postfix conf files
$configfile = 'helo_access';
......
......@@ -176,6 +176,7 @@ class installer_dist extends installer_base {
touch($config_dir.'/mime_header_checks');
touch($config_dir.'/nested_header_checks');
touch($config_dir.'/body_checks');
touch($config_dir.'/sasl_passwd');
//* Create the mailman files
if(!is_dir('/var/lib/mailman/data')) exec('mkdir -p /var/lib/mailman/data');
......
......@@ -1193,6 +1193,7 @@ class installer_base {
touch($config_dir.'/mime_header_checks');
touch($config_dir.'/nested_header_checks');
touch($config_dir.'/body_checks');
touch($config_dir.'/sasl_passwd');
//* Create the mailman files
if(!is_dir('/var/lib/mailman/data')) exec('mkdir -p /var/lib/mailman/data');
......
......@@ -16,3 +16,6 @@ DROP TABLE 'software_package';
DROP TABLE 'software_repo';
DROP TABLE 'software_update';
DROP TABLE 'software_update_inst';
-- Brexit
UPDATE `country` SET `eu` = 'n' WHERE `iso` = 'GB';
......@@ -2408,7 +2408,7 @@ INSERT INTO `country` (`iso`, `name`, `printable_name`, `iso3`, `numcode`, `eu`)
('UG', 'UGANDA', 'Uganda', 'UGA', 800, 'n'),
('UA', 'UKRAINE', 'Ukraine', 'UKR', 804, 'n'),
('AE', 'UNITED ARAB EMIRATES', 'United Arab Emirates', 'ARE', 784, 'n'),
('GB', 'UNITED KINGDOM', 'United Kingdom', 'GBR', 826, 'y'),
('GB', 'UNITED KINGDOM', 'United Kingdom', 'GBR', 826, 'n'),
('US', 'UNITED STATES', 'United States', 'USA', 840, 'n'),
('UM', 'UNITED STATES MINOR OUTLYING ISLANDS', 'United States Minor Outlying Islands', NULL, NULL, 'n'),
('UY', 'URUGUAY', 'Uruguay', 'URY', 858, 'n'),
......
......@@ -3,6 +3,7 @@
################################################
ServerTokens ProductOnly
ServerSignature Off
DirectoryIndex index.html index.cgi index.pl index.php index.xhtml index.htm standard_index.html
################################################
# ISPConfig Logfile configuration for vlogger
......
......@@ -54,7 +54,7 @@ address_verify_negative_refresh_time=60s
# needed for postfix < 3.3 when using reject_unverified_recipient (lmtp):
enable_original_recipient = yes
sender_dependent_relayhost_maps = proxy:mysql:{config_dir}/mysql-virtual_sender-relayhost.cf
smtp_sasl_password_maps = proxy:mysql:{config_dir}/mysql-virtual_sender-relayauth.cf, hash:{config_dir}/sasl_passwd
smtp_sasl_password_maps = proxy:mysql:{config_dir}/mysql-virtual_sender-relayauth.cf, texthash:{config_dir}/sasl_passwd
smtp_sender_dependent_authentication = yes
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous, noplaintext
......
......@@ -50,7 +50,7 @@ address_verify_negative_refresh_time=60s
# needed for postfix < 3.3 when using reject_unverified_recipient (lmtp):
enable_original_recipient = yes
sender_dependent_relayhost_maps = proxy:mysql:{config_dir}/mysql-virtual_sender-relayhost.cf
smtp_sasl_password_maps = proxy:mysql:{config_dir}/mysql-virtual_sender-relayauth.cf, hash:{config_dir}/sasl_passwd
smtp_sasl_password_maps = proxy:mysql:{config_dir}/mysql-virtual_sender-relayauth.cf, texthash:{config_dir}/sasl_passwd
smtp_sender_dependent_authentication = yes
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous, noplaintext
......
......@@ -49,7 +49,7 @@ address_verify_negative_refresh_time=60s
# needed for postfix < 3.3 when using reject_unverified_recipient (lmtp):
enable_original_recipient = yes
sender_dependent_relayhost_maps = proxy:mysql:{config_dir}/mysql-virtual_sender-relayhost.cf
smtp_sasl_password_maps = proxy:mysql:{config_dir}/mysql-virtual_sender-relayauth.cf, hash:{config_dir}/sasl_passwd
smtp_sasl_password_maps = proxy:mysql:{config_dir}/mysql-virtual_sender-relayauth.cf, texthash:{config_dir}/sasl_passwd
smtp_sender_dependent_authentication = yes
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous, noplaintext
......
......@@ -149,8 +149,8 @@ paths_w_setuid = /bin/ping
#paths = /usr/bin/X11/xterm, /usr/share/terminfo, /etc/terminfo
#devices = /dev/pts/0, /dev/pts/1, /dev/pts/2, /dev/pts/3, /dev/pts/4, /dev/ptyb4, /dev/ptya4, /dev/tty, /dev/tty0, /dev/tty4
+# coreutils from:
+# (echo -ne '\n[coreutils]\ncomment = non-sbin progs from coreutils\npaths = '; dpkg --listfiles coreutils | grep -E '^/bin/|/usr/bin/' | xargs -n1 -i@ echo -n "@, " | sed -e 's/, *$/\n/g' -e 's|/usr/bin/||g' -e 's|/bin/||g') >> /etc/jailkit/jk_init.ini
# coreutils from:
# (echo -ne '\n[coreutils]\ncomment = non-sbin progs from coreutils\npaths = '; dpkg --listfiles coreutils | grep -E '^/bin/|/usr/bin/' | xargs -n1 -i@ echo -n "@, " | sed -e 's/, *$/\n/g' -e 's|/usr/bin/||g' -e 's|/bin/||g') >> /etc/jailkit/jk_init.ini
[coreutils]
comment = non-sbin progs from coreutils
......@@ -225,3 +225,8 @@ includesections = php_common
comment = php version 7.4
paths = /usr/bin/php7.4, /usr/lib/php/7.4/, /usr/lib/php/20190902/, /usr/share/php/7.4/, /etc/php/7.4/cli/, /etc/php/7.4/mods-available/
includesections = php_common
[php8_0]
comment = php version 8.0
paths = /usr/bin/php8.0, /usr/lib/php/8.0/, /usr/lib/php/20200930/, /usr/share/php/8.0/, /etc/php/8.0/cli/, /etc/php/8.0/mods-available/
includesections = php_common
......@@ -2,8 +2,8 @@ user = {mysql_server_ispconfig_user}
password = {mysql_server_ispconfig_password}
dbname = {mysql_server_database}
hosts = {mysql_server_ip}
query = SELECT email FROM mail_user WHERE email = '%s' AND forward_in_lda = 'n' AND disabledeliver = 'n' AND postfix = 'y' AND server_id = {server_id}
query = SELECT email FROM mail_user WHERE email = '%s' AND forward_in_lda = 'n' AND disabledeliver = 'n' AND disablesmtp = 'n' AND server_id = {server_id}
AND EXISTS (SELECT domain_id FROM mail_domain WHERE domain = SUBSTRING_INDEX('%s', '@', -1) AND active = 'y' AND server_id = {server_id})
UNION
SELECT cc AS email FROM mail_user WHERE email = '%s' AND cc != '' AND (forward_in_lda = 'n' OR disabledeliver = 'y') AND postfix = 'y' AND server_id = {server_id}
SELECT cc AS email FROM mail_user WHERE email = '%s' AND cc != '' AND (forward_in_lda = 'n' OR disabledeliver = 'y') AND disablesmtp = 'n' AND server_id = {server_id}
AND EXISTS (SELECT domain_id FROM mail_domain WHERE domain = SUBSTRING_INDEX('%s', '@', -1) AND active = 'y' AND server_id = {server_id})
......@@ -52,7 +52,7 @@ address_verify_negative_refresh_time=60s
# needed for postfix < 3.3 when using reject_unverified_recipient (lmtp):
enable_original_recipient = yes
sender_dependent_relayhost_maps = proxy:mysql:{config_dir}/mysql-virtual_sender-relayhost.cf
smtp_sasl_password_maps = proxy:mysql:{config_dir}/mysql-virtual_sender-relayauth.cf, hash:{config_dir}/sasl_passwd
smtp_sasl_password_maps = proxy:mysql:{config_dir}/mysql-virtual_sender-relayauth.cf, texthash:{config_dir}/sasl_passwd
smtp_sender_dependent_authentication = yes
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous, noplaintext
......
......@@ -15,7 +15,7 @@ server {
listen <tmpl_var name='ip_address'>:<tmpl_var name='https_port'> ssl http2;
<tmpl_if name='use_proxy_protocol' op='==' value='y'>
<tmpl_if name='proxy_protocol_https' op='>' value='0'>
listen <tmpl_var name='ip_address'>:<tmpl_var name='proxy_protocol_https'> ssl proxy_protocol;
listen <tmpl_var name='ip_address'>:<tmpl_var name='proxy_protocol_https'> ssl http2 proxy_protocol;
</tmpl_if>
</tmpl_if>
......@@ -79,7 +79,7 @@ server {
</tmpl_if>
</tmpl_loop>
<tmpl_if name='use_proxy' op='!=' value='y'>
index index.html index.htm index.php index.cgi index.pl index.xhtml;
index index.html index.htm index.php index.cgi index.pl index.xhtml standard_index.html;
<tmpl_if name='ssi' op='==' value='y'>
location ~ \.shtml$ {
......
......@@ -114,7 +114,8 @@ class cronjob_jailkit_maintenance extends cronjob {
if (is_file( $rec['document_root']."/bin/bash" )) {
# test that /bin/bash functions in the jail
print "chroot --userspec ".$rec['system_user'].":".$rec['system_group']." ".$rec['document_root']." /bin/bash -c true 2>/dev/null\n";
if (! $app->system->exec_safe("chroot --userspec ?:? ? /bin/bash -c true 2>/dev/null", $rec['system_user'], $rec['system_group'], $rec['document_root'])) {
$app->system->exec_safe("chroot --userspec ?:? ? /bin/bash -c true 2>/dev/null", $rec['system_user'], $rec['system_group'], $rec['document_root']);
if ($app->system->last_exec_retcode()) { # return 0 means success
print "/bin/bash test failed, forcing update\n";
$options[] = 'force';
# bogus hash will not match, triggering an update
......
......@@ -2412,6 +2412,7 @@ class system{
public function create_jailkit_chroot($home_dir, $app_sections = array(), $options = array()) {
global $app;
$app->log("create_jailkit_chroot: called for home_dir $home_dir with options: " . print_r($options, true), LOGLEVEL_DEBUG);
// Disallow operating on root directory
if(realpath($home_dir) == '/') {
......@@ -2428,6 +2429,9 @@ class system{
} elseif(is_string($app_sections)) {
$app_sections = preg_split('/[\s,]+/', $app_sections);
}
if(! is_array($options)) {
$options = (is_string($options) ? preg_split('/[\s,]+/', $options) : array());
}
// Change ownership of the chroot directory to root
$this->chown($home_dir, 'root');
......@@ -2485,6 +2489,7 @@ class system{
public function create_jailkit_programs($home_dir, $programs = array(), $options = array()) {
global $app;
$app->log("create_jailkit_programs: called for home_dir $home_dir with options: " . print_r($options, true), LOGLEVEL_DEBUG);
// Disallow operating on root directory
if(realpath($home_dir) == '/') {
......@@ -2501,6 +2506,9 @@ class system{
} elseif(is_string($programs)) {
$programs = preg_split('/[\s,]+/', $programs);
}
if(! is_array($options)) {
$options = (is_string($options) ? preg_split('/[\s,]+/', $options) : array());
}
# prohibit ill-advised copying paths known to be sensitive/problematic
# (easy to bypass if needed, eg. use /./etc)
......
......@@ -788,17 +788,18 @@ class apache2_plugin {
$last_updated = array_unique($last_updated, SORT_REGULAR);
sort($last_updated, SORT_STRING);
$update_hash = hash('md5', implode(' ', $last_updated));
$check_for_jailkit_updates=false;
// Create jailkit chroot when enabling php_fpm_chroot
if($data['new']['php_fpm_chroot'] == 'y' && $data['old']['php_fpm_chroot'] != 'y') {
if($data['new']['php_fpm_chroot'] == 'y' && $data['old']['php_fpm_chroot'] != 'y' && $data['new']['php'] != 'no') {
$website = $app->db->queryOneRecord('SELECT * FROM web_domain WHERE domain_id = ?', $data['new']['domain_id']);
$this->website = array_merge($website, $data['new'], array('new_jailkit_hash' => $update_hash));
$this->jailkit_config = $jailkit_config;
$this->_setup_jailkit_chroot();
$this->_add_jailkit_user();
$check_for_jailkit_updates=false;
// else delete if unused
} elseif ($data['new']['delete_unused_jailkit'] == 'y' && $data['new']['php_fpm_chroot'] != 'y') {
} elseif (($data['new']['delete_unused_jailkit'] == 'y' && $data['new']['php_fpm_chroot'] != 'y') ||
($data['new']['delete_unused_jailkit'] == 'y' && $data['new']['php'] == 'no')) {
$check_for_jailkit_updates=false;
$this->_delete_jailkit_if_unused($data['new']['domain_id']);
if(is_dir($data['new']['document_root'].'/etc/jailkit')) {
......@@ -953,11 +954,11 @@ class apache2_plugin {
$app->system->exec_safe('chmod -R a+r ?', $error_page_path);
}
//* Copy the web skeleton files only when there is no index.ph or index.html file yet
if(!file_exists($data['new']['document_root'].'/'.$web_folder.'/index.html') && !file_exists($data['new']['document_root'].'/'.$web_folder.'/index.php')) {
//* Copy the web skeleton files only when there is no index.php, standard_index.html or index.html file yet
if(!file_exists($data['new']['document_root'].'/'.$web_folder.'/index.html') && !file_exists($data['new']['document_root'].'/'.$web_folder.'/index.php') && !file_exists($data['new']['document_root'].'/'.$web_folder.'/standard_index.html')) {
if (file_exists($conf['rootpath'] . '/conf-custom/index/standard_index.html_'.substr($conf['language'], 0, 2))) {
if(!file_exists($data['new']['document_root'] . '/' . $web_folder . '/index.html')) {
$app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf-custom/index/standard_index.html_' . substr($conf['language'], 0, 2), $data['new']['document_root'] . '/' . $web_folder . '/index.html');
if(!file_exists($data['new']['document_root'] . '/' . $web_folder . '/standard_index.html')) {
$app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf-custom/index/standard_index.html_' . substr($conf['language'], 0, 2), $data['new']['document_root'] . '/' . $web_folder . '/standard_index.html');
}
if(is_file($conf['rootpath'] . '/conf-custom/index/favicon.ico')) {
......@@ -968,13 +969,13 @@ class apache2_plugin {
}
} else {
if (file_exists($conf['rootpath'] . '/conf-custom/index/standard_index.html')) {
if(!file_exists($data['new']['document_root'].'/' . $web_folder . '/index.html')) $app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf-custom/index/standard_index.html', $data['new']['document_root'].'/' . $web_folder . '/index.html');
if(!file_exists($data['new']['document_root'].'/' . $web_folder . '/standard_index.html')) $app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf-custom/index/standard_index.html', $data['new']['document_root'].'/' . $web_folder . '/standard_index.html');
} else {
if(!file_exists($data['new']['document_root'].'/' . $web_folder . '/index.html')) $app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf/index/standard_index.html_'.substr($conf['language'], 0, 2), $data['new']['document_root'].'/' . $web_folder . '/index.html');
if(is_file($conf['rootpath'] . '/conf/index/favicon.ico')){
if(!file_exists($data['new']['document_root'].'/' . $web_folder . '/standard_index.html')) $app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf/index/standard_index.html_'.substr($conf['language'], 0, 2), $data['new']['document_root'].'/' . $web_folder . '/standard_index.html');
if(is_file($conf['rootpath'] . '/conf/index/favicon.ico')) {
if(!file_exists($data['new']['document_root'].'/' . $web_folder . '/favicon.ico')) $app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf/index/favicon.ico', $data['new']['document_root'].'/' . $web_folder . '/');
}
if(is_file($conf['rootpath'] . '/conf/index/robots.txt')){
if(is_file($conf['rootpath'] . '/conf/index/robots.txt')) {
if(!file_exists($data['new']['document_root'].'/' . $web_folder . '/robots.txt')) $app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf/index/robots.txt', $data['new']['document_root'].'/' . $web_folder . '/');
}
}
......@@ -3820,7 +3821,7 @@ class apache2_plugin {
}
// chroot is used by php-fpm
if (isset($parent_domain['php_fpm_chroot']) && $parent_domain['php_fpm_chroot'] == 'y') {
if (isset($parent_domain['php_fpm_chroot']) && $parent_domain['php_fpm_chroot'] == 'y' && $parent_domain['php'] != 'no') {
return;
}
......
......@@ -626,17 +626,18 @@ class nginx_plugin {
$last_updated = array_unique($last_updated, SORT_REGULAR);
sort($last_updated, SORT_STRING);
$update_hash = hash('md5', implode(' ', $last_updated));
$check_for_jailkit_updates=false;
// Create jailkit chroot when enabling php_fpm_chroot
if($data['new']['php_fpm_chroot'] == 'y' && $data['old']['php_fpm_chroot'] != 'y') {
if($data['new']['php_fpm_chroot'] == 'y' && $data['old']['php_fpm_chroot'] != 'y' && $data['new']['php'] != 'no') {
$website = $app->db->queryOneRecord('SELECT * FROM web_domain WHERE domain_id = ?', $data['new']['domain_id']);
$this->website = array_merge($website, $data['new'], array('new_jailkit_hash' => $update_hash));
$this->jailkit_config = $jailkit_config;
$this->_setup_jailkit_chroot();
$this->_add_jailkit_user();
$check_for_jailkit_updates=false;
// else delete if unused
} elseif ($data['new']['delete_unused_jailkit'] == 'y' && $data['new']['php_fpm_chroot'] != 'y') {
} elseif (($data['new']['delete_unused_jailkit'] == 'y' && $data['new']['php_fpm_chroot'] != 'y') ||
($data['new']['delete_unused_jailkit'] == 'y' && $data['new']['php'] == 'no')) {
$check_for_jailkit_updates=false;
$this->_delete_jailkit_if_unused($data['new']['domain_id']);
if(is_dir($data['new']['document_root'].'/etc/jailkit')) {
......@@ -791,10 +792,12 @@ class nginx_plugin {
$app->system->exec_safe('chmod -R a+r ?', $error_page_path);
}
//* Copy the web skeleton files only when there is no index.ph or index.html file yet
if(!file_exists($data['new']['document_root'].'/'.$web_folder.'/index.html') && !file_exists($data['new']['document_root'].'/'.$web_folder.'/index.php')) {
//* Copy the web skeleton files only when there is no index.php, standard_index.html or index.html file yet
if(!file_exists($data['new']['document_root'].'/'.$web_folder.'/index.html') && !file_exists($data['new']['document_root'].'/'.$web_folder.'/index.php') && !file_exists($data['new']['document_root'].'/'.$web_folder.'/standard_index.html')) {
if (file_exists($conf['rootpath'] . '/conf-custom/index/standard_index.html_'.substr($conf['language'], 0, 2))) {
if(!file_exists($data['new']['document_root'].'/' . $web_folder . '/index.html')) $app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf-custom/index/standard_index.html_'.substr($conf['language'], 0, 2), $data['new']['document_root'].'/' . $web_folder . '/index.html');
if(!file_exists($data['new']['document_root'] . '/' . $web_folder . '/standard_index.html')) {
$app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf-custom/index/standard_index.html_' . substr($conf['language'], 0, 2), $data['new']['document_root'] . '/' . $web_folder . '/standard_index.html');
}
if(is_file($conf['rootpath'] . '/conf-custom/index/favicon.ico')) {
if(!file_exists($data['new']['document_root'].'/' . $web_folder . '/favicon.ico')) $app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf-custom/index/favicon.ico', $data['new']['document_root'].'/' . $web_folder . '/');
......@@ -804,13 +807,13 @@ class nginx_plugin {
}
} else {
if (file_exists($conf['rootpath'] . '/conf-custom/index/standard_index.html')) {
if(!file_exists($data['new']['document_root'].'/' . $web_folder . '/index.html')) $app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf-custom/index/standard_index.html', $data['new']['document_root'].'/' . $web_folder . '/index.html');
if(!file_exists($data['new']['document_root'].'/' . $web_folder . '/standard_index.html')) $app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf-custom/index/standard_index.html', $data['new']['document_root'].'/' . $web_folder . '/standard_index.html');
} else {
if(!file_exists($data['new']['document_root'].'/' . $web_folder . '/index.html')) $app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf/index/standard_index.html_'.substr($conf['language'], 0, 2), $data['new']['document_root'].'/' . $web_folder . '/index.html');
if(is_file($conf['rootpath'] . '/conf/index/favicon.ico')){
if(!file_exists($data['new']['document_root'].'/' . $web_folder . '/standard_index.html')) $app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf/index/standard_index.html_'.substr($conf['language'], 0, 2), $data['new']['document_root'].'/' . $web_folder . '/standard_index.html');
if(is_file($conf['rootpath'] . '/conf/index/favicon.ico')) {
if(!file_exists($data['new']['document_root'].'/' . $web_folder . '/favicon.ico')) $app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf/index/favicon.ico', $data['new']['document_root'].'/' . $web_folder . '/');
}
if(is_file($conf['rootpath'] . '/conf/index/robots.txt')){
if(is_file($conf['rootpath'] . '/conf/index/robots.txt')) {
if(!file_exists($data['new']['document_root'].'/' . $web_folder . '/robots.txt')) $app->system->exec_safe('cp ? ?', $conf['rootpath'] . '/conf/index/robots.txt', $data['new']['document_root'].'/' . $web_folder . '/');
}
}
......@@ -3597,7 +3600,7 @@ class nginx_plugin {
}
// chroot is used by php-fpm
if (isset($parent_domain['php_fpm_chroot']) && $parent_domain['php_fpm_chroot'] == 'y') {
if (isset($parent_domain['php_fpm_chroot']) && $parent_domain['php_fpm_chroot'] == 'y' && $parent_domain['php'] != 'no') {
return;
}
......
......@@ -123,7 +123,7 @@ class openvz_plugin {
//* new diskspace for ploop-containers requieres "vzctl set"
if($data['new']['diskspace'] != $data['old']['diskspace']) {
escapeshell("vzctl set ? --diskspace ? --save", $veid, $data['new']['diskspace']."G");
$app->system->exec_safe("vzctl set ? --diskspace ? --save", $veid, $data['new']['diskspace']."G");
}
//* Apply config changes to the VM
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment