mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2026-03-25 19:17:22 +01:00
commit upstream
This commit is contained in:
@@ -225,6 +225,9 @@ def get_duplicity_additional_args(env):
|
||||
except ValueError:
|
||||
port = 22
|
||||
|
||||
if port is None:
|
||||
port = 22
|
||||
|
||||
return [
|
||||
f"--ssh-options= -i /root/.ssh/id_rsa_miab -p {port}",
|
||||
f"--rsync-options= -e \"/usr/bin/ssh -oStrictHostKeyChecking=no -oBatchMode=yes -p {port} -i /root/.ssh/id_rsa_miab\"",
|
||||
@@ -430,6 +433,9 @@ def list_target_files(config):
|
||||
except ValueError:
|
||||
port = 22
|
||||
|
||||
if port is None:
|
||||
port = 22
|
||||
|
||||
target_path = target.path
|
||||
if not target_path.endswith('/'):
|
||||
target_path = target_path + '/'
|
||||
@@ -553,7 +559,8 @@ def get_backup_config(env, for_save=False, for_ui=False):
|
||||
|
||||
# Merge in anything written to custom.yaml.
|
||||
try:
|
||||
custom_config = rtyaml.load(open(os.path.join(backup_root, 'custom.yaml')))
|
||||
with open(os.path.join(backup_root, 'custom.yaml'), 'r') as f:
|
||||
custom_config = rtyaml.load(f)
|
||||
if not isinstance(custom_config, dict): raise ValueError() # caught below
|
||||
config.update(custom_config)
|
||||
except:
|
||||
@@ -578,7 +585,8 @@ def get_backup_config(env, for_save=False, for_ui=False):
|
||||
config["target"] = "file://" + config["file_target_directory"]
|
||||
ssh_pub_key = os.path.join('/root', '.ssh', 'id_rsa_miab.pub')
|
||||
if os.path.exists(ssh_pub_key):
|
||||
config["ssh_pub_key"] = open(ssh_pub_key, 'r').read()
|
||||
with open(ssh_pub_key, 'r') as f:
|
||||
config["ssh_pub_key"] = f.read()
|
||||
|
||||
return config
|
||||
|
||||
|
||||
@@ -47,7 +47,8 @@ def read_password():
|
||||
return first
|
||||
|
||||
def setup_key_auth(mgmt_uri):
|
||||
key = open('/var/lib/mailinabox/api.key').read().strip()
|
||||
with open('/var/lib/mailinabox/api.key', 'r') as f:
|
||||
key = f.read().strip()
|
||||
|
||||
auth_handler = urllib.request.HTTPBasicAuthHandler()
|
||||
auth_handler.add_password(
|
||||
|
||||
@@ -897,7 +897,8 @@ def write_dkim_tables(domains, env):
|
||||
|
||||
def get_custom_dns_config(env, only_real_records=False):
|
||||
try:
|
||||
custom_dns = rtyaml.load(open(os.path.join(env['STORAGE_ROOT'], 'dns/custom.yaml')))
|
||||
with open(os.path.join(env['STORAGE_ROOT'], 'dns/custom.yaml'), 'r') as f:
|
||||
custom_dns = rtyaml.load(f)
|
||||
if not isinstance(custom_dns, dict): raise ValueError() # caught below
|
||||
except:
|
||||
return [ ]
|
||||
@@ -1121,6 +1122,7 @@ def set_secondary_dns(hostnames, env):
|
||||
resolver = dns.resolver.get_default_resolver()
|
||||
resolver.timeout = 5
|
||||
resolver.lifetime = 5
|
||||
|
||||
for item in hostnames:
|
||||
if not item.startswith("xfr:"):
|
||||
# Resolve hostname.
|
||||
|
||||
@@ -73,7 +73,8 @@ def scan_files(collector):
|
||||
continue
|
||||
elif fn[-3:] == '.gz':
|
||||
tmp_file = tempfile.NamedTemporaryFile()
|
||||
shutil.copyfileobj(gzip.open(fn), tmp_file)
|
||||
with gzip.open(fn, 'rb') as f:
|
||||
shutil.copyfileobj(f, tmp_file)
|
||||
|
||||
if VERBOSE:
|
||||
print("Processing file", fn, "...")
|
||||
|
||||
@@ -537,7 +537,8 @@ def check_certificate(domain, ssl_certificate, ssl_private_key, warn_if_expiring
|
||||
# Second, check that the certificate matches the private key.
|
||||
if ssl_private_key is not None:
|
||||
try:
|
||||
priv_key = load_pem(open(ssl_private_key, 'rb').read())
|
||||
with open(ssl_private_key, 'rb') as f:
|
||||
priv_key = load_pem(f.read())
|
||||
except ValueError as e:
|
||||
return ("The private key file %s is not a private key file: %s" % (ssl_private_key, str(e)), None)
|
||||
|
||||
|
||||
@@ -99,6 +99,12 @@ def run_services_checks(env, output, pool):
|
||||
fatal = fatal or fatal2
|
||||
output2.playback(output)
|
||||
|
||||
# Check fail2ban.
|
||||
code, ret = shell('check_output', ["fail2ban-client", "status"], capture_stderr=True, trap=True)
|
||||
if code != 0:
|
||||
output.print_error("fail2ban is not running.")
|
||||
all_running = False
|
||||
|
||||
if all_running:
|
||||
output.print_ok("All system services are running.")
|
||||
|
||||
@@ -213,7 +219,8 @@ def check_ssh_password(env, output):
|
||||
# the configuration file.
|
||||
if not os.path.exists("/etc/ssh/sshd_config"):
|
||||
return
|
||||
sshd = open("/etc/ssh/sshd_config").read()
|
||||
with open("/etc/ssh/sshd_config", "r") as f:
|
||||
sshd = f.read()
|
||||
if re.search("\nPasswordAuthentication\s+yes", sshd) \
|
||||
or not re.search("\nPasswordAuthentication\s+no", sshd):
|
||||
output.print_error("""The SSH server on this machine permits password-based login. A more secure
|
||||
@@ -609,10 +616,9 @@ def check_dnssec(domain, env, output, dns_zonefiles, is_checking_primary=False):
|
||||
# Some registrars may want the public key so they can compute the digest. The DS
|
||||
# record that we suggest using is for the KSK (and that's how the DS records were generated).
|
||||
# We'll also give the nice name for the key algorithm.
|
||||
dnssec_keys_file = os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/%s.conf' % alg_name_map[ds_alg])
|
||||
if os.path.isfile(dnssec_keys_file):
|
||||
dnssec_keys = load_env_vars_from_file(dnssec_keys_file)
|
||||
dnsssec_pubkey = open(os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/' + dnssec_keys['KSK'] + '.key')).read().split("\t")[3].split(" ")[3]
|
||||
dnssec_keys = load_env_vars_from_file(os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/%s.conf' % alg_name_map[ds_alg]))
|
||||
with open(os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/' + dnssec_keys['KSK'] + '.key'), 'r') as f:
|
||||
dnsssec_pubkey = f.read().split("\t")[3].split(" ")[3]
|
||||
|
||||
expected_ds_records[ (ds_keytag, ds_alg, ds_digalg, ds_digest) ] = {
|
||||
"record": rr_ds,
|
||||
@@ -812,15 +818,14 @@ def query_dns(qname, rtype, nxdomain='[Not Set]', at=None, as_list=False, retry=
|
||||
resolver = dns.resolver.get_default_resolver()
|
||||
|
||||
# Make sure at is not a string that cannot be used as a nameserver
|
||||
if at:
|
||||
if at not in {'[Not set]', '[timeout]'}:
|
||||
resolver = dns.resolver.Resolver()
|
||||
resolver.nameservers = [at]
|
||||
else:
|
||||
logging.error("at not set to a usable nameserver, %s", at)
|
||||
|
||||
if at and at not in {'[Not set]', '[timeout]'}:
|
||||
resolver = dns.resolver.Resolver()
|
||||
resolver.nameservers = [at]
|
||||
|
||||
# Set a timeout so that a non-responsive server doesn't hold us back.
|
||||
resolver.timeout = 5
|
||||
# The number of seconds to spend trying to get an answer to the question. If the
|
||||
# lifetime expires a dns.exception.Timeout exception will be raised.
|
||||
resolver.lifetime = 5
|
||||
|
||||
if retry:
|
||||
@@ -993,7 +998,8 @@ def run_and_output_changes(env, pool):
|
||||
# Load previously saved status checks.
|
||||
cache_fn = "/var/cache/mailinabox/status_checks.json"
|
||||
if os.path.exists(cache_fn):
|
||||
prev = json.load(open(cache_fn))
|
||||
with open(cache_fn, 'r') as f:
|
||||
prev = json.load(f)
|
||||
|
||||
# Group the serial output into categories by the headings.
|
||||
def group_by_heading(lines):
|
||||
|
||||
@@ -72,11 +72,6 @@
|
||||
html {
|
||||
filter: invert(100%) hue-rotate(180deg);
|
||||
}
|
||||
|
||||
/* Set explicit background color (necessary for Firefox) */
|
||||
html {
|
||||
background-color: #111;
|
||||
}
|
||||
|
||||
/* Override Boostrap theme here to give more contrast. The black turns to white by the filter. */
|
||||
.form-control {
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
}
|
||||
#system-checks .status-error td {
|
||||
#system-checks .status-error td, .summary-error {
|
||||
color: #733;
|
||||
}
|
||||
#system-checks .status-warning td {
|
||||
#system-checks .status-warning td, .summary-warning {
|
||||
color: #770;
|
||||
}
|
||||
#system-checks .status-ok td {
|
||||
#system-checks .status-ok td, .summary-ok {
|
||||
color: #040;
|
||||
}
|
||||
#system-checks div.extra {
|
||||
@@ -52,6 +52,9 @@
|
||||
</div> <!-- /col -->
|
||||
<div class="col-md-pull-3 col-md-8">
|
||||
|
||||
<div id="system-checks-summary">
|
||||
</div>
|
||||
|
||||
<table id="system-checks" class="table" style="max-width: 60em">
|
||||
<thead>
|
||||
</thead>
|
||||
@@ -64,6 +67,9 @@
|
||||
|
||||
<script>
|
||||
function show_system_status() {
|
||||
const summary = $('#system-checks-summary');
|
||||
summary.html("");
|
||||
|
||||
$('#system-checks tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
|
||||
|
||||
api(
|
||||
@@ -93,6 +99,12 @@ function show_system_status() {
|
||||
{ },
|
||||
function(r) {
|
||||
$('#system-checks tbody').html("");
|
||||
const ok_symbol = "✓";
|
||||
const error_symbol = "✖";
|
||||
const warning_symbol = "?";
|
||||
|
||||
let count_by_status = { ok: 0, error: 0, warning: 0 };
|
||||
|
||||
for (var i = 0; i < r.length; i++) {
|
||||
var n = $("<tr><td class='status'/><td class='message'><p style='margin: 0'/><div class='extra'/><a class='showhide' href='#'/></tr>");
|
||||
if (i == 0) n.addClass('first')
|
||||
@@ -100,9 +112,12 @@ function show_system_status() {
|
||||
n.addClass(r[i].type)
|
||||
else
|
||||
n.addClass("status-" + r[i].type)
|
||||
if (r[i].type == "ok") n.find('td.status').text("✓")
|
||||
if (r[i].type == "error") n.find('td.status').text("✖")
|
||||
if (r[i].type == "warning") n.find('td.status').text("?")
|
||||
|
||||
if (r[i].type == "ok") n.find('td.status').text(ok_symbol);
|
||||
if (r[i].type == "error") n.find('td.status').text(error_symbol);
|
||||
if (r[i].type == "warning") n.find('td.status').text(warning_symbol);
|
||||
count_by_status[r[i].type]++;
|
||||
|
||||
n.find('td.message p').text(r[i].text)
|
||||
$('#system-checks tbody').append(n);
|
||||
|
||||
@@ -122,8 +137,17 @@ function show_system_status() {
|
||||
n.find('> td.message > div').append(m);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Summary counts
|
||||
summary.html("Summary: ");
|
||||
if (count_by_status['error'] + count_by_status['warning'] == 0) {
|
||||
summary.append($('<span class="summary-ok"/>').text(`All ${count_by_status['ok']} ${ok_symbol} OK`));
|
||||
} else {
|
||||
summary.append($('<span class="summary-ok"/>').text(`${count_by_status['ok']} ${ok_symbol} OK, `));
|
||||
summary.append($('<span class="summary-error"/>').text(`${count_by_status['error']} ${error_symbol} Error, `));
|
||||
summary.append($('<span class="summary-warning"/>').text(`${count_by_status['warning']} ${warning_symbol} Warning`));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var current_privacy_setting = null;
|
||||
|
||||
@@ -14,7 +14,9 @@ def load_env_vars_from_file(fn):
|
||||
# Load settings from a KEY=VALUE file.
|
||||
import collections
|
||||
env = collections.OrderedDict()
|
||||
for line in open(fn): env.setdefault(*line.strip().split("=", 1))
|
||||
with open(fn, 'r') as f:
|
||||
for line in f:
|
||||
env.setdefault(*line.strip().split("=", 1))
|
||||
return env
|
||||
|
||||
def save_environment(env):
|
||||
@@ -34,7 +36,8 @@ def load_settings(env):
|
||||
import rtyaml
|
||||
fn = os.path.join(env['STORAGE_ROOT'], 'settings.yaml')
|
||||
try:
|
||||
config = rtyaml.load(open(fn, "r"))
|
||||
with open(fn, "r") as f:
|
||||
config = rtyaml.load(f)
|
||||
if not isinstance(config, dict): raise ValueError() # caught below
|
||||
return config
|
||||
except:
|
||||
|
||||
@@ -68,7 +68,8 @@ def get_web_domains_with_root_overrides(env):
|
||||
root_overrides = { }
|
||||
nginx_conf_custom_fn = os.path.join(env["STORAGE_ROOT"], "www/custom.yaml")
|
||||
if os.path.exists(nginx_conf_custom_fn):
|
||||
custom_settings = rtyaml.load(open(nginx_conf_custom_fn))
|
||||
with open(nginx_conf_custom_fn, 'r') as f:
|
||||
custom_settings = rtyaml.load(f)
|
||||
for domain, settings in custom_settings.items():
|
||||
for type, value in [('redirect', settings.get('redirects', {}).get('/')),
|
||||
('proxy', settings.get('proxies', {}).get('/'))]:
|
||||
@@ -80,14 +81,19 @@ def do_web_update(env):
|
||||
# Pre-load what SSL certificates we will use for each domain.
|
||||
ssl_certificates = get_ssl_certificates(env)
|
||||
|
||||
# Helper for reading config files and templates
|
||||
def read_conf(conf_fn):
|
||||
with open(os.path.join(os.path.dirname(__file__), "../conf", conf_fn), "r") as f:
|
||||
return f.read()
|
||||
|
||||
# Build an nginx configuration file.
|
||||
nginx_conf = open(os.path.join(os.path.dirname(__file__), "../conf/nginx-top.conf")).read()
|
||||
nginx_conf = read_conf("nginx-top.conf")
|
||||
nginx_conf = re.sub("{{phpver}}", get_php_version(), nginx_conf)
|
||||
|
||||
# Load the templates.
|
||||
template0 = open(os.path.join(os.path.dirname(__file__), "../conf/nginx.conf")).read()
|
||||
template1 = open(os.path.join(os.path.dirname(__file__), "../conf/nginx-alldomains.conf")).read()
|
||||
template2 = open(os.path.join(os.path.dirname(__file__), "../conf/nginx-primaryonly.conf")).read()
|
||||
template0 = read_conf("nginx.conf")
|
||||
template1 = read_conf("nginx-alldomains.conf")
|
||||
template2 = read_conf("nginx-primaryonly.conf")
|
||||
template3 = "\trewrite ^(.*) https://$REDIRECT_DOMAIN$1 permanent;\n"
|
||||
template4 = open(os.path.join(os.path.dirname(__file__), "../conf/nginx-webonlydomains.conf")).read()
|
||||
|
||||
@@ -153,11 +159,8 @@ def make_domain_config(domain, templates, ssl_certificates, env):
|
||||
def hashfile(filepath):
|
||||
import hashlib
|
||||
sha1 = hashlib.sha1()
|
||||
f = open(filepath, 'rb')
|
||||
try:
|
||||
with open(filepath, 'rb') as f:
|
||||
sha1.update(f.read())
|
||||
finally:
|
||||
f.close()
|
||||
return sha1.hexdigest()
|
||||
nginx_conf_extra += "\t# ssl files sha1: %s / %s\n" % (hashfile(tls_cert["private-key"]), hashfile(tls_cert["certificate"]))
|
||||
|
||||
@@ -165,7 +168,8 @@ def make_domain_config(domain, templates, ssl_certificates, env):
|
||||
hsts = "yes"
|
||||
nginx_conf_custom_fn = os.path.join(env["STORAGE_ROOT"], "www/custom.yaml")
|
||||
if os.path.exists(nginx_conf_custom_fn):
|
||||
yaml = rtyaml.load(open(nginx_conf_custom_fn))
|
||||
with open(nginx_conf_custom_fn, 'r') as f:
|
||||
yaml = rtyaml.load(f)
|
||||
if domain in yaml:
|
||||
yaml = yaml[domain]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user