mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2026-03-12 17:07:23 +01:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba75ff7820 | ||
|
|
a14b17794b | ||
|
|
7c9f3e0b23 | ||
|
|
83d8dbca3e | ||
|
|
cdd0a821eb | ||
|
|
81b5af6b64 | ||
|
|
fc5cc9753b | ||
|
|
1aca6fe08f | ||
|
|
cf3e1cd595 | ||
|
|
b044dda28f | ||
|
|
f66f39b61d | ||
|
|
6de7d59f14 | ||
|
|
9c8f2e75fc | ||
|
|
cbc4bf553d | ||
|
|
4e3cfead46 | ||
|
|
8844a9185f | ||
|
|
3249a55f3a | ||
|
|
b58fb54725 | ||
|
|
82903cd09e | ||
|
|
fb14e30feb | ||
|
|
d9ac321f25 | ||
|
|
bf5e9200f8 | ||
|
|
5f5f00af4a | ||
|
|
6b73bb5d80 | ||
|
|
3055f9a79c | ||
|
|
1c84e0aeb6 | ||
|
|
ae1b56d23f | ||
|
|
946cd63e8e | ||
|
|
01fa8cf72c | ||
|
|
fac8477ba1 | ||
|
|
61744095a8 | ||
|
|
d5b38a27e6 | ||
|
|
6666d28c44 | ||
|
|
66675ff2e9 | ||
|
|
9ee2d946b7 | ||
|
|
ff7d4196a6 | ||
|
|
490b36d86c | ||
|
|
867d9c4669 | ||
|
|
1ad5892acd | ||
|
|
69bd137b4e | ||
|
|
736b3de221 | ||
|
|
42f2e983e5 | ||
|
|
c9f30e8059 |
49
CHANGELOG.md
49
CHANGELOG.md
@@ -1,6 +1,55 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
v0.19b (August 20, 2016)
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
This update corrects a security issue introduced in v0.18.
|
||||||
|
|
||||||
|
A remote code execution vulnerability is corrected in how the munin system monitoring graphs are generated for the control panel. The vulnerability involves an administrative user visiting a carefully crafted URL.
|
||||||
|
|
||||||
|
v0.19a (August 18, 2016)
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
This update corrects a security issue in v0.19.
|
||||||
|
|
||||||
|
* fail2ban won't start if Roundcube had not yet been used - new installations probably do not have fail2ban running.
|
||||||
|
|
||||||
|
v0.19 (August 13, 2016)
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Mail:
|
||||||
|
|
||||||
|
* Roundcube is updated to version 1.2.1.
|
||||||
|
* SSLv3 and RC4 are now no longer supported in incoming and outgoing mail (SMTP port 25).
|
||||||
|
|
||||||
|
Control panel:
|
||||||
|
|
||||||
|
* The users and aliases APIs are now documented on their control panel pages.
|
||||||
|
* The HSTS header was missing.
|
||||||
|
* New status checks were added for the ufw firewall.
|
||||||
|
|
||||||
|
DNS:
|
||||||
|
|
||||||
|
* Add SRV records for CardDAV/CalDAV to facilitate autoconfiguration (e.g. in DavDroid, whose latest version didn't seem to work to configure with entering just a hostname).
|
||||||
|
|
||||||
|
System:
|
||||||
|
|
||||||
|
* fail2ban jails added for SMTP submission, Roundcube, ownCloud, the control panel, and munin.
|
||||||
|
* Mail-in-a-Box can now be installed on the i686 architecture.
|
||||||
|
|
||||||
|
v0.18c (June 2, 2016)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
* Domain aliases (and misconfigured aliases/catch-alls with non-existent local targets) would accept mail and deliver it to new mailbox folders on disk even if the target address didn't correspond with an existing mail user, instead of rejecting the mail. This issue was introduced in v0.18.
|
||||||
|
* The Munin Monitoring link in the control panel now opens a new window.
|
||||||
|
* Added an undocumented before-backup script.
|
||||||
|
|
||||||
|
v0.18b (May 16, 2016)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
* Fixed a Roundcube user accounts issue introduced in v0.18.
|
||||||
|
|
||||||
v0.18 (May 15, 2016)
|
v0.18 (May 15, 2016)
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ by me:
|
|||||||
$ curl -s https://keybase.io/joshdata/key.asc | gpg --import
|
$ curl -s https://keybase.io/joshdata/key.asc | gpg --import
|
||||||
gpg: key C10BDD81: public key "Joshua Tauberer <jt@occams.info>" imported
|
gpg: key C10BDD81: public key "Joshua Tauberer <jt@occams.info>" imported
|
||||||
|
|
||||||
$ git verify-tag v0.18
|
$ git verify-tag v0.19b
|
||||||
gpg: Signature made ..... using RSA key ID C10BDD81
|
gpg: Signature made ..... using RSA key ID C10BDD81
|
||||||
gpg: Good signature from "Joshua Tauberer <jt@occams.info>"
|
gpg: Good signature from "Joshua Tauberer <jt@occams.info>"
|
||||||
gpg: WARNING: This key is not certified with a trusted signature!
|
gpg: WARNING: This key is not certified with a trusted signature!
|
||||||
@@ -72,7 +72,7 @@ and on my [personal homepage](https://razor.occams.info/). (Of course, if this r
|
|||||||
|
|
||||||
Checkout the tag corresponding to the most recent release:
|
Checkout the tag corresponding to the most recent release:
|
||||||
|
|
||||||
$ git checkout v0.17c
|
$ git checkout v0.19b
|
||||||
|
|
||||||
Begin the installation.
|
Begin the installation.
|
||||||
|
|
||||||
|
|||||||
12
conf/fail2ban/filter.d/miab-management-daemon.conf
Normal file
12
conf/fail2ban/filter.d/miab-management-daemon.conf
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Fail2Ban filter Mail-in-a-Box management daemon
|
||||||
|
|
||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
before = common.conf
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
_daemon = mailinabox
|
||||||
|
|
||||||
|
failregex = Mail-in-a-Box Management Daemon: Failed login attempt from ip <HOST> - timestamp .*
|
||||||
|
ignoreregex =
|
||||||
7
conf/fail2ban/filter.d/miab-munin.conf
Normal file
7
conf/fail2ban/filter.d/miab-munin.conf
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
before = common.conf
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
failregex=<HOST> - .*GET /admin/munin/.* HTTP/1.1\" 401.*
|
||||||
|
ignoreregex =
|
||||||
7
conf/fail2ban/filter.d/miab-owncloud.conf
Normal file
7
conf/fail2ban/filter.d/miab-owncloud.conf
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
before = common.conf
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
failregex=Login failed: .*Remote IP: '<HOST>[\)']
|
||||||
|
ignoreregex =
|
||||||
7
conf/fail2ban/filter.d/miab-postfix-submission.conf
Normal file
7
conf/fail2ban/filter.d/miab-postfix-submission.conf
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
before = common.conf
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
failregex=postfix/submission/smtpd.*warning.*\[<HOST>\]: .* authentication (failed|aborted)
|
||||||
|
ignoreregex =
|
||||||
9
conf/fail2ban/filter.d/miab-roundcube.conf
Normal file
9
conf/fail2ban/filter.d/miab-roundcube.conf
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[INCLUDES]
|
||||||
|
|
||||||
|
before = common.conf
|
||||||
|
|
||||||
|
[Definition]
|
||||||
|
|
||||||
|
failregex = IMAP Error: Login failed for .*? from <HOST>\. AUTHENTICATE.*
|
||||||
|
|
||||||
|
ignoreregex =
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
# Fail2Ban configuration file for Mail-in-a-Box
|
# Fail2Ban configuration file for Mail-in-a-Box. Do not edit.
|
||||||
|
# This file is re-generated on updates.
|
||||||
|
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
# Whitelist our own IP addresses. 127.0.0.1/8 is the default. But our status checks
|
# Whitelist our own IP addresses. 127.0.0.1/8 is the default. But our status checks
|
||||||
@@ -6,24 +7,53 @@
|
|||||||
# ours too. The string is substituted during installation.
|
# ours too. The string is substituted during installation.
|
||||||
ignoreip = 127.0.0.1/8 PUBLIC_IP
|
ignoreip = 127.0.0.1/8 PUBLIC_IP
|
||||||
|
|
||||||
# JAILS
|
|
||||||
|
|
||||||
[ssh]
|
|
||||||
maxretry = 7
|
|
||||||
bantime = 3600
|
|
||||||
|
|
||||||
[ssh-ddos]
|
|
||||||
enabled = true
|
|
||||||
|
|
||||||
[sasl]
|
|
||||||
enabled = true
|
|
||||||
|
|
||||||
[dovecot]
|
[dovecot]
|
||||||
enabled = true
|
enabled = true
|
||||||
filter = dovecotimap
|
filter = dovecotimap
|
||||||
|
logpath = /var/log/mail.log
|
||||||
findtime = 30
|
findtime = 30
|
||||||
maxretry = 20
|
maxretry = 20
|
||||||
|
|
||||||
|
[miab-management]
|
||||||
|
enabled = true
|
||||||
|
filter = miab-management-daemon
|
||||||
|
port = http,https
|
||||||
|
logpath = /var/log/syslog
|
||||||
|
maxretry = 20
|
||||||
|
findtime = 30
|
||||||
|
|
||||||
|
[miab-munin]
|
||||||
|
enabled = true
|
||||||
|
port = http,https
|
||||||
|
filter = miab-munin
|
||||||
|
logpath = /var/log/nginx/access.log
|
||||||
|
maxretry = 20
|
||||||
|
findtime = 30
|
||||||
|
|
||||||
|
[miab-owncloud]
|
||||||
|
enabled = true
|
||||||
|
port = http,https
|
||||||
|
filter = miab-owncloud
|
||||||
|
logpath = STORAGE_ROOT/owncloud/owncloud.log
|
||||||
|
maxretry = 20
|
||||||
|
findtime = 120
|
||||||
|
|
||||||
|
[miab-postfix587]
|
||||||
|
enabled = true
|
||||||
|
port = 587
|
||||||
|
filter = miab-postfix-submission
|
||||||
|
logpath = /var/log/mail.log
|
||||||
|
maxretry = 20
|
||||||
|
findtime = 30
|
||||||
|
|
||||||
|
[miab-roundcube]
|
||||||
|
enabled = true
|
||||||
|
port = http,https
|
||||||
|
filter = miab-roundcube
|
||||||
|
logpath = /var/log/roundcubemail/errors
|
||||||
|
maxretry = 20
|
||||||
|
findtime = 30
|
||||||
|
|
||||||
[recidive]
|
[recidive]
|
||||||
enabled = true
|
enabled = true
|
||||||
maxretry = 10
|
maxretry = 10
|
||||||
@@ -38,3 +68,13 @@ action = iptables-allports[name=recidive]
|
|||||||
# By default we don't configure this address and no action is required from the admin anyway.
|
# By default we don't configure this address and no action is required from the admin anyway.
|
||||||
# So the notification is ommited. This will prevent message appearing in the mail.log that mail
|
# So the notification is ommited. This will prevent message appearing in the mail.log that mail
|
||||||
# can't be delivered to fail2ban@$HOSTNAME.
|
# can't be delivered to fail2ban@$HOSTNAME.
|
||||||
|
|
||||||
|
[sasl]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[ssh]
|
||||||
|
maxretry = 7
|
||||||
|
bantime = 3600
|
||||||
|
|
||||||
|
[ssh-ddos]
|
||||||
|
enabled = true
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
add_header X-Frame-Options "DENY";
|
add_header X-Frame-Options "DENY";
|
||||||
add_header X-Content-Type-Options nosniff;
|
add_header X-Content-Type-Options nosniff;
|
||||||
add_header Content-Security-Policy "frame-ancestors 'none';";
|
add_header Content-Security-Policy "frame-ancestors 'none';";
|
||||||
|
add_header Strict-Transport-Security max-age=31536000;
|
||||||
}
|
}
|
||||||
|
|
||||||
# ownCloud configuration.
|
# ownCloud configuration.
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
# This script performs a backup of all user data:
|
# This script performs a backup of all user data:
|
||||||
# 1) System services are stopped.
|
# 1) System services are stopped.
|
||||||
# 2) An incremental encrypted backup is made using duplicity.
|
# 2) STORAGE_ROOT/backup/before-backup is executed if it exists.
|
||||||
# 3) The stopped services are restarted.
|
# 3) An incremental encrypted backup is made using duplicity.
|
||||||
# 4) STORAGE_ROOT/backup/after-backup is executd if it exists.
|
# 4) The stopped services are restarted.
|
||||||
|
# 5) STORAGE_ROOT/backup/after-backup is executed if it exists.
|
||||||
|
|
||||||
import os, os.path, shutil, glob, re, datetime, sys
|
import os, os.path, shutil, glob, re, datetime, sys
|
||||||
import dateutil.parser, dateutil.relativedelta, dateutil.tz
|
import dateutil.parser, dateutil.relativedelta, dateutil.tz
|
||||||
@@ -258,6 +259,15 @@ def perform_backup(full_backup):
|
|||||||
service_command("postfix", "stop", quit=True)
|
service_command("postfix", "stop", quit=True)
|
||||||
service_command("dovecot", "stop", quit=True)
|
service_command("dovecot", "stop", quit=True)
|
||||||
|
|
||||||
|
# Execute a pre-backup script that copies files outside the homedir.
|
||||||
|
# Run as the STORAGE_USER user, not as root. Pass our settings in
|
||||||
|
# environment variables so the script has access to STORAGE_ROOT.
|
||||||
|
pre_script = os.path.join(backup_root, 'before-backup')
|
||||||
|
if os.path.exists(pre_script):
|
||||||
|
shell('check_call',
|
||||||
|
['su', env['STORAGE_USER'], '-c', pre_script, config["target"]],
|
||||||
|
env=env)
|
||||||
|
|
||||||
# Run a backup of STORAGE_ROOT (but excluding the backups themselves!).
|
# Run a backup of STORAGE_ROOT (but excluding the backups themselves!).
|
||||||
# --allow-source-mismatch is needed in case the box's hostname is changed
|
# --allow-source-mismatch is needed in case the box's hostname is changed
|
||||||
# after the first backup. See #396.
|
# after the first backup. See #396.
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import os, os.path, re, json
|
import os, os.path, re, json, time
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from flask import Flask, request, render_template, abort, Response, send_from_directory, make_response
|
from flask import Flask, request, render_template, abort, Response, send_from_directory, make_response
|
||||||
@@ -45,6 +46,9 @@ def authorized_personnel_only(viewfunc):
|
|||||||
privs = []
|
privs = []
|
||||||
error = "Incorrect username or password"
|
error = "Incorrect username or password"
|
||||||
|
|
||||||
|
# Write a line in the log recording the failed login
|
||||||
|
log_failed_login(request)
|
||||||
|
|
||||||
# Authorized to access an API view?
|
# Authorized to access an API view?
|
||||||
if "admin" in privs:
|
if "admin" in privs:
|
||||||
# Call view func.
|
# Call view func.
|
||||||
@@ -117,6 +121,9 @@ def me():
|
|||||||
try:
|
try:
|
||||||
email, privs = auth_service.authenticate(request, env)
|
email, privs = auth_service.authenticate(request, env)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
|
# Log the failed login
|
||||||
|
log_failed_login(request)
|
||||||
|
|
||||||
return json_response({
|
return json_response({
|
||||||
"status": "invalid",
|
"status": "invalid",
|
||||||
"reason": "Incorrect username or password",
|
"reason": "Incorrect username or password",
|
||||||
@@ -534,10 +541,9 @@ def munin_cgi(filename):
|
|||||||
headers based on parameters in the requesting URL. All output is written
|
headers based on parameters in the requesting URL. All output is written
|
||||||
to stdout which munin_cgi splits into response headers and binary response
|
to stdout which munin_cgi splits into response headers and binary response
|
||||||
data.
|
data.
|
||||||
munin-cgi-graph reads environment variables as well as passed input to determine
|
munin-cgi-graph reads environment variables to determine
|
||||||
what it should do. It expects a path to be in the env-var PATH_INFO, and a
|
what it should do. It expects a path to be in the env-var PATH_INFO, and a
|
||||||
querystring to be in the env-var QUERY_STRING as well as passed as input to the
|
querystring to be in the env-var QUERY_STRING.
|
||||||
command.
|
|
||||||
munin-cgi-graph has several failure modes. Some write HTTP Status headers and
|
munin-cgi-graph has several failure modes. Some write HTTP Status headers and
|
||||||
others return nonzero exit codes.
|
others return nonzero exit codes.
|
||||||
Situating munin_cgi between the user-agent and munin-cgi-graph enables keeping
|
Situating munin_cgi between the user-agent and munin-cgi-graph enables keeping
|
||||||
@@ -545,7 +551,7 @@ def munin_cgi(filename):
|
|||||||
support infrastructure like spawn-fcgi.
|
support infrastructure like spawn-fcgi.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
COMMAND = 'su - munin --preserve-environment --shell=/bin/bash -c /usr/lib/munin/cgi/munin-cgi-graph "%s"'
|
COMMAND = 'su - munin --preserve-environment --shell=/bin/bash -c /usr/lib/munin/cgi/munin-cgi-graph'
|
||||||
# su changes user, we use the munin user here
|
# su changes user, we use the munin user here
|
||||||
# --preserve-environment retains the environment, which is where Popen's `env` data is
|
# --preserve-environment retains the environment, which is where Popen's `env` data is
|
||||||
# --shell=/bin/bash ensures the shell used is bash
|
# --shell=/bin/bash ensures the shell used is bash
|
||||||
@@ -557,12 +563,10 @@ def munin_cgi(filename):
|
|||||||
|
|
||||||
query_str = request.query_string.decode("utf-8", 'ignore')
|
query_str = request.query_string.decode("utf-8", 'ignore')
|
||||||
|
|
||||||
env = {'PATH_INFO': '/%s/' % filename, 'QUERY_STRING': query_str}
|
env = {'PATH_INFO': '/%s/' % filename, 'REQUEST_METHOD': 'GET', 'QUERY_STRING': query_str}
|
||||||
cmd = COMMAND % query_str
|
|
||||||
code, binout = utils.shell('check_output',
|
code, binout = utils.shell('check_output',
|
||||||
cmd.split(' ', 5),
|
COMMAND.split(" ", 5),
|
||||||
# Using a maxsplit of 5 keeps the last 2 arguments together
|
# Using a maxsplit of 5 keeps the last arguments together
|
||||||
input=query_str.encode('UTF-8'),
|
|
||||||
env=env,
|
env=env,
|
||||||
return_bytes=True,
|
return_bytes=True,
|
||||||
trap=True)
|
trap=True)
|
||||||
@@ -583,6 +587,22 @@ def munin_cgi(filename):
|
|||||||
app.logger.warning("munin_cgi: munin-cgi-graph returned 404 status code. PATH_INFO=%s", env['PATH_INFO'])
|
app.logger.warning("munin_cgi: munin-cgi-graph returned 404 status code. PATH_INFO=%s", env['PATH_INFO'])
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def log_failed_login(request):
|
||||||
|
# We need to figure out the ip to list in the message, all our calls are routed
|
||||||
|
# through nginx who will put the original ip in X-Forwarded-For.
|
||||||
|
# During setup we call the management interface directly to determine the user
|
||||||
|
# status. So we can't always use X-Forwarded-For because during setup that header
|
||||||
|
# will not be present.
|
||||||
|
if request.headers.getlist("X-Forwarded-For"):
|
||||||
|
ip = request.headers.getlist("X-Forwarded-For")[0]
|
||||||
|
else:
|
||||||
|
ip = request.remote_addr
|
||||||
|
|
||||||
|
# We need to add a timestamp to the log message, otherwise /dev/log will eat the "duplicate"
|
||||||
|
# message.
|
||||||
|
app.logger.warning( "Mail-in-a-Box Management Daemon: Failed login attempt from ip %s - timestamp %s" % (ip, time.time()))
|
||||||
|
|
||||||
|
|
||||||
# APP
|
# APP
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -274,6 +274,13 @@ def build_zone(domain, all_domains, additional_records, www_redirect_domains, en
|
|||||||
if not has_rec(dmarc_qname, "TXT", prefix="v=DMARC1; "):
|
if not has_rec(dmarc_qname, "TXT", prefix="v=DMARC1; "):
|
||||||
records.append((dmarc_qname, "TXT", 'v=DMARC1; p=reject', "Recommended. Prevents use of this domain name for outbound mail by specifying that the SPF rule should be honoured for mail from @%s." % (qname + "." + domain)))
|
records.append((dmarc_qname, "TXT", 'v=DMARC1; p=reject', "Recommended. Prevents use of this domain name for outbound mail by specifying that the SPF rule should be honoured for mail from @%s." % (qname + "." + domain)))
|
||||||
|
|
||||||
|
# Add CardDAV/CalDAV SRV records on the non-primary hostname that points to the primary hostname.
|
||||||
|
# The SRV record format is priority (0, whatever), weight (0, whatever), port, service provider hostname (w/ trailing dot).
|
||||||
|
if domain != env["PRIMARY_HOSTNAME"]:
|
||||||
|
for dav in ("card", "cal"):
|
||||||
|
qname = "_" + dav + "davs._tcp"
|
||||||
|
if not has_rec(qname, "SRV"):
|
||||||
|
records.append((qname, "SRV", "0 0 443 " + env["PRIMARY_HOSTNAME"] + ".", "Recommended. Specifies the hostname of the server that handles CardDAV/CalDAV services for email addresses on this domain."))
|
||||||
|
|
||||||
# Sort the records. The None records *must* go first in the nsd zone file. Otherwise it doesn't matter.
|
# Sort the records. The None records *must* go first in the nsd zone file. Otherwise it doesn't matter.
|
||||||
records.sort(key = lambda rec : list(reversed(rec[0].split(".")) if rec[0] is not None else ""))
|
records.sort(key = lambda rec : list(reversed(rec[0].split(".")) if rec[0] is not None else ""))
|
||||||
|
|||||||
@@ -1,136 +1,211 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import re, os.path
|
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
|
|
||||||
import mailconfig
|
import mailconfig
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
|
|
||||||
def scan_mail_log(logger, env):
|
def scan_mail_log(logger, env):
|
||||||
collector = {
|
""" Scan the system's mail log files and collect interesting data
|
||||||
"other-services": set(),
|
|
||||||
"imap-logins": { },
|
|
||||||
"postgrey": { },
|
|
||||||
"rejected-mail": { },
|
|
||||||
"activity-by-hour": { "imap-logins": defaultdict(int), "smtp-sends": defaultdict(int) },
|
|
||||||
}
|
|
||||||
|
|
||||||
collector["real_mail_addresses"] = set(mailconfig.get_mail_users(env)) | set(alias[0] for alias in mailconfig.get_mail_aliases(env))
|
This function scans the 2 most recent mail log files in /var/log/.
|
||||||
|
|
||||||
for fn in ('/var/log/mail.log.1', '/var/log/mail.log'):
|
Args:
|
||||||
if not os.path.exists(fn): continue
|
logger (ConsoleOutput): Object used for writing messages to the console
|
||||||
with open(fn, 'rb') as log:
|
env (dict): Dictionary containing MiaB settings
|
||||||
for line in log:
|
"""
|
||||||
line = line.decode("utf8", errors='replace')
|
|
||||||
scan_mail_log_line(line.strip(), collector)
|
|
||||||
|
|
||||||
if collector["imap-logins"]:
|
collector = {
|
||||||
logger.add_heading("Recent IMAP Logins")
|
"other-services": set(),
|
||||||
logger.print_block("The most recent login from each remote IP adddress is show.")
|
"imap-logins": {},
|
||||||
for k in utils.sort_email_addresses(collector["imap-logins"], env):
|
"pop3-logins": {},
|
||||||
for ip, date in sorted(collector["imap-logins"][k].items(), key = lambda kv : kv[1]):
|
"postgrey": {},
|
||||||
logger.print_line(k + "\t" + str(date) + "\t" + ip)
|
"rejected-mail": {},
|
||||||
|
"activity-by-hour": {
|
||||||
|
"imap-logins": defaultdict(int),
|
||||||
|
"pop3-logins": defaultdict(int),
|
||||||
|
"smtp-sends": defaultdict(int),
|
||||||
|
"smtp-receives": defaultdict(int),
|
||||||
|
},
|
||||||
|
"real_mail_addresses": (
|
||||||
|
set(mailconfig.get_mail_users(env)) | set(alias[0] for alias in mailconfig.get_mail_aliases(env))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if collector["postgrey"]:
|
for fn in ('/var/log/mail.log.1', '/var/log/mail.log'):
|
||||||
logger.add_heading("Greylisted Mail")
|
if not os.path.exists(fn):
|
||||||
logger.print_block("The following mail was greylisted, meaning the emails were temporarily rejected. Legitimate senders will try again within ten minutes.")
|
continue
|
||||||
logger.print_line("recipient" + "\t" + "received" + "\t" + "sender" + "\t" + "delivered")
|
with open(fn, 'rb') as log:
|
||||||
for recipient in utils.sort_email_addresses(collector["postgrey"], env):
|
for line in log:
|
||||||
for (client_address, sender), (first_date, delivered_date) in sorted(collector["postgrey"][recipient].items(), key = lambda kv : kv[1][0]):
|
line = line.decode("utf8", errors='replace')
|
||||||
logger.print_line(recipient + "\t" + str(first_date) + "\t" + sender + "\t" + (("delivered " + str(delivered_date)) if delivered_date else "no retry yet"))
|
scan_mail_log_line(line.strip(), collector)
|
||||||
|
|
||||||
if collector["rejected-mail"]:
|
if collector["imap-logins"]:
|
||||||
logger.add_heading("Rejected Mail")
|
logger.add_heading("Recent IMAP Logins")
|
||||||
logger.print_block("The following incoming mail was rejected.")
|
logger.print_block("The most recent login from each remote IP adddress is shown.")
|
||||||
for k in utils.sort_email_addresses(collector["rejected-mail"], env):
|
for k in utils.sort_email_addresses(collector["imap-logins"], env):
|
||||||
for date, sender, message in collector["rejected-mail"][k]:
|
for ip, date in sorted(collector["imap-logins"][k].items(), key=lambda kv: kv[1]):
|
||||||
logger.print_line(k + "\t" + str(date) + "\t" + sender + "\t" + message)
|
logger.print_line(k + "\t" + str(date) + "\t" + ip)
|
||||||
|
|
||||||
logger.add_heading("Activity by Hour")
|
if collector["pop3-logins"]:
|
||||||
for h in range(24):
|
logger.add_heading("Recent POP3 Logins")
|
||||||
logger.print_line("%d\t%d\t%d" % (h, collector["activity-by-hour"]["imap-logins"][h], collector["activity-by-hour"]["smtp-sends"][h] ))
|
logger.print_block("The most recent login from each remote IP adddress is shown.")
|
||||||
|
for k in utils.sort_email_addresses(collector["pop3-logins"], env):
|
||||||
|
for ip, date in sorted(collector["pop3-logins"][k].items(), key=lambda kv: kv[1]):
|
||||||
|
logger.print_line(k + "\t" + str(date) + "\t" + ip)
|
||||||
|
|
||||||
|
if collector["postgrey"]:
|
||||||
|
logger.add_heading("Greylisted Mail")
|
||||||
|
logger.print_block("The following mail was greylisted, meaning the emails were temporarily rejected. "
|
||||||
|
"Legitimate senders will try again within ten minutes.")
|
||||||
|
logger.print_line("recipient" + "\t" + "received" + 3 * "\t" + "sender" + 6 * "\t" + "delivered")
|
||||||
|
for recipient in utils.sort_email_addresses(collector["postgrey"], env):
|
||||||
|
sorted_recipients = sorted(collector["postgrey"][recipient].items(), key=lambda kv: kv[1][0])
|
||||||
|
for (client_address, sender), (first_date, delivered_date) in sorted_recipients:
|
||||||
|
logger.print_line(
|
||||||
|
recipient + "\t" + str(first_date) + "\t" + sender + "\t" +
|
||||||
|
(("delivered " + str(delivered_date)) if delivered_date else "no retry yet")
|
||||||
|
)
|
||||||
|
|
||||||
|
if collector["rejected-mail"]:
|
||||||
|
logger.add_heading("Rejected Mail")
|
||||||
|
logger.print_block("The following incoming mail was rejected.")
|
||||||
|
for k in utils.sort_email_addresses(collector["rejected-mail"], env):
|
||||||
|
for date, sender, message in collector["rejected-mail"][k]:
|
||||||
|
logger.print_line(k + "\t" + str(date) + "\t" + sender + "\t" + message)
|
||||||
|
|
||||||
|
logger.add_heading("Activity by Hour")
|
||||||
|
logger.print_block("Dovecot logins and Postfix mail traffic per hour.")
|
||||||
|
logger.print_block("Hour\tIMAP\tPOP3\tSent\tReceived")
|
||||||
|
for h in range(24):
|
||||||
|
logger.print_line(
|
||||||
|
"%d\t%d\t\t%d\t\t%d\t\t%d" % (
|
||||||
|
h,
|
||||||
|
collector["activity-by-hour"]["imap-logins"][h],
|
||||||
|
collector["activity-by-hour"]["pop3-logins"][h],
|
||||||
|
collector["activity-by-hour"]["smtp-sends"][h],
|
||||||
|
collector["activity-by-hour"]["smtp-receives"][h],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(collector["other-services"]) > 0:
|
||||||
|
logger.add_heading("Other")
|
||||||
|
logger.print_block("Unrecognized services in the log: " + ", ".join(collector["other-services"]))
|
||||||
|
|
||||||
if len(collector["other-services"]) > 0:
|
|
||||||
logger.add_heading("Other")
|
|
||||||
logger.print_block("Unrecognized services in the log: " + ", ".join(collector["other-services"]))
|
|
||||||
|
|
||||||
def scan_mail_log_line(line, collector):
|
def scan_mail_log_line(line, collector):
|
||||||
m = re.match(r"(\S+ \d+ \d+:\d+:\d+) (\S+) (\S+?)(\[\d+\])?: (.*)", line)
|
""" Scan a log line and extract interesting data """
|
||||||
if not m: return
|
|
||||||
|
|
||||||
date, system, service, pid, log = m.groups()
|
m = re.match(r"(\S+ \d+ \d+:\d+:\d+) (\S+) (\S+?)(\[\d+\])?: (.*)", line)
|
||||||
date = dateutil.parser.parse(date)
|
|
||||||
|
|
||||||
if service == "dovecot":
|
|
||||||
scan_dovecot_line(date, log, collector)
|
|
||||||
|
|
||||||
elif service == "postgrey":
|
if not m:
|
||||||
scan_postgrey_line(date, log, collector)
|
return
|
||||||
|
|
||||||
elif service == "postfix/smtpd":
|
date, system, service, pid, log = m.groups()
|
||||||
scan_postfix_smtpd_line(date, log, collector)
|
date = dateutil.parser.parse(date)
|
||||||
|
|
||||||
elif service == "postfix/submission/smtpd":
|
if service == "dovecot":
|
||||||
scan_postfix_submission_line(date, log, collector)
|
scan_dovecot_line(date, log, collector)
|
||||||
|
elif service == "postgrey":
|
||||||
|
scan_postgrey_line(date, log, collector)
|
||||||
|
elif service == "postfix/smtpd":
|
||||||
|
scan_postfix_smtpd_line(date, log, collector)
|
||||||
|
elif service == "postfix/cleanup":
|
||||||
|
scan_postfix_cleanup_line(date, log, collector)
|
||||||
|
elif service == "postfix/submission/smtpd":
|
||||||
|
scan_postfix_submission_line(date, log, collector)
|
||||||
|
elif service in ("postfix/qmgr", "postfix/pickup", "postfix/cleanup", "postfix/scache", "spampd", "postfix/anvil",
|
||||||
|
"postfix/master", "opendkim", "postfix/lmtp", "postfix/tlsmgr"):
|
||||||
|
# nothing to look at
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
collector["other-services"].add(service)
|
||||||
|
|
||||||
elif service in ("postfix/qmgr", "postfix/pickup", "postfix/cleanup",
|
|
||||||
"postfix/scache", "spampd", "postfix/anvil", "postfix/master",
|
|
||||||
"opendkim", "postfix/lmtp", "postfix/tlsmgr"):
|
|
||||||
# nothing to look at
|
|
||||||
pass
|
|
||||||
|
|
||||||
else:
|
def scan_dovecot_line(date, line, collector):
|
||||||
collector["other-services"].add(service)
|
""" Scan a dovecot log line and extract interesting data """
|
||||||
|
|
||||||
|
m = re.match("(imap|pop3)-login: Login: user=<(.*?)>, method=PLAIN, rip=(.*?),", line)
|
||||||
|
|
||||||
|
if m:
|
||||||
|
prot, login, ip = m.group(1), m.group(2), m.group(3)
|
||||||
|
logins_key = "%s-logins" % prot
|
||||||
|
if ip != "127.0.0.1": # local login from webmail/zpush
|
||||||
|
collector[logins_key].setdefault(login, {})[ip] = date
|
||||||
|
collector["activity-by-hour"][logins_key][date.hour] += 1
|
||||||
|
|
||||||
def scan_dovecot_line(date, log, collector):
|
|
||||||
m = re.match("imap-login: Login: user=<(.*?)>, method=PLAIN, rip=(.*?),", log)
|
|
||||||
if m:
|
|
||||||
login, ip = m.group(1), m.group(2)
|
|
||||||
if ip != "127.0.0.1": # local login from webmail/zpush
|
|
||||||
collector["imap-logins"].setdefault(login, {})[ip] = date
|
|
||||||
collector["activity-by-hour"]["imap-logins"][date.hour] += 1
|
|
||||||
|
|
||||||
def scan_postgrey_line(date, log, collector):
|
def scan_postgrey_line(date, log, collector):
|
||||||
m = re.match("action=(greylist|pass), reason=(.*?), (?:delay=\d+, )?client_name=(.*), client_address=(.*), sender=(.*), recipient=(.*)", log)
|
""" Scan a postgrey log line and extract interesting data """
|
||||||
if m:
|
|
||||||
action, reason, client_name, client_address, sender, recipient = m.groups()
|
m = re.match("action=(greylist|pass), reason=(.*?), (?:delay=\d+, )?client_name=(.*), client_address=(.*), "
|
||||||
key = (client_address, sender)
|
"sender=(.*), recipient=(.*)",
|
||||||
if action == "greylist" and reason == "new":
|
log)
|
||||||
collector["postgrey"].setdefault(recipient, {})[key] = (date, None)
|
|
||||||
elif action == "pass" and reason == "triplet found" and key in collector["postgrey"].get(recipient, {}):
|
if m:
|
||||||
collector["postgrey"][recipient][key] = (collector["postgrey"][recipient][key][0], date)
|
action, reason, client_name, client_address, sender, recipient = m.groups()
|
||||||
|
key = (client_address, sender)
|
||||||
|
if action == "greylist" and reason == "new":
|
||||||
|
collector["postgrey"].setdefault(recipient, {})[key] = (date, None)
|
||||||
|
elif action == "pass" and reason == "triplet found" and key in collector["postgrey"].get(recipient, {}):
|
||||||
|
collector["postgrey"][recipient][key] = (collector["postgrey"][recipient][key][0], date)
|
||||||
|
|
||||||
|
|
||||||
def scan_postfix_smtpd_line(date, log, collector):
|
def scan_postfix_smtpd_line(date, log, collector):
|
||||||
m = re.match("NOQUEUE: reject: RCPT from .*?: (.*?); from=<(.*?)> to=<(.*?)>", log)
|
""" Scan a postfix smtpd log line and extract interesting data """
|
||||||
if m:
|
|
||||||
message, sender, recipient = m.groups()
|
|
||||||
if recipient in collector["real_mail_addresses"]:
|
|
||||||
# only log mail to real recipients
|
|
||||||
|
|
||||||
# skip this, is reported in the greylisting report
|
# Check if the incomming mail was rejected
|
||||||
if "Recipient address rejected: Greylisted" in message:
|
|
||||||
return
|
|
||||||
|
|
||||||
# simplify this one
|
m = re.match("NOQUEUE: reject: RCPT from .*?: (.*?); from=<(.*?)> to=<(.*?)>", log)
|
||||||
m = re.search(r"Client host \[(.*?)\] blocked using zen.spamhaus.org; (.*)", message)
|
|
||||||
if m:
|
|
||||||
message = "ip blocked: " + m.group(2)
|
|
||||||
|
|
||||||
# simplify this one too
|
if m:
|
||||||
m = re.search(r"Sender address \[.*@(.*)\] blocked using dbl.spamhaus.org; (.*)", message)
|
message, sender, recipient = m.groups()
|
||||||
if m:
|
if recipient in collector["real_mail_addresses"]:
|
||||||
message = "domain blocked: " + m.group(2)
|
# only log mail to real recipients
|
||||||
|
|
||||||
collector["rejected-mail"].setdefault(recipient, []).append( (date, sender, message) )
|
# skip this, if reported in the greylisting report
|
||||||
|
if "Recipient address rejected: Greylisted" in message:
|
||||||
|
return
|
||||||
|
|
||||||
|
# simplify this one
|
||||||
|
m = re.search(r"Client host \[(.*?)\] blocked using zen.spamhaus.org; (.*)", message)
|
||||||
|
if m:
|
||||||
|
message = "ip blocked: " + m.group(2)
|
||||||
|
|
||||||
|
# simplify this one too
|
||||||
|
m = re.search(r"Sender address \[.*@(.*)\] blocked using dbl.spamhaus.org; (.*)", message)
|
||||||
|
if m:
|
||||||
|
message = "domain blocked: " + m.group(2)
|
||||||
|
|
||||||
|
collector["rejected-mail"].setdefault(recipient, []).append((date, sender, message))
|
||||||
|
|
||||||
|
|
||||||
|
def scan_postfix_cleanup_line(date, _, collector):
|
||||||
|
""" Scan a postfix cleanup log line and extract interesting data
|
||||||
|
|
||||||
|
It is assumed that every log of postfix/cleanup indicates an email that was successfulfy received by Postfix.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
collector["activity-by-hour"]["smtp-receives"][date.hour] += 1
|
||||||
|
|
||||||
def scan_postfix_submission_line(date, log, collector):
|
def scan_postfix_submission_line(date, log, collector):
|
||||||
m = re.match("([A-Z0-9]+): client=(\S+), sasl_method=PLAIN, sasl_username=(\S+)", log)
|
""" Scan a postfix submission log line and extract interesting data """
|
||||||
if m:
|
|
||||||
procid, client, user = m.groups()
|
m = re.match("([A-Z0-9]+): client=(\S+), sasl_method=PLAIN, sasl_username=(\S+)", log)
|
||||||
collector["activity-by-hour"]["smtp-sends"][date.hour] += 1
|
|
||||||
|
if m:
|
||||||
|
# procid, client, user = m.groups()
|
||||||
|
collector["activity-by-hour"]["smtp-sends"][date.hour] += 1
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from status_checks import ConsoleOutput
|
from status_checks import ConsoleOutput
|
||||||
env = utils.load_environment()
|
|
||||||
scan_mail_log(ConsoleOutput(), env)
|
env_vars = utils.load_environment()
|
||||||
|
scan_mail_log(ConsoleOutput(), env_vars)
|
||||||
|
|||||||
@@ -18,6 +18,29 @@ from mailconfig import get_mail_domains, get_mail_aliases
|
|||||||
|
|
||||||
from utils import shell, sort_domains, load_env_vars_from_file, load_settings
|
from utils import shell, sort_domains, load_env_vars_from_file, load_settings
|
||||||
|
|
||||||
|
def get_services():
|
||||||
|
return [
|
||||||
|
{ "name": "Local DNS (bind9)", "port": 53, "public": False, },
|
||||||
|
#{ "name": "NSD Control", "port": 8952, "public": False, },
|
||||||
|
{ "name": "Local DNS Control (bind9/rndc)", "port": 953, "public": False, },
|
||||||
|
{ "name": "Dovecot LMTP LDA", "port": 10026, "public": False, },
|
||||||
|
{ "name": "Postgrey", "port": 10023, "public": False, },
|
||||||
|
{ "name": "Spamassassin", "port": 10025, "public": False, },
|
||||||
|
{ "name": "OpenDKIM", "port": 8891, "public": False, },
|
||||||
|
{ "name": "OpenDMARC", "port": 8893, "public": False, },
|
||||||
|
{ "name": "Memcached", "port": 11211, "public": False, },
|
||||||
|
{ "name": "Mail-in-a-Box Management Daemon", "port": 10222, "public": False, },
|
||||||
|
{ "name": "SSH Login (ssh)", "port": get_ssh_port(), "public": True, },
|
||||||
|
{ "name": "Public DNS (nsd4)", "port": 53, "public": True, },
|
||||||
|
{ "name": "Incoming Mail (SMTP/postfix)", "port": 25, "public": True, },
|
||||||
|
{ "name": "Outgoing Mail (SMTP 587/postfix)", "port": 587, "public": True, },
|
||||||
|
#{ "name": "Postfix/master", "port": 10587, "public": True, },
|
||||||
|
{ "name": "IMAPS (dovecot)", "port": 993, "public": True, },
|
||||||
|
{ "name": "Mail Filters (Sieve/dovecot)", "port": 4190, "public": True, },
|
||||||
|
{ "name": "HTTP Web (nginx)", "port": 80, "public": True, },
|
||||||
|
{ "name": "HTTPS Web (nginx)", "port": 443, "public": True, },
|
||||||
|
]
|
||||||
|
|
||||||
def run_checks(rounded_values, env, output, pool):
|
def run_checks(rounded_values, env, output, pool):
|
||||||
# run systems checks
|
# run systems checks
|
||||||
output.add_heading("System")
|
output.add_heading("System")
|
||||||
@@ -61,33 +84,9 @@ def get_ssh_port():
|
|||||||
|
|
||||||
def run_services_checks(env, output, pool):
|
def run_services_checks(env, output, pool):
|
||||||
# Check that system services are running.
|
# Check that system services are running.
|
||||||
|
|
||||||
services = [
|
|
||||||
{ "name": "Local DNS (bind9)", "port": 53, "public": False, },
|
|
||||||
#{ "name": "NSD Control", "port": 8952, "public": False, },
|
|
||||||
{ "name": "Local DNS Control (bind9/rndc)", "port": 953, "public": False, },
|
|
||||||
{ "name": "Dovecot LMTP LDA", "port": 10026, "public": False, },
|
|
||||||
{ "name": "Postgrey", "port": 10023, "public": False, },
|
|
||||||
{ "name": "Spamassassin", "port": 10025, "public": False, },
|
|
||||||
{ "name": "OpenDKIM", "port": 8891, "public": False, },
|
|
||||||
{ "name": "OpenDMARC", "port": 8893, "public": False, },
|
|
||||||
{ "name": "Memcached", "port": 11211, "public": False, },
|
|
||||||
{ "name": "Mail-in-a-Box Management Daemon", "port": 10222, "public": False, },
|
|
||||||
|
|
||||||
{ "name": "SSH Login (ssh)", "port": get_ssh_port(), "public": True, },
|
|
||||||
{ "name": "Public DNS (nsd4)", "port": 53, "public": True, },
|
|
||||||
{ "name": "Incoming Mail (SMTP/postfix)", "port": 25, "public": True, },
|
|
||||||
{ "name": "Outgoing Mail (SMTP 587/postfix)", "port": 587, "public": True, },
|
|
||||||
#{ "name": "Postfix/master", "port": 10587, "public": True, },
|
|
||||||
{ "name": "IMAPS (dovecot)", "port": 993, "public": True, },
|
|
||||||
{ "name": "Mail Filters (Sieve/dovecot)", "port": 4190, "public": True, },
|
|
||||||
{ "name": "HTTP Web (nginx)", "port": 80, "public": True, },
|
|
||||||
{ "name": "HTTPS Web (nginx)", "port": 443, "public": True, },
|
|
||||||
]
|
|
||||||
|
|
||||||
all_running = True
|
all_running = True
|
||||||
fatal = False
|
fatal = False
|
||||||
ret = pool.starmap(check_service, ((i, service, env) for i, service in enumerate(services)), chunksize=1)
|
ret = pool.starmap(check_service, ((i, service, env) for i, service in enumerate(get_services())), chunksize=1)
|
||||||
for i, running, fatal2, output2 in sorted(ret):
|
for i, running, fatal2, output2 in sorted(ret):
|
||||||
if output2 is None: continue # skip check (e.g. no port was set, e.g. no sshd)
|
if output2 is None: continue # skip check (e.g. no port was set, e.g. no sshd)
|
||||||
all_running = all_running and running
|
all_running = all_running and running
|
||||||
@@ -169,6 +168,26 @@ def run_system_checks(rounded_values, env, output):
|
|||||||
check_free_disk_space(rounded_values, env, output)
|
check_free_disk_space(rounded_values, env, output)
|
||||||
check_free_memory(rounded_values, env, output)
|
check_free_memory(rounded_values, env, output)
|
||||||
|
|
||||||
|
def check_ufw(env, output):
|
||||||
|
ufw = shell('check_output', ['ufw', 'status']).splitlines()
|
||||||
|
|
||||||
|
if ufw[0] == "Status: active":
|
||||||
|
not_allowed_ports = 0
|
||||||
|
for service in get_services():
|
||||||
|
if service["public"] and not is_port_allowed(ufw, service["port"]):
|
||||||
|
not_allowed_ports += 1
|
||||||
|
output.print_error("Port %s (%s) should be allowed in the firewall, please re-run the setup." % (service["port"], service["name"]))
|
||||||
|
|
||||||
|
if not_allowed_ports == 0:
|
||||||
|
output.print_ok("Firewall is active.")
|
||||||
|
else:
|
||||||
|
output.print_warning("""The firewall is disabled on this machine. This might be because the system
|
||||||
|
is protected by an external firewall. We can't protect the system against bruteforce attacks
|
||||||
|
without the local firewall active. Connect to the system via ssh and try to run: ufw enable.""")
|
||||||
|
|
||||||
|
def is_port_allowed(ufw, port):
|
||||||
|
return any(re.match(str(port) +"[/ \t].*", item) for item in ufw)
|
||||||
|
|
||||||
def check_ssh_password(env, output):
|
def check_ssh_password(env, output):
|
||||||
# Check that SSH login with password is disabled. The openssh-server
|
# Check that SSH login with password is disabled. The openssh-server
|
||||||
# package may not be installed so check that before trying to access
|
# package may not be installed so check that before trying to access
|
||||||
@@ -240,6 +259,8 @@ def run_network_checks(env, output):
|
|||||||
|
|
||||||
output.add_heading("Network")
|
output.add_heading("Network")
|
||||||
|
|
||||||
|
check_ufw(env, output)
|
||||||
|
|
||||||
# Stop if we cannot make an outbound connection on port 25. Many residential
|
# Stop if we cannot make an outbound connection on port 25. Many residential
|
||||||
# networks block outbound port 25 to prevent their network from sending spam.
|
# networks block outbound port 25 to prevent their network from sending spam.
|
||||||
# See if we can reach one of Google's MTAs with a 5-second timeout.
|
# See if we can reach one of Google's MTAs with a 5-second timeout.
|
||||||
|
|||||||
@@ -106,6 +106,41 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h3>Mail aliases API (advanced)</h3>
|
||||||
|
|
||||||
|
<p>Use your box’s mail aliases API to add and remove mail aliases from the command-line or custom services you build.</p>
|
||||||
|
|
||||||
|
<p>Usage:</p>
|
||||||
|
|
||||||
|
<pre>curl -X <b>VERB</b> [-d "<b>parameters</b>"] --user {email}:{password} https://{{hostname}}/admin/mail/aliases[<b>action</b>]</pre>
|
||||||
|
|
||||||
|
<p>Brackets denote an optional argument. Please note that the POST body <code>parameters</code> must be URL-encoded.</p>
|
||||||
|
|
||||||
|
<p>The email and password given to the <code>--user</code> option must be an administrative user on this system.</p>
|
||||||
|
|
||||||
|
<h4 style="margin-bottom: 0">Verbs</h4>
|
||||||
|
|
||||||
|
<table class="table" style="margin-top: .5em">
|
||||||
|
<thead><th>Verb</th> <th>Action</th><th></th></thead>
|
||||||
|
<tr><td>GET</td><td><i>(none)</i></td> <td>Returns a list of existing mail aliases. Adding <code>?format=json</code> to the URL will give JSON-encoded results.</td></tr>
|
||||||
|
<tr><td>POST</td><td>/add</td> <td>Adds a new mail alias. Required POST-body parameters are <code>address</code> and <code>forward_to</code>.</td></tr>
|
||||||
|
<tr><td>POST</td><td>/remove</td> <td>Removes a mail alias. Required POST-body parameter is <code>address</code>.</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h4>Examples:</h4>
|
||||||
|
|
||||||
|
<p>Try these examples. For simplicity the examples omit the <code>--user me@mydomain.com:yourpassword</code> command line argument which you must fill in with your email address and password.</p>
|
||||||
|
|
||||||
|
<pre># Gives a JSON-encoded list of all mail aliases
|
||||||
|
curl -X GET https://{{hostname}}/admin/mail/aliases?format=json
|
||||||
|
|
||||||
|
# Adds a new alias
|
||||||
|
curl -X POST -d "address=new_alias@mydomail.com" -d "forward_to=my_email@mydomain.com" https://{{hostname}}/admin/mail/aliases/add
|
||||||
|
|
||||||
|
# Removes an alias
|
||||||
|
curl -X POST -d "address=new_alias@mydomail.com" https://{{hostname}}/admin/mail/aliases/remove
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function show_aliases() {
|
function show_aliases() {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
<p>It is possible to set custom DNS records on domains hosted here.</p>
|
<p>It is possible to set custom DNS records on domains hosted here.</p>
|
||||||
|
|
||||||
<h3>Set Custom DNS Records</h3>
|
<h3>Set custom DNS records</h3>
|
||||||
|
|
||||||
<p>You can set additional DNS records, such as if you have a website running on another server, to add DKIM records for external mail providers, or for various confirmation-of-ownership tests.</p>
|
<p>You can set additional DNS records, such as if you have a website running on another server, to add DKIM records for external mail providers, or for various confirmation-of-ownership tests.</p>
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3>Using a Secondary Nameserver</h3>
|
<h3>Using a secondary nameserver</h3>
|
||||||
|
|
||||||
<p>If your TLD requires you to have two separate nameservers, you can either set up <a href="#" onclick="return show_panel('external_dns')">external DNS</a> and ignore the DNS server on this box entirely, or use the DNS server on this box but add a secondary (aka “slave”) nameserver.</p>
|
<p>If your TLD requires you to have two separate nameservers, you can either set up <a href="#" onclick="return show_panel('external_dns')">external DNS</a> and ignore the DNS server on this box entirely, or use the DNS server on this box but add a secondary (aka “slave”) nameserver.</p>
|
||||||
<p>If you choose to use a seconday nameserver, you must find a seconday nameserver service provider. Your domain name registrar or virtual cloud provider may provide this service for you. Once you set up the seconday nameserver service, enter the hostname (not the IP address) of <em>their</em> secondary nameserver in the box below.</p>
|
<p>If you choose to use a seconday nameserver, you must find a seconday nameserver service provider. Your domain name registrar or virtual cloud provider may provide this service for you. Once you set up the seconday nameserver service, enter the hostname (not the IP address) of <em>their</em> secondary nameserver in the box below.</p>
|
||||||
|
|||||||
@@ -93,7 +93,7 @@
|
|||||||
<li class="dropdown-header">Advanced Pages</li>
|
<li class="dropdown-header">Advanced Pages</li>
|
||||||
<li><a href="#custom_dns" onclick="return show_panel(this);">Custom DNS</a></li>
|
<li><a href="#custom_dns" onclick="return show_panel(this);">Custom DNS</a></li>
|
||||||
<li><a href="#external_dns" onclick="return show_panel(this);">External DNS</a></li>
|
<li><a href="#external_dns" onclick="return show_panel(this);">External DNS</a></li>
|
||||||
<li><a href="/admin/munin">Munin Monitoring</a></li>
|
<li><a href="/admin/munin" target="_blank">Munin Monitoring</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<p>You need a TLS certificate for this box’s hostname ({{hostname}}) and every other domain name and subdomain that this box is hosting a website for (see the list below).</p>
|
<p>You need a TLS certificate for this box’s hostname ({{hostname}}) and every other domain name and subdomain that this box is hosting a website for (see the list below).</p>
|
||||||
|
|
||||||
<div id="ssl_provision">
|
<div id="ssl_provision">
|
||||||
<h3>Provision a Certificate</h3>
|
<h3>Provision a certificate</h3>
|
||||||
|
|
||||||
<div id="ssl_provision_p" style="display: none; margin-top: 1.5em">
|
<div id="ssl_provision_p" style="display: none; margin-top: 1.5em">
|
||||||
<button onclick='return provision_tls_cert();' class='btn btn-primary' style="float: left; margin: 0 1.5em 1em 0;">Provision</button>
|
<button onclick='return provision_tls_cert();' class='btn btn-primary' style="float: left; margin: 0 1.5em 1em 0;">Provision</button>
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3>Certificate Status</h3>
|
<h3>Certificate status</h3>
|
||||||
|
|
||||||
<p style="margin-top: 1.5em">Certificates expire after a period of time. All certificates will be automatically renewed through <a href="https://letsencrypt.org/" target="_blank">Let’s Encrypt</a> 14 days prior to expiration.</p>
|
<p style="margin-top: 1.5em">Certificates expire after a period of time. All certificates will be automatically renewed through <a href="https://letsencrypt.org/" target="_blank">Let’s Encrypt</a> 14 days prior to expiration.</p>
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
<h3 id="ssl_install_header">Install Certificate</h3>
|
<h3 id="ssl_install_header">Install certificate</h3>
|
||||||
|
|
||||||
<p>There are many other places where you can get a free or cheap certificate. If you don't want to use our automatic Let's Encrypt integration, you can give <a href="https://www.namecheap.com/security/ssl-certificates/domain-validation.aspx">Namecheap’s $9 certificate</a>, <a href="https://www.startssl.com/">StartSSL’s free express lane</a>, <a href="https://buy.wosign.com/free/">WoSign’s free TLS</a></a> or any other certificate provider a try.</p>
|
<p>There are many other places where you can get a free or cheap certificate. If you don't want to use our automatic Let's Encrypt integration, you can give <a href="https://www.namecheap.com/security/ssl-certificates/domain-validation.aspx">Namecheap’s $9 certificate</a>, <a href="https://www.startssl.com/">StartSSL’s free express lane</a>, <a href="https://buy.wosign.com/free/">WoSign’s free TLS</a></a> or any other certificate provider a try.</p>
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<h3>Available Backups</h3>
|
<h3>Available backups</h3>
|
||||||
|
|
||||||
<p>The backup location currently contains the backups listed below. The total size of the backups is currently <span id="backup-total-size"></span>.</p>
|
<p>The backup location currently contains the backups listed below. The total size of the backups is currently <span id="backup-total-size"></span>.</p>
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,48 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h3>Mail user API (advanced)</h3>
|
||||||
|
|
||||||
|
<p>Use your box’s mail user API to add/change/remove users from the command-line or custom services you build.</p>
|
||||||
|
|
||||||
|
<p>Usage:</p>
|
||||||
|
|
||||||
|
<pre>curl -X <b>VERB</b> [-d "<b>parameters</b>"] --user {email}:{password} https://{{hostname}}/admin/mail/users[<b>action</b>]</pre>
|
||||||
|
|
||||||
|
<p>Brackets denote an optional argument. Please note that the POST body <code>parameters</code> must be URL-encoded.</p>
|
||||||
|
|
||||||
|
<p>The email and password given to the <code>--user</code> option must be an administrative user on this system.</p>
|
||||||
|
|
||||||
|
<h4 style="margin-bottom: 0">Verbs</h4>
|
||||||
|
|
||||||
|
<table class="table" style="margin-top: .5em">
|
||||||
|
<thead><th>Verb</th> <th>Action</th><th></th></thead>
|
||||||
|
<tr><td>GET</td><td><i>(none)</i></td> <td>Returns a list of existing mail users. Adding <code>?format=json</code> to the URL will give JSON-encoded results.</td></tr>
|
||||||
|
<tr><td>POST</td><td>/add</td> <td>Adds a new mail user. Required POST-body parameters are <code>email</code> and <code>password</code>.</td></tr>
|
||||||
|
<tr><td>POST</td><td>/remove</td> <td>Removes a mail user. Required POST-by parameter is <code>email</code>.</td></tr>
|
||||||
|
<tr><td>POST</td><td>/privileges/add</td> <td>Used to make a mail user an admin. Required POST-body parameters are <code>email</code> and <code>privilege=admin</code>.</td></tr>
|
||||||
|
<tr><td>POST</td><td>/privileges/remove</td> <td>Used to remove the admin privilege from a mail user. Required POST-body parameter is <code>email</code>.</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h4>Examples:</h4>
|
||||||
|
|
||||||
|
<p>Try these examples. For simplicity the examples omit the <code>--user me@mydomain.com:yourpassword</code> command line argument which you must fill in with your administrative email address and password.</p>
|
||||||
|
|
||||||
|
<pre># Gives a JSON-encoded list of all mail users
|
||||||
|
curl -X GET https://{{hostname}}/admin/mail/users?format=json
|
||||||
|
|
||||||
|
# Adds a new email user
|
||||||
|
curl -X POST -d "email=new_user@mydomail.com" -d "password=s3curE_pa5Sw0rD" https://{{hostname}}/admin/mail/users/add
|
||||||
|
|
||||||
|
# Removes a email user
|
||||||
|
curl -X POST -d "email=new_user@mydomail.com" https://{{hostname}}/admin/mail/users/remove
|
||||||
|
|
||||||
|
# Adds admin privilege to an email user
|
||||||
|
curl -X POST -d "email=new_user@mydomail.com" -d "privilege=admin" https://{{hostname}}/admin/mail/users/privileges/add
|
||||||
|
|
||||||
|
# Removes admin privilege from an email user
|
||||||
|
curl -X POST -d "email=new_user@mydomail.com" https://{{hostname}}/admin/mail/users/privileges/remove
|
||||||
|
</pre>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function show_users() {
|
function show_users() {
|
||||||
@@ -170,7 +212,7 @@ function users_set_password(elem) {
|
|||||||
yourpw = "<p class='text-danger'>If you change your own password, you will be logged out of this control panel and will need to log in again.</p>";
|
yourpw = "<p class='text-danger'>If you change your own password, you will be logged out of this control panel and will need to log in again.</p>";
|
||||||
|
|
||||||
show_modal_confirm(
|
show_modal_confirm(
|
||||||
"Archive User",
|
"Set Password",
|
||||||
$("<p>Set a new password for <b>" + email + "</b>?</p> <p><label for='users_set_password_pw' style='display: block; font-weight: normal'>New Password:</label><input type='password' id='users_set_password_pw'></p><p><small>Passwords must be at least four characters and may not contain spaces.</small>" + yourpw + "</p>"),
|
$("<p>Set a new password for <b>" + email + "</b>?</p> <p><label for='users_set_password_pw' style='display: block; font-weight: normal'>New Password:</label><input type='password' id='users_set_password_pw'></p><p><small>Passwords must be at least four characters and may not contain spaces.</small>" + yourpw + "</p>"),
|
||||||
"Set Password",
|
"Set Password",
|
||||||
function() {
|
function() {
|
||||||
|
|||||||
14
security.md
14
security.md
@@ -69,6 +69,16 @@ The [setup guide video](https://mailinabox.email/) explains how to verify the ho
|
|||||||
|
|
||||||
If DNSSEC is enabled at the box's domain name's registrar, the SSHFP record that the box automatically puts into DNS can also be used to verify the host key fingerprint by setting `VerifyHostKeyDNS yes` in your `ssh/.config` file or by logging in with `ssh -o VerifyHostKeyDNS=yes`. ([source](management/dns_update.py))
|
If DNSSEC is enabled at the box's domain name's registrar, the SSHFP record that the box automatically puts into DNS can also be used to verify the host key fingerprint by setting `VerifyHostKeyDNS yes` in your `ssh/.config` file or by logging in with `ssh -o VerifyHostKeyDNS=yes`. ([source](management/dns_update.py))
|
||||||
|
|
||||||
|
### Brute-force attack mitigation
|
||||||
|
|
||||||
|
`fail2ban` provides some protection from brute-force login attacks (repeated logins that guess account passwords) by blocking offending IP addresses at the network level.
|
||||||
|
|
||||||
|
The following services are protected: SSH, IMAP (dovecot), SMTP submission (postfix), webmail (roundcube), ownCloud/CalDAV/CardDAV (over HTTP), and the Mail-in-a-Box control panel & munin (over HTTP).
|
||||||
|
|
||||||
|
Some other services running on the box may be missing fail2ban filters.
|
||||||
|
|
||||||
|
`fail2ban` only blocks IPv4 addresses, however. If the box has a public IPv6 address, it is not protected from these attacks.
|
||||||
|
|
||||||
Outbound Mail
|
Outbound Mail
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
@@ -80,7 +90,7 @@ The first step in resolving the destination server for an email address is perfo
|
|||||||
|
|
||||||
### Encryption
|
### Encryption
|
||||||
|
|
||||||
The box (along with the vast majority of mail servers) uses [opportunistic encryption](https://en.wikipedia.org/wiki/Opportunistic_encryption), meaning the mail is encrypted in transit and protected from passive eavesdropping, but it is not protected from an active man-in-the-middle attack. Modern encryption settings will be used to the extent the recipient server supports them. ([source](setup/mail-postfix.sh))
|
The box (along with the vast majority of mail servers) uses [opportunistic encryption](https://en.wikipedia.org/wiki/Opportunistic_encryption), meaning the mail is encrypted in transit and protected from passive eavesdropping, but it is not protected from an active man-in-the-middle attack. Modern encryption settings (TLSv1 and later, no RC4) will be used to the extent the recipient server supports them. ([source](setup/mail-postfix.sh))
|
||||||
|
|
||||||
### DANE
|
### DANE
|
||||||
|
|
||||||
@@ -101,7 +111,7 @@ Incoming Mail
|
|||||||
|
|
||||||
### Encryption
|
### Encryption
|
||||||
|
|
||||||
As discussed above, there is no way to require on-the-wire encryption of mail. When the box receives an incoming email (SMTP on port 25), it offers encryption (STARTTLS) but cannot require that senders use it because some senders may not support STARTTLS at all and other senders may support STARTTLS but not with the latest protocols/ciphers. To give senders the best chance at making use of encryption, the box offers protocols back to SSLv3 and ciphers with key lengths as low as 112 bits. Modern clients (senders) will make use of the 256-bit ciphers and Diffie-Hellman ciphers with a 2048-bit key for forward secrecy, however. ([source](setup/mail-postfix.sh))
|
As discussed above, there is no way to require on-the-wire encryption of mail. When the box receives an incoming email (SMTP on port 25), it offers encryption (STARTTLS) but cannot require that senders use it because some senders may not support STARTTLS at all and other senders may support STARTTLS but not with the latest protocols/ciphers. To give senders the best chance at making use of encryption, the box offers protocols back to TLSv1 and ciphers with key lengths as low as 112 bits. Modern clients (senders) will make use of the 256-bit ciphers and Diffie-Hellman ciphers with a 2048-bit key for perfect forward secrecy, however. ([source](setup/mail-postfix.sh))
|
||||||
|
|
||||||
### DANE
|
### DANE
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#########################################################
|
#########################################################
|
||||||
|
|
||||||
if [ -z "$TAG" ]; then
|
if [ -z "$TAG" ]; then
|
||||||
TAG=v0.18
|
TAG=v0.19b
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Are we running as root?
|
# Are we running as root?
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ apt_install \
|
|||||||
# would be 20 users). Set it to 250 times the number of cores this
|
# would be 20 users). Set it to 250 times the number of cores this
|
||||||
# machine has, so on a two-core machine that's 500 processes/100 users).
|
# machine has, so on a two-core machine that's 500 processes/100 users).
|
||||||
tools/editconf.py /etc/dovecot/conf.d/10-master.conf \
|
tools/editconf.py /etc/dovecot/conf.d/10-master.conf \
|
||||||
default_process_limit=$(echo "`nproc` * 250" | bc)
|
default_process_limit=$(echo "`nproc` * 250" | bc) \
|
||||||
|
log_path=/var/log/mail.log
|
||||||
|
|
||||||
# The inotify `max_user_instances` default is 128, which constrains
|
# The inotify `max_user_instances` default is 128, which constrains
|
||||||
# the total number of watched (IMAP IDLE push) folders by open connections.
|
# the total number of watched (IMAP IDLE push) folders by open connections.
|
||||||
|
|||||||
@@ -122,8 +122,9 @@ tools/editconf.py /etc/postfix/main.cf \
|
|||||||
smtpd_tls_cert_file=$STORAGE_ROOT/ssl/ssl_certificate.pem \
|
smtpd_tls_cert_file=$STORAGE_ROOT/ssl/ssl_certificate.pem \
|
||||||
smtpd_tls_key_file=$STORAGE_ROOT/ssl/ssl_private_key.pem \
|
smtpd_tls_key_file=$STORAGE_ROOT/ssl/ssl_private_key.pem \
|
||||||
smtpd_tls_dh1024_param_file=$STORAGE_ROOT/ssl/dh2048.pem \
|
smtpd_tls_dh1024_param_file=$STORAGE_ROOT/ssl/dh2048.pem \
|
||||||
|
smtpd_tls_protocols=\!SSLv2,\!SSLv3 \
|
||||||
smtpd_tls_ciphers=medium \
|
smtpd_tls_ciphers=medium \
|
||||||
smtpd_tls_exclude_ciphers=aNULL \
|
smtpd_tls_exclude_ciphers=aNULL,RC4 \
|
||||||
smtpd_tls_received_header=yes
|
smtpd_tls_received_header=yes
|
||||||
|
|
||||||
# Prevent non-authenticated users from sending mail that requires being
|
# Prevent non-authenticated users from sending mail that requires being
|
||||||
@@ -158,6 +159,10 @@ tools/editconf.py /etc/postfix/main.cf \
|
|||||||
# even if we don't know if it's to the right party, than to not encrypt at all. Instead we'll
|
# even if we don't know if it's to the right party, than to not encrypt at all. Instead we'll
|
||||||
# now see notices about trusted certs. The CA file is provided by the package `ca-certificates`.
|
# now see notices about trusted certs. The CA file is provided by the package `ca-certificates`.
|
||||||
tools/editconf.py /etc/postfix/main.cf \
|
tools/editconf.py /etc/postfix/main.cf \
|
||||||
|
smtp_tls_protocols=\!SSLv2,\!SSLv3 \
|
||||||
|
smtp_tls_mandatory_protocols=\!SSLv2,\!SSLv3 \
|
||||||
|
smtp_tls_ciphers=medium \
|
||||||
|
smtp_tls_exclude_ciphers=aNULL,RC4 \
|
||||||
smtp_tls_security_level=dane \
|
smtp_tls_security_level=dane \
|
||||||
smtp_dns_support_level=dnssec \
|
smtp_dns_support_level=dnssec \
|
||||||
smtp_tls_CAfile=/etc/ssl/certs/ca-certificates.crt \
|
smtp_tls_CAfile=/etc/ssl/certs/ca-certificates.crt \
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ driver = sqlite
|
|||||||
connect = $db_path
|
connect = $db_path
|
||||||
default_pass_scheme = SHA512-CRYPT
|
default_pass_scheme = SHA512-CRYPT
|
||||||
password_query = SELECT email as user, password FROM users WHERE email='%u';
|
password_query = SELECT email as user, password FROM users WHERE email='%u';
|
||||||
user_query = SELECT email AS user, "mail" as uid, "mail" as gid, "$STORAGE_ROOT/mail/mailboxes/%d/%n" as home FROM users;
|
user_query = SELECT email AS user, "mail" as uid, "mail" as gid, "$STORAGE_ROOT/mail/mailboxes/%d/%n" as home FROM users WHERE email='%u';
|
||||||
iterate_query = SELECT email AS user FROM users;
|
iterate_query = SELECT email AS user FROM users;
|
||||||
EOF
|
EOF
|
||||||
chmod 0600 /etc/dovecot/dovecot-sql.conf.ext # per Dovecot instructions
|
chmod 0600 /etc/dovecot/dovecot-sql.conf.ext # per Dovecot instructions
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ if [ ! -f $STORAGE_ROOT/owncloud/owncloud.db ]; then
|
|||||||
mkdir -p $STORAGE_ROOT/owncloud
|
mkdir -p $STORAGE_ROOT/owncloud
|
||||||
|
|
||||||
# Create an initial configuration file.
|
# Create an initial configuration file.
|
||||||
TIMEZONE=$(cat /etc/timezone)
|
|
||||||
instanceid=oc$(echo $PRIMARY_HOSTNAME | sha1sum | fold -w 10 | head -n 1)
|
instanceid=oc$(echo $PRIMARY_HOSTNAME | sha1sum | fold -w 10 | head -n 1)
|
||||||
cat > $STORAGE_ROOT/owncloud/config.php <<EOF;
|
cat > $STORAGE_ROOT/owncloud/config.php <<EOF;
|
||||||
<?php
|
<?php
|
||||||
@@ -125,7 +124,6 @@ if [ ! -f $STORAGE_ROOT/owncloud/owncloud.db ]; then
|
|||||||
'mail_smtppassword' => '',
|
'mail_smtppassword' => '',
|
||||||
'mail_from_address' => 'owncloud',
|
'mail_from_address' => 'owncloud',
|
||||||
'mail_domain' => '$PRIMARY_HOSTNAME',
|
'mail_domain' => '$PRIMARY_HOSTNAME',
|
||||||
'logtimezone' => '$TIMEZONE',
|
|
||||||
);
|
);
|
||||||
?>
|
?>
|
||||||
EOF
|
EOF
|
||||||
@@ -163,7 +161,11 @@ fi
|
|||||||
# so set it here. It also can change if the box's PRIMARY_HOSTNAME changes, so
|
# so set it here. It also can change if the box's PRIMARY_HOSTNAME changes, so
|
||||||
# this will make sure it has the right value.
|
# this will make sure it has the right value.
|
||||||
# * Some settings weren't included in previous versions of Mail-in-a-Box.
|
# * Some settings weren't included in previous versions of Mail-in-a-Box.
|
||||||
|
# * We need to set the timezone to the system timezone to allow fail2ban to ban
|
||||||
|
# users within the proper timeframe
|
||||||
|
# * We need to set the logdateformat to something that will work correctly with fail2ban
|
||||||
# Use PHP to read the settings file, modify it, and write out the new settings array.
|
# Use PHP to read the settings file, modify it, and write out the new settings array.
|
||||||
|
TIMEZONE=$(cat /etc/timezone)
|
||||||
CONFIG_TEMP=$(/bin/mktemp)
|
CONFIG_TEMP=$(/bin/mktemp)
|
||||||
php <<EOF > $CONFIG_TEMP && mv $CONFIG_TEMP $STORAGE_ROOT/owncloud/config.php;
|
php <<EOF > $CONFIG_TEMP && mv $CONFIG_TEMP $STORAGE_ROOT/owncloud/config.php;
|
||||||
<?php
|
<?php
|
||||||
@@ -175,6 +177,9 @@ include("$STORAGE_ROOT/owncloud/config.php");
|
|||||||
\$CONFIG['overwrite.cli.url'] = '/cloud';
|
\$CONFIG['overwrite.cli.url'] = '/cloud';
|
||||||
\$CONFIG['mail_from_address'] = 'administrator'; # just the local part, matches our master administrator address
|
\$CONFIG['mail_from_address'] = 'administrator'; # just the local part, matches our master administrator address
|
||||||
|
|
||||||
|
\$CONFIG['logtimezone'] = '$TIMEZONE';
|
||||||
|
\$CONFIG['logdateformat'] = 'Y-m-d H:i:s';
|
||||||
|
|
||||||
echo "<?php\n\\\$CONFIG = ";
|
echo "<?php\n\\\$CONFIG = ";
|
||||||
var_export(\$CONFIG);
|
var_export(\$CONFIG);
|
||||||
echo ";";
|
echo ";";
|
||||||
|
|||||||
@@ -47,15 +47,15 @@ if [ -e ~/.wgetrc ]; then
|
|||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check that we are running on x86_64, any other architecture is unsupported and
|
# Check that we are running on x86_64 or i686, any other architecture is unsupported and
|
||||||
# will fail later in the setup when we try to install the custom build lucene packages.
|
# will fail later in the setup when we try to install the custom build lucene packages.
|
||||||
#
|
#
|
||||||
# Set ARM=1 to ignore this check if you have built the packages yourself. If you do this
|
# Set ARM=1 to ignore this check if you have built the packages yourself. If you do this
|
||||||
# you are on your own!
|
# you are on your own!
|
||||||
ARCHITECTURE=$(uname -m)
|
ARCHITECTURE=$(uname -m)
|
||||||
if [ "$ARCHITECTURE" != "x86_64" ]; then
|
if [ "$ARCHITECTURE" != "x86_64" ] && [ "$ARCHITECTURE" != "i686" ]; then
|
||||||
if [ -z "$ARM" ]; then
|
if [ -z "$ARM" ]; then
|
||||||
echo "Mail-in-a-Box only supports x86_64 and will not work on any other architecture, like ARM."
|
echo "Mail-in-a-Box only supports x86_64 or i686 and will not work on any other architecture, like ARM."
|
||||||
echo "Your architecture is $ARCHITECTURE"
|
echo "Your architecture is $ARCHITECTURE"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -111,15 +111,22 @@ source setup/zpush.sh
|
|||||||
source setup/management.sh
|
source setup/management.sh
|
||||||
source setup/munin.sh
|
source setup/munin.sh
|
||||||
|
|
||||||
# Ping the management daemon to write the DNS and nginx configuration files.
|
# Wait for the management daemon to start...
|
||||||
until nc -z -w 4 127.0.0.1 10222
|
until nc -z -w 4 127.0.0.1 10222
|
||||||
do
|
do
|
||||||
echo Waiting for the Mail-in-a-Box management daemon to start...
|
echo Waiting for the Mail-in-a-Box management daemon to start...
|
||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# ...and then have it write the DNS and nginx configuration files and start those
|
||||||
|
# services.
|
||||||
tools/dns_update
|
tools/dns_update
|
||||||
tools/web_update
|
tools/web_update
|
||||||
|
|
||||||
|
# Give fail2ban another restart. The log files may not all have been present when
|
||||||
|
# fail2ban was first configured, but they should exist now.
|
||||||
|
restart_service fail2ban
|
||||||
|
|
||||||
# If DNS is already working, try to provision TLS certficates from Let's Encrypt.
|
# If DNS is already working, try to provision TLS certficates from Let's Encrypt.
|
||||||
# Suppress extra reasons why domains aren't getting a new certificate.
|
# Suppress extra reasons why domains aren't getting a new certificate.
|
||||||
management/ssl_certificates.py -q
|
management/ssl_certificates.py -q
|
||||||
|
|||||||
@@ -291,10 +291,17 @@ restart_service resolvconf
|
|||||||
|
|
||||||
# ### Fail2Ban Service
|
# ### Fail2Ban Service
|
||||||
|
|
||||||
# Configure the Fail2Ban installation to prevent dumb bruce-force attacks against dovecot, postfix and ssh
|
# Configure the Fail2Ban installation to prevent dumb bruce-force attacks against dovecot, postfix, ssh, etc.
|
||||||
cat conf/fail2ban/jail.local \
|
rm -f /etc/fail2ban/jail.local # we used to use this file but don't anymore
|
||||||
|
cat conf/fail2ban/jails.conf \
|
||||||
| sed "s/PUBLIC_IP/$PUBLIC_IP/g" \
|
| sed "s/PUBLIC_IP/$PUBLIC_IP/g" \
|
||||||
> /etc/fail2ban/jail.local
|
| sed "s#STORAGE_ROOT#$STORAGE_ROOT#" \
|
||||||
cp conf/fail2ban/dovecotimap.conf /etc/fail2ban/filter.d/dovecotimap.conf
|
> /etc/fail2ban/jail.d/mailinabox.conf
|
||||||
|
cp -f conf/fail2ban/filter.d/* /etc/fail2ban/filter.d/
|
||||||
|
|
||||||
|
# On first installation, the log files that the jails look at don't all exist.
|
||||||
|
# e.g., The roundcube error log isn't normally created until someone logs into
|
||||||
|
# Roundcube for the first time. This causes fail2ban to fail to start. Later
|
||||||
|
# scripts will ensure the files exist and then fail2ban is given another
|
||||||
|
# restart at the very end of setup.
|
||||||
restart_service fail2ban
|
restart_service fail2ban
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ apt-get purge -qq -y roundcube* #NODOC
|
|||||||
# Install Roundcube from source if it is not already present or if it is out of date.
|
# Install Roundcube from source if it is not already present or if it is out of date.
|
||||||
# Combine the Roundcube version number with the commit hash of vacation_sieve to track
|
# Combine the Roundcube version number with the commit hash of vacation_sieve to track
|
||||||
# whether we have the latest version.
|
# whether we have the latest version.
|
||||||
VERSION=1.1.5
|
VERSION=1.2.1
|
||||||
HASH=8A59D196EF0AA6D9C717B00699215135ABCB99CF
|
HASH=81fbfba4683522f6e54006d0300a48e6da3f3bbd
|
||||||
VACATION_SIEVE_VERSION=91ea6f52216390073d1f5b70b5f6bea0bfaee7e5
|
VACATION_SIEVE_VERSION=91ea6f52216390073d1f5b70b5f6bea0bfaee7e5
|
||||||
PERSISTENT_LOGIN_VERSION=1e9d724476a370ce917a2fcd5b3217b0c306c24e
|
PERSISTENT_LOGIN_VERSION=1e9d724476a370ce917a2fcd5b3217b0c306c24e
|
||||||
HTML5_NOTIFIER_VERSION=4b370e3cd60dabd2f428a26f45b677ad1b7118d5
|
HTML5_NOTIFIER_VERSION=4b370e3cd60dabd2f428a26f45b677ad1b7118d5
|
||||||
@@ -91,7 +91,7 @@ cat > /usr/local/lib/roundcubemail/config/config.inc.php <<EOF;
|
|||||||
\$config['log_dir'] = '/var/log/roundcubemail/';
|
\$config['log_dir'] = '/var/log/roundcubemail/';
|
||||||
\$config['temp_dir'] = '/tmp/roundcubemail/';
|
\$config['temp_dir'] = '/tmp/roundcubemail/';
|
||||||
\$config['db_dsnw'] = 'sqlite:///$STORAGE_ROOT/mail/roundcube/roundcube.sqlite?mode=0640';
|
\$config['db_dsnw'] = 'sqlite:///$STORAGE_ROOT/mail/roundcube/roundcube.sqlite?mode=0640';
|
||||||
\$config['default_host'] = 'ssl://127.0.0.1';
|
\$config['default_host'] = 'ssl://localhost';
|
||||||
\$config['default_port'] = 993;
|
\$config['default_port'] = 993;
|
||||||
\$config['imap_timeout'] = 15;
|
\$config['imap_timeout'] = 15;
|
||||||
\$config['smtp_server'] = 'tls://127.0.0.1';
|
\$config['smtp_server'] = 'tls://127.0.0.1';
|
||||||
@@ -133,6 +133,9 @@ EOF
|
|||||||
mkdir -p /var/log/roundcubemail /tmp/roundcubemail $STORAGE_ROOT/mail/roundcube
|
mkdir -p /var/log/roundcubemail /tmp/roundcubemail $STORAGE_ROOT/mail/roundcube
|
||||||
chown -R www-data.www-data /var/log/roundcubemail /tmp/roundcubemail $STORAGE_ROOT/mail/roundcube
|
chown -R www-data.www-data /var/log/roundcubemail /tmp/roundcubemail $STORAGE_ROOT/mail/roundcube
|
||||||
|
|
||||||
|
# Ensure the log file monitored by fail2ban exists, or else fail2ban can't start.
|
||||||
|
sudo -u www-data touch /var/log/roundcubemail/errors
|
||||||
|
|
||||||
# Password changing plugin settings
|
# Password changing plugin settings
|
||||||
# The config comes empty by default, so we need the settings
|
# The config comes empty by default, so we need the settings
|
||||||
# we're not planning to change in config.inc.dist...
|
# we're not planning to change in config.inc.dist...
|
||||||
@@ -157,6 +160,12 @@ chmod 775 $STORAGE_ROOT/mail
|
|||||||
chown root.www-data $STORAGE_ROOT/mail/users.sqlite
|
chown root.www-data $STORAGE_ROOT/mail/users.sqlite
|
||||||
chmod 664 $STORAGE_ROOT/mail/users.sqlite
|
chmod 664 $STORAGE_ROOT/mail/users.sqlite
|
||||||
|
|
||||||
|
# Run Roundcube database migration script, if the database exists (it's created by
|
||||||
|
# Roundcube on first use).
|
||||||
|
if [ -f $STORAGE_ROOT/mail/roundcube/roundcube.sqlite ]; then
|
||||||
|
/usr/local/lib/roundcubemail/bin/updatedb.sh --dir /usr/local/lib/roundcubemail/SQL --package roundcube
|
||||||
|
fi
|
||||||
|
|
||||||
# Enable PHP modules.
|
# Enable PHP modules.
|
||||||
php5enmod mcrypt
|
php5enmod mcrypt
|
||||||
restart_service php5-fpm
|
restart_service php5-fpm
|
||||||
|
|||||||
196
tests/fail2ban.py
Normal file
196
tests/fail2ban.py
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
# Test that a box's fail2ban setting are working
|
||||||
|
# correctly by attempting a bunch of failed logins.
|
||||||
|
#
|
||||||
|
# Specify a SSH login command (which we use to reset
|
||||||
|
# fail2ban after each test) and the hostname to
|
||||||
|
# try to log in to.
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
import sys, os, time, functools
|
||||||
|
|
||||||
|
# parse command line
|
||||||
|
|
||||||
|
if len(sys.argv) != 3:
|
||||||
|
print("Usage: tests/fail2ban.py \"ssh user@hostname\" hostname")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
ssh_command, hostname = sys.argv[1:3]
|
||||||
|
|
||||||
|
# define some test types
|
||||||
|
|
||||||
|
import socket
|
||||||
|
socket.setdefaulttimeout(10)
|
||||||
|
|
||||||
|
class IsBlocked(Exception):
|
||||||
|
"""Tests raise this exception when it appears that a fail2ban
|
||||||
|
jail is in effect, i.e. on a connection refused error."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def smtp_test():
|
||||||
|
import smtplib
|
||||||
|
|
||||||
|
try:
|
||||||
|
server = smtplib.SMTP(hostname, 587)
|
||||||
|
except ConnectionRefusedError:
|
||||||
|
# looks like fail2ban worked
|
||||||
|
raise IsBlocked()
|
||||||
|
server.starttls()
|
||||||
|
server.ehlo_or_helo_if_needed()
|
||||||
|
|
||||||
|
try:
|
||||||
|
server.login("fakeuser", "fakepassword")
|
||||||
|
raise Exception("authentication didn't fail")
|
||||||
|
except smtplib.SMTPAuthenticationError:
|
||||||
|
# athentication should fail
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
server.quit()
|
||||||
|
except:
|
||||||
|
# ignore errors here
|
||||||
|
pass
|
||||||
|
|
||||||
|
def imap_test():
|
||||||
|
import imaplib
|
||||||
|
|
||||||
|
try:
|
||||||
|
M = imaplib.IMAP4_SSL(hostname)
|
||||||
|
except ConnectionRefusedError:
|
||||||
|
# looks like fail2ban worked
|
||||||
|
raise IsBlocked()
|
||||||
|
|
||||||
|
try:
|
||||||
|
M.login("fakeuser", "fakepassword")
|
||||||
|
raise Exception("authentication didn't fail")
|
||||||
|
except imaplib.IMAP4.error:
|
||||||
|
# authentication should fail
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
M.logout() # shuts down connection, has nothing to do with login()
|
||||||
|
|
||||||
|
def http_test(url, expected_status, postdata=None, qsargs=None, auth=None):
|
||||||
|
import urllib.parse
|
||||||
|
import requests
|
||||||
|
from requests.auth import HTTPBasicAuth
|
||||||
|
|
||||||
|
# form request
|
||||||
|
url = urllib.parse.urljoin("https://" + hostname, url)
|
||||||
|
if qsargs: url += "?" + urllib.parse.urlencode(qsargs)
|
||||||
|
urlopen = requests.get if not postdata else requests.post
|
||||||
|
|
||||||
|
try:
|
||||||
|
# issue request
|
||||||
|
r = urlopen(
|
||||||
|
url,
|
||||||
|
auth=HTTPBasicAuth(*auth) if auth else None,
|
||||||
|
data=postdata,
|
||||||
|
headers={'User-Agent': 'Mail-in-a-Box fail2ban tester'},
|
||||||
|
timeout=8,
|
||||||
|
verify=False) # don't bother with HTTPS validation, it may not be configured yet
|
||||||
|
except requests.exceptions.ConnectTimeout as e:
|
||||||
|
raise IsBlocked()
|
||||||
|
except requests.exceptions.ConnectionError as e:
|
||||||
|
if "Connection refused" in str(e):
|
||||||
|
raise IsBlocked()
|
||||||
|
raise # some other unexpected condition
|
||||||
|
|
||||||
|
# return response status code
|
||||||
|
if r.status_code != expected_status:
|
||||||
|
r.raise_for_status() # anything but 200
|
||||||
|
raise IOError("Got unexpected status code %s." % r.status_code)
|
||||||
|
|
||||||
|
# define how to run a test
|
||||||
|
|
||||||
|
def restart_fail2ban_service(final=False):
|
||||||
|
# Log in over SSH to restart fail2ban.
|
||||||
|
command = "sudo fail2ban-client reload"
|
||||||
|
if not final:
|
||||||
|
# Stop recidive jails during testing.
|
||||||
|
command += " && sudo fail2ban-client stop recidive"
|
||||||
|
os.system("%s \"%s\"" % (ssh_command, command))
|
||||||
|
|
||||||
|
def testfunc_runner(i, testfunc, *args):
|
||||||
|
print(i+1, end=" ", flush=True)
|
||||||
|
testfunc(*args)
|
||||||
|
|
||||||
|
def run_test(testfunc, args, count, within_seconds, parallel):
|
||||||
|
# Run testfunc count times in within_seconds seconds (and actually
|
||||||
|
# within a little less time so we're sure we're under the limit).
|
||||||
|
#
|
||||||
|
# Because some services are slow, like IMAP, we can't necessarily
|
||||||
|
# run testfunc sequentially and still get to count requests within
|
||||||
|
# the required time. So we split the requests across threads.
|
||||||
|
|
||||||
|
import requests.exceptions
|
||||||
|
from multiprocessing import Pool
|
||||||
|
|
||||||
|
restart_fail2ban_service()
|
||||||
|
|
||||||
|
# Log.
|
||||||
|
print(testfunc.__name__, " ".join(str(a) for a in args), "...")
|
||||||
|
|
||||||
|
# Record the start time so we can know how to evenly space our
|
||||||
|
# calls to testfunc.
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
with Pool(parallel) as p:
|
||||||
|
# Distribute the requests across the pool.
|
||||||
|
asyncresults = []
|
||||||
|
for i in range(count):
|
||||||
|
ar = p.apply_async(testfunc_runner, [i, testfunc] + list(args))
|
||||||
|
asyncresults.append(ar)
|
||||||
|
|
||||||
|
# Wait for all runs to finish.
|
||||||
|
p.close()
|
||||||
|
p.join()
|
||||||
|
|
||||||
|
# Check for errors.
|
||||||
|
for ar in asyncresults:
|
||||||
|
try:
|
||||||
|
ar.get()
|
||||||
|
except IsBlocked:
|
||||||
|
print("Test machine prematurely blocked!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Did we make enough requests within the limit?
|
||||||
|
if (time.time()-start_time) > within_seconds:
|
||||||
|
raise Exception("Test failed to make %s requests in %d seconds." % (count, within_seconds))
|
||||||
|
|
||||||
|
# Wait a moment for the block to be put into place.
|
||||||
|
time.sleep(4)
|
||||||
|
|
||||||
|
# The next call should fail.
|
||||||
|
print("*", end=" ", flush=True)
|
||||||
|
try:
|
||||||
|
testfunc(*args)
|
||||||
|
except IsBlocked:
|
||||||
|
# Success -- this one is supposed to be refused.
|
||||||
|
print("blocked [OK]")
|
||||||
|
return True # OK
|
||||||
|
|
||||||
|
print("not blocked!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# run tests
|
||||||
|
|
||||||
|
# SMTP bans at 10 even though we say 20 in the config because we get
|
||||||
|
# doubled-up warnings in the logs, we'll let that be for now
|
||||||
|
run_test(smtp_test, [], 10, 30, 8)
|
||||||
|
|
||||||
|
# IMAP
|
||||||
|
run_test(imap_test, [], 20, 30, 4)
|
||||||
|
|
||||||
|
# Mail-in-a-Box control panel
|
||||||
|
run_test(http_test, ["/admin/me", 200], 20, 30, 1)
|
||||||
|
|
||||||
|
# Munin via the Mail-in-a-Box control panel
|
||||||
|
run_test(http_test, ["/admin/munin/", 401], 20, 30, 1)
|
||||||
|
|
||||||
|
# ownCloud
|
||||||
|
run_test(http_test, ["/cloud/remote.php/webdav", 401, None, None, ["aa", "aa"]], 20, 120, 1)
|
||||||
|
|
||||||
|
# restart fail2ban so that this client machine is no longer blocked
|
||||||
|
restart_fail2ban_service(final=True)
|
||||||
@@ -33,7 +33,6 @@ PORT 25
|
|||||||
AES256-SHA256 - 256 bits 250 2.0.0 Ok
|
AES256-SHA256 - 256 bits 250 2.0.0 Ok
|
||||||
AES256-SHA - 256 bits 250 2.0.0 Ok
|
AES256-SHA - 256 bits 250 2.0.0 Ok
|
||||||
AES256-GCM-SHA384 - 256 bits 250 2.0.0 Ok
|
AES256-GCM-SHA384 - 256 bits 250 2.0.0 Ok
|
||||||
ECDHE-RSA-RC4-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok
|
|
||||||
ECDHE-RSA-AES128-SHA256 ECDH-256 bits 128 bits 250 2.0.0 Ok
|
ECDHE-RSA-AES128-SHA256 ECDH-256 bits 128 bits 250 2.0.0 Ok
|
||||||
ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok
|
ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok
|
||||||
ECDHE-RSA-AES128-GCM-SHA256 ECDH-256 bits 128 bits 250 2.0.0 Ok
|
ECDHE-RSA-AES128-GCM-SHA256 ECDH-256 bits 128 bits 250 2.0.0 Ok
|
||||||
@@ -43,8 +42,6 @@ PORT 25
|
|||||||
DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
||||||
DHE-RSA-AES128-GCM-SHA256 DH-2048 bits 128 bits 250 2.0.0 Ok
|
DHE-RSA-AES128-GCM-SHA256 DH-2048 bits 128 bits 250 2.0.0 Ok
|
||||||
SEED-SHA - 128 bits 250 2.0.0 Ok
|
SEED-SHA - 128 bits 250 2.0.0 Ok
|
||||||
RC4-SHA - 128 bits 250 2.0.0 Ok
|
|
||||||
RC4-MD5 - 128 bits 250 2.0.0 Ok
|
|
||||||
CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok
|
CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok
|
||||||
AES128-SHA256 - 128 bits 250 2.0.0 Ok
|
AES128-SHA256 - 128 bits 250 2.0.0 Ok
|
||||||
AES128-SHA - 128 bits 250 2.0.0 Ok
|
AES128-SHA - 128 bits 250 2.0.0 Ok
|
||||||
@@ -62,37 +59,11 @@ PORT 25
|
|||||||
DHE-RSA-AES256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok
|
DHE-RSA-AES256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok
|
||||||
CAMELLIA256-SHA - 256 bits 250 2.0.0 Ok
|
CAMELLIA256-SHA - 256 bits 250 2.0.0 Ok
|
||||||
AES256-SHA - 256 bits 250 2.0.0 Ok
|
AES256-SHA - 256 bits 250 2.0.0 Ok
|
||||||
ECDHE-RSA-RC4-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok
|
|
||||||
ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok
|
ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok
|
||||||
DHE-RSA-SEED-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
DHE-RSA-SEED-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
||||||
DHE-RSA-CAMELLIA128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
DHE-RSA-CAMELLIA128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
||||||
DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
||||||
SEED-SHA - 128 bits 250 2.0.0 Ok
|
SEED-SHA - 128 bits 250 2.0.0 Ok
|
||||||
RC4-SHA - 128 bits 250 2.0.0 Ok
|
|
||||||
RC4-MD5 - 128 bits 250 2.0.0 Ok
|
|
||||||
CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok
|
|
||||||
AES128-SHA - 128 bits 250 2.0.0 Ok
|
|
||||||
ECDHE-RSA-DES-CBC3-SHA ECDH-256 bits 112 bits 250 2.0.0 Ok
|
|
||||||
EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits 250 2.0.0 Ok
|
|
||||||
DES-CBC3-SHA - 112 bits 250 2.0.0 Ok
|
|
||||||
|
|
||||||
* SSLV3 Cipher Suites:
|
|
||||||
Preferred:
|
|
||||||
ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok
|
|
||||||
Accepted:
|
|
||||||
ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok
|
|
||||||
DHE-RSA-CAMELLIA256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok
|
|
||||||
DHE-RSA-AES256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok
|
|
||||||
CAMELLIA256-SHA - 256 bits 250 2.0.0 Ok
|
|
||||||
AES256-SHA - 256 bits 250 2.0.0 Ok
|
|
||||||
ECDHE-RSA-RC4-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok
|
|
||||||
ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok
|
|
||||||
DHE-RSA-SEED-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
|
||||||
DHE-RSA-CAMELLIA128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
|
||||||
DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
|
||||||
SEED-SHA - 128 bits 250 2.0.0 Ok
|
|
||||||
RC4-SHA - 128 bits 250 2.0.0 Ok
|
|
||||||
RC4-MD5 - 128 bits 250 2.0.0 Ok
|
|
||||||
CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok
|
CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok
|
||||||
AES128-SHA - 128 bits 250 2.0.0 Ok
|
AES128-SHA - 128 bits 250 2.0.0 Ok
|
||||||
ECDHE-RSA-DES-CBC3-SHA ECDH-256 bits 112 bits 250 2.0.0 Ok
|
ECDHE-RSA-DES-CBC3-SHA ECDH-256 bits 112 bits 250 2.0.0 Ok
|
||||||
@@ -108,23 +79,23 @@ PORT 25
|
|||||||
DHE-RSA-AES256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok
|
DHE-RSA-AES256-SHA DH-2048 bits 256 bits 250 2.0.0 Ok
|
||||||
CAMELLIA256-SHA - 256 bits 250 2.0.0 Ok
|
CAMELLIA256-SHA - 256 bits 250 2.0.0 Ok
|
||||||
AES256-SHA - 256 bits 250 2.0.0 Ok
|
AES256-SHA - 256 bits 250 2.0.0 Ok
|
||||||
ECDHE-RSA-RC4-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok
|
|
||||||
ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok
|
ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits 250 2.0.0 Ok
|
||||||
DHE-RSA-SEED-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
DHE-RSA-SEED-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
||||||
DHE-RSA-CAMELLIA128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
DHE-RSA-CAMELLIA128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
||||||
DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
||||||
SEED-SHA - 128 bits 250 2.0.0 Ok
|
SEED-SHA - 128 bits 250 2.0.0 Ok
|
||||||
RC4-SHA - 128 bits 250 2.0.0 Ok
|
|
||||||
RC4-MD5 - 128 bits 250 2.0.0 Ok
|
|
||||||
CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok
|
CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok
|
||||||
AES128-SHA - 128 bits 250 2.0.0 Ok
|
AES128-SHA - 128 bits 250 2.0.0 Ok
|
||||||
ECDHE-RSA-DES-CBC3-SHA ECDH-256 bits 112 bits 250 2.0.0 Ok
|
ECDHE-RSA-DES-CBC3-SHA ECDH-256 bits 112 bits 250 2.0.0 Ok
|
||||||
EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits 250 2.0.0 Ok
|
EDH-RSA-DES-CBC3-SHA DH-2048 bits 112 bits 250 2.0.0 Ok
|
||||||
DES-CBC3-SHA - 112 bits 250 2.0.0 Ok
|
DES-CBC3-SHA - 112 bits 250 2.0.0 Ok
|
||||||
|
|
||||||
Should Not Offer: DHE-RSA-SEED-SHA, ECDHE-RSA-RC4-SHA, EDH-RSA-DES-CBC3-SHA, RC4-MD5, RC4-SHA, SEED-SHA
|
* SSLV3 Cipher Suites:
|
||||||
Could Also Offer: DHE-DSS-AES128-GCM-SHA256, DHE-DSS-AES128-SHA, DHE-DSS-AES128-SHA256, DHE-DSS-AES256-GCM-SHA384, DHE-DSS-AES256-SHA, DHE-DSS-AES256-SHA256, DHE-DSS-CAMELLIA128-SHA, DHE-DSS-CAMELLIA256-SHA, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384, ECDHE-ECDSA-DES-CBC3-SHA, SRP-3DES-EDE-CBC-SHA, SRP-AES-128-CBC-SHA, SRP-AES-256-CBC-SHA, SRP-DSS-3DES-EDE-CBC-SHA, SRP-DSS-AES-128-CBC-SHA, SRP-DSS-AES-256-CBC-SHA, SRP-RSA-3DES-EDE-CBC-SHA, SRP-RSA-AES-128-CBC-SHA, SRP-RSA-AES-256-CBC-SHA
|
Server rejected all cipher suites.
|
||||||
Supported Clients: OpenSSL/1.0.2, Yahoo Slurp/Jan 2015, BingPreview/Jan 2015, OpenSSL/1.0.1l, YandexBot/Jan 2015, Android/4.4.2, Safari/8/iOS 8.1.2, Safari/7/OS X 10.9, Safari/8/OS X 10.10, Safari/7/iOS 7.1, Safari/6/iOS 6.0.1, Baidu/Jan 2015, Firefox/31.3.0 ESR/Win 7, Android/5.0.0, IE/11/Win 7, Java/8u31, Googlebot/Feb 2015, Chrome/42/OS X, IE Mobile/11/Win Phone 8.1, IE/11/Win 8.1, Android/4.0.4, Android/4.1.1, Safari/6.0.4/OS X 10.8.4, Android/4.3, Android/4.2.2, Safari/5.1.9/OS X 10.6.8, Java/7u25, OpenSSL/0.9.8y, Firefox/37/OS X, IE/7/Vista, IE/8-10/Win 7, IE Mobile/10/Win Phone 8.0, Java/6u45, Android/2.3.7, IE/8/XP
|
|
||||||
|
Should Not Offer: DHE-RSA-SEED-SHA, EDH-RSA-DES-CBC3-SHA, SEED-SHA
|
||||||
|
Could Also Offer: DH-DSS-AES128-GCM-SHA256, DH-DSS-AES128-SHA, DH-DSS-AES128-SHA256, DH-DSS-AES256-GCM-SHA384, DH-DSS-AES256-SHA, DH-DSS-AES256-SHA256, DH-DSS-CAMELLIA128-SHA, DH-DSS-CAMELLIA256-SHA, DH-DSS-DES-CBC3-SHA, DH-RSA-AES128-GCM-SHA256, DH-RSA-AES128-SHA, DH-RSA-AES128-SHA256, DH-RSA-AES256-GCM-SHA384, DH-RSA-AES256-SHA, DH-RSA-AES256-SHA256, DH-RSA-CAMELLIA128-SHA, DH-RSA-CAMELLIA256-SHA, DH-RSA-DES-CBC3-SHA, DHE-DSS-AES128-GCM-SHA256, DHE-DSS-AES128-SHA, DHE-DSS-AES128-SHA256, DHE-DSS-AES256-GCM-SHA384, DHE-DSS-AES256-SHA, DHE-DSS-AES256-SHA256, DHE-DSS-CAMELLIA128-SHA, DHE-DSS-CAMELLIA256-SHA, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384, ECDHE-ECDSA-DES-CBC3-SHA, SRP-3DES-EDE-CBC-SHA, SRP-AES-128-CBC-SHA, SRP-AES-256-CBC-SHA, SRP-DSS-3DES-EDE-CBC-SHA, SRP-DSS-AES-128-CBC-SHA, SRP-DSS-AES-256-CBC-SHA, SRP-RSA-3DES-EDE-CBC-SHA, SRP-RSA-AES-128-CBC-SHA, SRP-RSA-AES-256-CBC-SHA
|
||||||
|
Supported Clients: OpenSSL/1.0.2, OpenSSL/1.0.1l, BingPreview/Jan 2015, Yahoo Slurp/Jan 2015, YandexBot/Jan 2015, Android/4.4.2, Safari/7/iOS 7.1, Safari/8/OS X 10.10, Safari/8/iOS 8.1.2, Safari/7/OS X 10.9, Safari/6/iOS 6.0.1, Firefox/31.3.0 ESR/Win 7, Baidu/Jan 2015, IE/11/Win 8.1, IE/11/Win 7, IE Mobile/11/Win Phone 8.1, Android/5.0.0, Java/8u31, Chrome/42/OS X, Googlebot/Feb 2015, Android/4.1.1, Android/4.0.4, Safari/6.0.4/OS X 10.8.4, Android/4.2.2, Android/4.3, Safari/5.1.9/OS X 10.6.8, Firefox/37/OS X, OpenSSL/0.9.8y, Java/7u25, IE/8-10/Win 7, IE/7/Vista, IE Mobile/10/Win Phone 8.0, Android/2.3.7, Java/6u45, IE/8/XP
|
||||||
|
|
||||||
PORT 587
|
PORT 587
|
||||||
--------
|
--------
|
||||||
@@ -192,9 +163,6 @@ PORT 587
|
|||||||
CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok
|
CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok
|
||||||
AES128-SHA - 128 bits 250 2.0.0 Ok
|
AES128-SHA - 128 bits 250 2.0.0 Ok
|
||||||
|
|
||||||
* SSLV3 Cipher Suites:
|
|
||||||
Server rejected all cipher suites.
|
|
||||||
|
|
||||||
* TLSV1 Cipher Suites:
|
* TLSV1 Cipher Suites:
|
||||||
Preferred:
|
Preferred:
|
||||||
ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok
|
ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok
|
||||||
@@ -210,11 +178,14 @@ PORT 587
|
|||||||
DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
DHE-RSA-AES128-SHA DH-2048 bits 128 bits 250 2.0.0 Ok
|
||||||
SEED-SHA - 128 bits 250 2.0.0 Ok
|
SEED-SHA - 128 bits 250 2.0.0 Ok
|
||||||
CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok
|
CAMELLIA128-SHA - 128 bits 250 2.0.0 Ok
|
||||||
AES128-SHA - 128 bits 250 2.0.0 Ok
|
AES128-SHA - 128 bits 250 2.0.0 Ok
|
||||||
|
|
||||||
|
* SSLV3 Cipher Suites:
|
||||||
|
Server rejected all cipher suites.
|
||||||
|
|
||||||
Should Not Offer: AES128-GCM-SHA256, AES128-SHA, AES128-SHA256, AES256-GCM-SHA384, AES256-SHA, AES256-SHA256, CAMELLIA128-SHA, CAMELLIA256-SHA, DHE-RSA-CAMELLIA128-SHA, DHE-RSA-CAMELLIA256-SHA, DHE-RSA-SEED-SHA, SEED-SHA
|
Should Not Offer: AES128-GCM-SHA256, AES128-SHA, AES128-SHA256, AES256-GCM-SHA384, AES256-SHA, AES256-SHA256, CAMELLIA128-SHA, CAMELLIA256-SHA, DHE-RSA-CAMELLIA128-SHA, DHE-RSA-CAMELLIA256-SHA, DHE-RSA-SEED-SHA, SEED-SHA
|
||||||
Could Also Offer: DHE-DSS-AES128-GCM-SHA256, DHE-DSS-AES128-SHA256, DHE-DSS-AES256-GCM-SHA384, DHE-DSS-AES256-SHA, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384
|
Could Also Offer: DHE-DSS-AES128-GCM-SHA256, DHE-DSS-AES128-SHA256, DHE-DSS-AES256-GCM-SHA384, DHE-DSS-AES256-SHA, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384
|
||||||
Supported Clients: OpenSSL/1.0.2, Yahoo Slurp/Jan 2015, BingPreview/Jan 2015, OpenSSL/1.0.1l, YandexBot/Jan 2015, Android/4.4.2, Safari/8/iOS 8.1.2, Safari/7/OS X 10.9, Safari/8/OS X 10.10, Safari/7/iOS 7.1, IE Mobile/11/Win Phone 8.1, IE/11/Win 8.1, IE/11/Win 7, Safari/6/iOS 6.0.1, Firefox/31.3.0 ESR/Win 7, Baidu/Jan 2015, Android/5.0.0, Chrome/42/OS X, Java/8u31, Googlebot/Feb 2015, Firefox/37/OS X, Android/4.0.4, Android/4.1.1, Safari/6.0.4/OS X 10.8.4, Android/4.3, Android/4.2.2, Safari/5.1.9/OS X 10.6.8, OpenSSL/0.9.8y, IE/7/Vista, IE/8-10/Win 7, IE Mobile/10/Win Phone 8.0, Java/7u25, Java/6u45, Android/2.3.7
|
Supported Clients: OpenSSL/1.0.2, OpenSSL/1.0.1l, BingPreview/Jan 2015, Yahoo Slurp/Jan 2015, YandexBot/Jan 2015, Android/4.4.2, Safari/7/iOS 7.1, IE/11/Win 8.1, Safari/8/iOS 8.1.2, IE/11/Win 7, IE Mobile/11/Win Phone 8.1, Safari/8/OS X 10.10, Safari/7/OS X 10.9, Safari/6/iOS 6.0.1, Firefox/31.3.0 ESR/Win 7, Baidu/Jan 2015, Chrome/42/OS X, Android/5.0.0, Java/8u31, Googlebot/Feb 2015, Firefox/37/OS X, Android/4.0.4, Android/4.1.1, Safari/6.0.4/OS X 10.8.4, Android/4.2.2, Android/4.3, Safari/5.1.9/OS X 10.6.8, IE/8-10/Win 7, IE/7/Vista, IE Mobile/10/Win Phone 8.0, OpenSSL/0.9.8y, Java/7u25, Java/6u45, Android/2.3.7
|
||||||
|
|
||||||
PORT 443
|
PORT 443
|
||||||
--------
|
--------
|
||||||
@@ -226,22 +197,22 @@ PORT 443
|
|||||||
Client-initiated Renegotiations: OK - Rejected
|
Client-initiated Renegotiations: OK - Rejected
|
||||||
Secure Renegotiation: OK - Supported
|
Secure Renegotiation: OK - Supported
|
||||||
|
|
||||||
* HTTP Strict Transport Security:
|
* OpenSSL Heartbleed:
|
||||||
OK - HSTS header received: max-age=31536000
|
OK - Not vulnerable to Heartbleed
|
||||||
|
|
||||||
* Session Resumption:
|
* Session Resumption:
|
||||||
With Session IDs: OK - Supported (5 successful, 0 failed, 0 errors, 5 total attempts).
|
With Session IDs: OK - Supported (5 successful, 0 failed, 0 errors, 5 total attempts).
|
||||||
With TLS Session Tickets: OK - Supported
|
With TLS Session Tickets: OK - Supported
|
||||||
|
|
||||||
* OpenSSL Heartbleed:
|
* HTTP Strict Transport Security:
|
||||||
OK - Not vulnerable to Heartbleed
|
OK - HSTS header received: max-age=31536000
|
||||||
|
|
||||||
|
Unhandled exception when processing --chrome_sha1:
|
||||||
|
exceptions.TypeError - Incorrect padding
|
||||||
|
|
||||||
* SSLV2 Cipher Suites:
|
* SSLV2 Cipher Suites:
|
||||||
Server rejected all cipher suites.
|
Server rejected all cipher suites.
|
||||||
|
|
||||||
* Google Chrome SHA-1 Deprecation Status:
|
|
||||||
OK - Leaf certificate expires before 2016.
|
|
||||||
|
|
||||||
* TLSV1_2 Cipher Suites:
|
* TLSV1_2 Cipher Suites:
|
||||||
Preferred:
|
Preferred:
|
||||||
ECDHE-RSA-AES128-GCM-SHA256 ECDH-256 bits 128 bits HTTP 200 OK
|
ECDHE-RSA-AES128-GCM-SHA256 ECDH-256 bits 128 bits HTTP 200 OK
|
||||||
@@ -270,9 +241,6 @@ PORT 443
|
|||||||
DHE-RSA-AES128-SHA DH-2048 bits 128 bits HTTP 200 OK
|
DHE-RSA-AES128-SHA DH-2048 bits 128 bits HTTP 200 OK
|
||||||
DES-CBC3-SHA - 112 bits HTTP 200 OK
|
DES-CBC3-SHA - 112 bits HTTP 200 OK
|
||||||
|
|
||||||
* SSLV3 Cipher Suites:
|
|
||||||
Server rejected all cipher suites.
|
|
||||||
|
|
||||||
* TLSV1 Cipher Suites:
|
* TLSV1 Cipher Suites:
|
||||||
Preferred:
|
Preferred:
|
||||||
ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits HTTP 200 OK
|
ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits HTTP 200 OK
|
||||||
@@ -281,11 +249,14 @@ PORT 443
|
|||||||
DHE-RSA-AES256-SHA DH-2048 bits 256 bits HTTP 200 OK
|
DHE-RSA-AES256-SHA DH-2048 bits 256 bits HTTP 200 OK
|
||||||
ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits HTTP 200 OK
|
ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits HTTP 200 OK
|
||||||
DHE-RSA-AES128-SHA DH-2048 bits 128 bits HTTP 200 OK
|
DHE-RSA-AES128-SHA DH-2048 bits 128 bits HTTP 200 OK
|
||||||
DES-CBC3-SHA - 112 bits HTTP 200 OK
|
DES-CBC3-SHA - 112 bits HTTP 200 OK
|
||||||
|
|
||||||
|
* SSLV3 Cipher Suites:
|
||||||
|
Server rejected all cipher suites.
|
||||||
|
|
||||||
Should Not Offer: (none -- good)
|
Should Not Offer: (none -- good)
|
||||||
Could Also Offer: AES128-GCM-SHA256, AES128-SHA, AES128-SHA256, AES256-GCM-SHA384, AES256-SHA, AES256-SHA256, CAMELLIA128-SHA, CAMELLIA256-SHA, DHE-DSS-AES128-GCM-SHA256, DHE-DSS-AES128-SHA, DHE-DSS-AES128-SHA256, DHE-DSS-AES256-GCM-SHA384, DHE-DSS-AES256-SHA, DHE-DSS-AES256-SHA256, DHE-DSS-CAMELLIA128-SHA, DHE-DSS-CAMELLIA256-SHA, DHE-RSA-CAMELLIA128-SHA, DHE-RSA-CAMELLIA256-SHA, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384, SRP-AES-128-CBC-SHA, SRP-AES-256-CBC-SHA, SRP-DSS-AES-128-CBC-SHA, SRP-DSS-AES-256-CBC-SHA, SRP-RSA-AES-128-CBC-SHA, SRP-RSA-AES-256-CBC-SHA
|
Could Also Offer: AES128-GCM-SHA256, AES128-SHA, AES128-SHA256, AES256-GCM-SHA384, AES256-SHA, AES256-SHA256, CAMELLIA128-SHA, CAMELLIA256-SHA, DH-DSS-AES128-GCM-SHA256, DH-DSS-AES128-SHA, DH-DSS-AES128-SHA256, DH-DSS-AES256-GCM-SHA384, DH-DSS-AES256-SHA, DH-DSS-AES256-SHA256, DH-DSS-CAMELLIA128-SHA, DH-DSS-CAMELLIA256-SHA, DH-RSA-AES128-GCM-SHA256, DH-RSA-AES128-SHA, DH-RSA-AES128-SHA256, DH-RSA-AES256-GCM-SHA384, DH-RSA-AES256-SHA, DH-RSA-AES256-SHA256, DH-RSA-CAMELLIA128-SHA, DH-RSA-CAMELLIA256-SHA, DHE-DSS-AES128-GCM-SHA256, DHE-DSS-AES128-SHA, DHE-DSS-AES128-SHA256, DHE-DSS-AES256-GCM-SHA384, DHE-DSS-AES256-SHA, DHE-DSS-AES256-SHA256, DHE-DSS-CAMELLIA128-SHA, DHE-DSS-CAMELLIA256-SHA, DHE-RSA-CAMELLIA128-SHA, DHE-RSA-CAMELLIA256-SHA, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384, SRP-AES-128-CBC-SHA, SRP-AES-256-CBC-SHA, SRP-DSS-AES-128-CBC-SHA, SRP-DSS-AES-256-CBC-SHA, SRP-RSA-AES-128-CBC-SHA, SRP-RSA-AES-256-CBC-SHA
|
||||||
Supported Clients: YandexBot/Jan 2015, OpenSSL/1.0.2, Yahoo Slurp/Jan 2015, BingPreview/Jan 2015, OpenSSL/1.0.1l, Android/4.4.2, Safari/8/iOS 8.1.2, Safari/8/OS X 10.10, Safari/7/OS X 10.9, Safari/7/iOS 7.1, Safari/6/iOS 6.0.1, Android/5.0.0, Chrome/42/OS X, IE/11/Win 8.1, IE/11/Win 7, Java/8u31, IE Mobile/11/Win Phone 8.1, Googlebot/Feb 2015, Firefox/37/OS X, Firefox/31.3.0 ESR/Win 7, Android/4.2.2, Android/4.0.4, Baidu/Jan 2015, Safari/5.1.9/OS X 10.6.8, Android/4.1.1, Safari/6.0.4/OS X 10.8.4, Android/4.3, OpenSSL/0.9.8y, IE/7/Vista, IE/8-10/Win 7, IE Mobile/10/Win Phone 8.0, Java/7u25, Java/6u45, Android/2.3.7, IE/8/XP
|
Supported Clients: OpenSSL/1.0.2, OpenSSL/1.0.1l, BingPreview/Jan 2015, YandexBot/Jan 2015, Yahoo Slurp/Jan 2015, Android/4.4.2, Safari/7/iOS 7.1, Safari/8/OS X 10.10, Safari/8/iOS 8.1.2, Safari/7/OS X 10.9, Safari/6/iOS 6.0.1, Chrome/42/OS X, IE/11/Win 8.1, IE/11/Win 7, Android/5.0.0, Java/8u31, IE Mobile/11/Win Phone 8.1, Googlebot/Feb 2015, Firefox/31.3.0 ESR/Win 7, Firefox/37/OS X, Android/4.1.1, Android/4.0.4, Baidu/Jan 2015, Safari/6.0.4/OS X 10.8.4, Android/4.2.2, Android/4.3, Safari/5.1.9/OS X 10.6.8, IE/8-10/Win 7, IE/7/Vista, OpenSSL/0.9.8y, IE Mobile/10/Win Phone 8.0, Java/7u25, Android/2.3.7, Java/6u45, IE/8/XP
|
||||||
|
|
||||||
PORT 993
|
PORT 993
|
||||||
--------
|
--------
|
||||||
@@ -299,13 +270,13 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec
|
|||||||
* OpenSSL Heartbleed:
|
* OpenSSL Heartbleed:
|
||||||
OK - Not vulnerable to Heartbleed
|
OK - Not vulnerable to Heartbleed
|
||||||
|
|
||||||
* SSLV2 Cipher Suites:
|
|
||||||
Server rejected all cipher suites.
|
|
||||||
|
|
||||||
* Session Resumption:
|
* Session Resumption:
|
||||||
With Session IDs: NOT SUPPORTED (0 successful, 5 failed, 0 errors, 5 total attempts).
|
With Session IDs: NOT SUPPORTED (0 successful, 5 failed, 0 errors, 5 total attempts).
|
||||||
With TLS Session Tickets: NOT SUPPORTED - TLS ticket assigned but not accepted.
|
With TLS Session Tickets: NOT SUPPORTED - TLS ticket assigned but not accepted.
|
||||||
|
|
||||||
|
* SSLV2 Cipher Suites:
|
||||||
|
Server rejected all cipher suites.
|
||||||
|
|
||||||
* TLSV1_2 Cipher Suites:
|
* TLSV1_2 Cipher Suites:
|
||||||
Preferred:
|
Preferred:
|
||||||
ECDHE-RSA-AES256-SHA ECDH-384 bits 256 bits
|
ECDHE-RSA-AES256-SHA ECDH-384 bits 256 bits
|
||||||
@@ -336,9 +307,6 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec
|
|||||||
CAMELLIA128-SHA - 128 bits
|
CAMELLIA128-SHA - 128 bits
|
||||||
AES128-SHA - 128 bits
|
AES128-SHA - 128 bits
|
||||||
|
|
||||||
* SSLV3 Cipher Suites:
|
|
||||||
Server rejected all cipher suites.
|
|
||||||
|
|
||||||
* TLSV1 Cipher Suites:
|
* TLSV1 Cipher Suites:
|
||||||
Preferred:
|
Preferred:
|
||||||
ECDHE-RSA-AES256-SHA ECDH-384 bits 256 bits
|
ECDHE-RSA-AES256-SHA ECDH-384 bits 256 bits
|
||||||
@@ -352,11 +320,14 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec
|
|||||||
DHE-RSA-CAMELLIA128-SHA DH-1024 bits 128 bits
|
DHE-RSA-CAMELLIA128-SHA DH-1024 bits 128 bits
|
||||||
DHE-RSA-AES128-SHA DH-1024 bits 128 bits
|
DHE-RSA-AES128-SHA DH-1024 bits 128 bits
|
||||||
CAMELLIA128-SHA - 128 bits
|
CAMELLIA128-SHA - 128 bits
|
||||||
AES128-SHA - 128 bits
|
AES128-SHA - 128 bits
|
||||||
|
|
||||||
|
* SSLV3 Cipher Suites:
|
||||||
|
Server rejected all cipher suites.
|
||||||
|
|
||||||
Should Not Offer: AES128-SHA, AES256-SHA, CAMELLIA128-SHA, CAMELLIA256-SHA, DHE-RSA-CAMELLIA128-SHA, DHE-RSA-CAMELLIA256-SHA
|
Should Not Offer: AES128-SHA, AES256-SHA, CAMELLIA128-SHA, CAMELLIA256-SHA, DHE-RSA-CAMELLIA128-SHA, DHE-RSA-CAMELLIA256-SHA
|
||||||
Could Also Offer: DHE-DSS-AES128-GCM-SHA256, DHE-DSS-AES128-SHA256, DHE-DSS-AES256-GCM-SHA384, DHE-DSS-AES256-SHA, DHE-RSA-AES128-GCM-SHA256, DHE-RSA-AES128-SHA256, DHE-RSA-AES256-GCM-SHA384, DHE-RSA-AES256-SHA256, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384, ECDHE-RSA-AES128-GCM-SHA256, ECDHE-RSA-AES128-SHA256, ECDHE-RSA-AES256-GCM-SHA384, ECDHE-RSA-AES256-SHA384
|
Could Also Offer: DHE-DSS-AES128-GCM-SHA256, DHE-DSS-AES128-SHA256, DHE-DSS-AES256-GCM-SHA384, DHE-DSS-AES256-SHA, DHE-RSA-AES128-GCM-SHA256, DHE-RSA-AES128-SHA256, DHE-RSA-AES256-GCM-SHA384, DHE-RSA-AES256-SHA256, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384, ECDHE-RSA-AES128-GCM-SHA256, ECDHE-RSA-AES128-SHA256, ECDHE-RSA-AES256-GCM-SHA384, ECDHE-RSA-AES256-SHA384
|
||||||
Supported Clients: OpenSSL/1.0.2, Baidu/Jan 2015, Yahoo Slurp/Jan 2015, BingPreview/Jan 2015, OpenSSL/1.0.1l, Firefox/31.3.0 ESR/Win 7, Googlebot/Feb 2015, Android/4.2.2, Android/5.0.0, Android/4.0.4, Safari/8/iOS 8.1.2, Safari/7/OS X 10.9, YandexBot/Jan 2015, Safari/8/OS X 10.10, Safari/7/iOS 7.1, Chrome/42/OS X, Safari/5.1.9/OS X 10.6.8, Android/4.1.1, Firefox/37/OS X, Safari/6.0.4/OS X 10.8.4, Android/4.3, Safari/6/iOS 6.0.1, Android/4.4.2, OpenSSL/0.9.8y, IE Mobile/11/Win Phone 8.1, IE/7/Vista, IE/11/Win 8.1, IE/11/Win 7, IE/8-10/Win 7, IE Mobile/10/Win Phone 8.0, Java/8u31, Java/7u25, Java/6u45, Android/2.3.7
|
Supported Clients: OpenSSL/1.0.2, Firefox/31.3.0 ESR/Win 7, OpenSSL/1.0.1l, BingPreview/Jan 2015, Yahoo Slurp/Jan 2015, Baidu/Jan 2015, Safari/7/iOS 7.1, Chrome/42/OS X, Googlebot/Feb 2015, Android/4.0.4, Safari/8/iOS 8.1.2, Android/4.1.1, Android/5.0.0, Safari/6/iOS 6.0.1, YandexBot/Jan 2015, Safari/6.0.4/OS X 10.8.4, Android/4.2.2, Safari/8/OS X 10.10, Firefox/37/OS X, Safari/7/OS X 10.9, Android/4.3, Safari/5.1.9/OS X 10.6.8, Android/4.4.2, IE/8-10/Win 7, IE/7/Vista, IE/11/Win 8.1, IE/11/Win 7, OpenSSL/0.9.8y, IE Mobile/10/Win Phone 8.0, IE Mobile/11/Win Phone 8.1, Java/7u25, Java/8u31, Java/6u45, Android/2.3.7
|
||||||
|
|
||||||
PORT 995
|
PORT 995
|
||||||
--------
|
--------
|
||||||
@@ -370,13 +341,13 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec
|
|||||||
* OpenSSL Heartbleed:
|
* OpenSSL Heartbleed:
|
||||||
OK - Not vulnerable to Heartbleed
|
OK - Not vulnerable to Heartbleed
|
||||||
|
|
||||||
* SSLV2 Cipher Suites:
|
|
||||||
Server rejected all cipher suites.
|
|
||||||
|
|
||||||
* Session Resumption:
|
* Session Resumption:
|
||||||
With Session IDs: NOT SUPPORTED (0 successful, 5 failed, 0 errors, 5 total attempts).
|
With Session IDs: NOT SUPPORTED (0 successful, 5 failed, 0 errors, 5 total attempts).
|
||||||
With TLS Session Tickets: NOT SUPPORTED - TLS ticket assigned but not accepted.
|
With TLS Session Tickets: NOT SUPPORTED - TLS ticket assigned but not accepted.
|
||||||
|
|
||||||
|
* SSLV2 Cipher Suites:
|
||||||
|
Server rejected all cipher suites.
|
||||||
|
|
||||||
* TLSV1_2 Cipher Suites:
|
* TLSV1_2 Cipher Suites:
|
||||||
Preferred:
|
Preferred:
|
||||||
ECDHE-RSA-AES256-SHA ECDH-384 bits 256 bits
|
ECDHE-RSA-AES256-SHA ECDH-384 bits 256 bits
|
||||||
@@ -407,9 +378,6 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec
|
|||||||
CAMELLIA128-SHA - 128 bits
|
CAMELLIA128-SHA - 128 bits
|
||||||
AES128-SHA - 128 bits
|
AES128-SHA - 128 bits
|
||||||
|
|
||||||
* SSLV3 Cipher Suites:
|
|
||||||
Server rejected all cipher suites.
|
|
||||||
|
|
||||||
* TLSV1 Cipher Suites:
|
* TLSV1 Cipher Suites:
|
||||||
Preferred:
|
Preferred:
|
||||||
ECDHE-RSA-AES256-SHA ECDH-384 bits 256 bits
|
ECDHE-RSA-AES256-SHA ECDH-384 bits 256 bits
|
||||||
@@ -423,9 +391,12 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec
|
|||||||
DHE-RSA-CAMELLIA128-SHA DH-1024 bits 128 bits
|
DHE-RSA-CAMELLIA128-SHA DH-1024 bits 128 bits
|
||||||
DHE-RSA-AES128-SHA DH-1024 bits 128 bits
|
DHE-RSA-AES128-SHA DH-1024 bits 128 bits
|
||||||
CAMELLIA128-SHA - 128 bits
|
CAMELLIA128-SHA - 128 bits
|
||||||
AES128-SHA - 128 bits
|
AES128-SHA - 128 bits
|
||||||
|
|
||||||
|
* SSLV3 Cipher Suites:
|
||||||
|
Server rejected all cipher suites.
|
||||||
|
|
||||||
Should Not Offer: AES128-SHA, AES256-SHA, CAMELLIA128-SHA, CAMELLIA256-SHA, DHE-RSA-CAMELLIA128-SHA, DHE-RSA-CAMELLIA256-SHA
|
Should Not Offer: AES128-SHA, AES256-SHA, CAMELLIA128-SHA, CAMELLIA256-SHA, DHE-RSA-CAMELLIA128-SHA, DHE-RSA-CAMELLIA256-SHA
|
||||||
Could Also Offer: DHE-DSS-AES128-GCM-SHA256, DHE-DSS-AES128-SHA256, DHE-DSS-AES256-GCM-SHA384, DHE-DSS-AES256-SHA, DHE-RSA-AES128-GCM-SHA256, DHE-RSA-AES128-SHA256, DHE-RSA-AES256-GCM-SHA384, DHE-RSA-AES256-SHA256, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384, ECDHE-RSA-AES128-GCM-SHA256, ECDHE-RSA-AES128-SHA256, ECDHE-RSA-AES256-GCM-SHA384, ECDHE-RSA-AES256-SHA384
|
Could Also Offer: DHE-DSS-AES128-GCM-SHA256, DHE-DSS-AES128-SHA256, DHE-DSS-AES256-GCM-SHA384, DHE-DSS-AES256-SHA, DHE-RSA-AES128-GCM-SHA256, DHE-RSA-AES128-SHA256, DHE-RSA-AES256-GCM-SHA384, DHE-RSA-AES256-SHA256, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384, ECDHE-RSA-AES128-GCM-SHA256, ECDHE-RSA-AES128-SHA256, ECDHE-RSA-AES256-GCM-SHA384, ECDHE-RSA-AES256-SHA384
|
||||||
Supported Clients: OpenSSL/1.0.2, Baidu/Jan 2015, Yahoo Slurp/Jan 2015, BingPreview/Jan 2015, OpenSSL/1.0.1l, Firefox/31.3.0 ESR/Win 7, Googlebot/Feb 2015, Android/4.2.2, Android/5.0.0, Android/4.0.4, Safari/8/iOS 8.1.2, Safari/7/OS X 10.9, YandexBot/Jan 2015, Safari/8/OS X 10.10, Safari/7/iOS 7.1, Chrome/42/OS X, Safari/5.1.9/OS X 10.6.8, Android/4.1.1, Firefox/37/OS X, Safari/6.0.4/OS X 10.8.4, Android/4.3, Safari/6/iOS 6.0.1, Android/4.4.2, OpenSSL/0.9.8y, IE Mobile/11/Win Phone 8.1, IE/7/Vista, IE/11/Win 8.1, IE/11/Win 7, IE/8-10/Win 7, IE Mobile/10/Win Phone 8.0, Java/8u31, Java/7u25, Java/6u45, Android/2.3.7
|
Supported Clients: OpenSSL/1.0.2, Firefox/31.3.0 ESR/Win 7, OpenSSL/1.0.1l, BingPreview/Jan 2015, Yahoo Slurp/Jan 2015, Baidu/Jan 2015, Safari/7/iOS 7.1, Chrome/42/OS X, Googlebot/Feb 2015, Android/4.0.4, Safari/8/iOS 8.1.2, Android/4.1.1, Android/5.0.0, Safari/6/iOS 6.0.1, YandexBot/Jan 2015, Safari/6.0.4/OS X 10.8.4, Android/4.2.2, Safari/8/OS X 10.10, Firefox/37/OS X, Safari/7/OS X 10.9, Android/4.3, Safari/5.1.9/OS X 10.6.8, Android/4.4.2, IE/8-10/Win 7, IE/7/Vista, IE/11/Win 8.1, IE/11/Win 7, OpenSSL/0.9.8y, IE Mobile/10/Win Phone 8.0, IE Mobile/11/Win Phone 8.1, Java/7u25, Java/8u31, Java/6u45, Android/2.3.7
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user