mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2026-03-12 17:07:23 +01:00
Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
35
CHANGELOG.md
35
CHANGELOG.md
@@ -1,6 +1,41 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
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)
|
||||
--------------------
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ by me:
|
||||
$ curl -s https://keybase.io/joshdata/key.asc | gpg --import
|
||||
gpg: key C10BDD81: public key "Joshua Tauberer <jt@occams.info>" imported
|
||||
|
||||
$ git verify-tag v0.18
|
||||
$ git verify-tag v0.19
|
||||
gpg: Signature made ..... using RSA key ID C10BDD81
|
||||
gpg: Good signature from "Joshua Tauberer <jt@occams.info>"
|
||||
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:
|
||||
|
||||
$ git checkout v0.17c
|
||||
$ git checkout v0.19
|
||||
|
||||
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]
|
||||
# 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.
|
||||
ignoreip = 127.0.0.1/8 PUBLIC_IP
|
||||
|
||||
# JAILS
|
||||
|
||||
[ssh]
|
||||
maxretry = 7
|
||||
bantime = 3600
|
||||
|
||||
[ssh-ddos]
|
||||
enabled = true
|
||||
|
||||
[sasl]
|
||||
enabled = true
|
||||
|
||||
[dovecot]
|
||||
enabled = true
|
||||
filter = dovecotimap
|
||||
logpath = /var/log/mail.log
|
||||
findtime = 30
|
||||
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]
|
||||
enabled = true
|
||||
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.
|
||||
# So the notification is ommited. This will prevent message appearing in the mail.log that mail
|
||||
# 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-Content-Type-Options nosniff;
|
||||
add_header Content-Security-Policy "frame-ancestors 'none';";
|
||||
add_header Strict-Transport-Security max-age=31536000;
|
||||
}
|
||||
|
||||
# ownCloud configuration.
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
# This script performs a backup of all user data:
|
||||
# 1) System services are stopped.
|
||||
# 2) An incremental encrypted backup is made using duplicity.
|
||||
# 3) The stopped services are restarted.
|
||||
# 4) STORAGE_ROOT/backup/after-backup is executd if it exists.
|
||||
# 2) STORAGE_ROOT/backup/before-backup is executed if it exists.
|
||||
# 3) An incremental encrypted backup is made using duplicity.
|
||||
# 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 dateutil.parser, dateutil.relativedelta, dateutil.tz
|
||||
@@ -258,6 +259,15 @@ def perform_backup(full_backup):
|
||||
service_command("postfix", "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!).
|
||||
# --allow-source-mismatch is needed in case the box's hostname is changed
|
||||
# after the first backup. See #396.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import os, os.path, re, json
|
||||
import os, os.path, re, json, time
|
||||
import subprocess
|
||||
|
||||
from functools import wraps
|
||||
|
||||
from flask import Flask, request, render_template, abort, Response, send_from_directory, make_response
|
||||
@@ -45,6 +46,9 @@ def authorized_personnel_only(viewfunc):
|
||||
privs = []
|
||||
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?
|
||||
if "admin" in privs:
|
||||
# Call view func.
|
||||
@@ -117,6 +121,9 @@ def me():
|
||||
try:
|
||||
email, privs = auth_service.authenticate(request, env)
|
||||
except ValueError as e:
|
||||
# Log the failed login
|
||||
log_failed_login(request)
|
||||
|
||||
return json_response({
|
||||
"status": "invalid",
|
||||
"reason": "Incorrect username or password",
|
||||
@@ -583,6 +590,22 @@ def munin_cgi(filename):
|
||||
app.logger.warning("munin_cgi: munin-cgi-graph returned 404 status code. PATH_INFO=%s", env['PATH_INFO'])
|
||||
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
|
||||
|
||||
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; "):
|
||||
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.
|
||||
records.sort(key = lambda rec : list(reversed(rec[0].split(".")) if rec[0] is not None else ""))
|
||||
|
||||
@@ -1,25 +1,45 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import os.path
|
||||
import re
|
||||
from collections import defaultdict
|
||||
import re, os.path
|
||||
|
||||
import dateutil.parser
|
||||
|
||||
import mailconfig
|
||||
import utils
|
||||
|
||||
|
||||
def scan_mail_log(logger, env):
|
||||
""" Scan the system's mail log files and collect interesting data
|
||||
|
||||
This function scans the 2 most recent mail log files in /var/log/.
|
||||
|
||||
Args:
|
||||
logger (ConsoleOutput): Object used for writing messages to the console
|
||||
env (dict): Dictionary containing MiaB settings
|
||||
"""
|
||||
|
||||
collector = {
|
||||
"other-services": set(),
|
||||
"imap-logins": { },
|
||||
"postgrey": { },
|
||||
"rejected-mail": { },
|
||||
"activity-by-hour": { "imap-logins": defaultdict(int), "smtp-sends": defaultdict(int) },
|
||||
"imap-logins": {},
|
||||
"pop3-logins": {},
|
||||
"postgrey": {},
|
||||
"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))
|
||||
)
|
||||
}
|
||||
|
||||
collector["real_mail_addresses"] = set(mailconfig.get_mail_users(env)) | set(alias[0] for alias in mailconfig.get_mail_aliases(env))
|
||||
|
||||
for fn in ('/var/log/mail.log.1', '/var/log/mail.log'):
|
||||
if not os.path.exists(fn): continue
|
||||
if not os.path.exists(fn):
|
||||
continue
|
||||
with open(fn, 'rb') as log:
|
||||
for line in log:
|
||||
line = line.decode("utf8", errors='replace')
|
||||
@@ -27,18 +47,30 @@ def scan_mail_log(logger, env):
|
||||
|
||||
if collector["imap-logins"]:
|
||||
logger.add_heading("Recent IMAP Logins")
|
||||
logger.print_block("The most recent login from each remote IP adddress is show.")
|
||||
logger.print_block("The most recent login from each remote IP adddress is shown.")
|
||||
for k in utils.sort_email_addresses(collector["imap-logins"], env):
|
||||
for ip, date in sorted(collector["imap-logins"][k].items(), key = lambda kv : kv[1]):
|
||||
for ip, date in sorted(collector["imap-logins"][k].items(), key=lambda kv: kv[1]):
|
||||
logger.print_line(k + "\t" + str(date) + "\t" + ip)
|
||||
|
||||
if collector["pop3-logins"]:
|
||||
logger.add_heading("Recent POP3 Logins")
|
||||
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" + "\t" + "sender" + "\t" + "delivered")
|
||||
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):
|
||||
for (client_address, sender), (first_date, delivered_date) in sorted(collector["postgrey"][recipient].items(), key = lambda kv : kv[1][0]):
|
||||
logger.print_line(recipient + "\t" + str(first_date) + "\t" + sender + "\t" + (("delivered " + str(delivered_date)) if delivered_date else "no retry yet"))
|
||||
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")
|
||||
@@ -48,51 +80,73 @@ def scan_mail_log(logger, env):
|
||||
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%d" % (h, collector["activity-by-hour"]["imap-logins"][h], collector["activity-by-hour"]["smtp-sends"][h] ))
|
||||
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"]))
|
||||
|
||||
|
||||
def scan_mail_log_line(line, collector):
|
||||
""" Scan a log line and extract interesting data """
|
||||
|
||||
m = re.match(r"(\S+ \d+ \d+:\d+:\d+) (\S+) (\S+?)(\[\d+\])?: (.*)", line)
|
||||
if not m: return
|
||||
|
||||
if not m:
|
||||
return
|
||||
|
||||
date, system, service, pid, log = m.groups()
|
||||
date = dateutil.parser.parse(date)
|
||||
|
||||
if service == "dovecot":
|
||||
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"):
|
||||
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)
|
||||
|
||||
def scan_dovecot_line(date, log, collector):
|
||||
m = re.match("imap-login: Login: user=<(.*?)>, method=PLAIN, rip=(.*?),", log)
|
||||
|
||||
def scan_dovecot_line(date, line, collector):
|
||||
""" Scan a dovecot log line and extract interesting data """
|
||||
|
||||
m = re.match("(imap|pop3)-login: Login: user=<(.*?)>, method=PLAIN, rip=(.*?),", line)
|
||||
|
||||
if m:
|
||||
login, ip = m.group(1), m.group(2)
|
||||
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["imap-logins"].setdefault(login, {})[ip] = date
|
||||
collector["activity-by-hour"]["imap-logins"][date.hour] += 1
|
||||
collector[logins_key].setdefault(login, {})[ip] = date
|
||||
collector["activity-by-hour"][logins_key][date.hour] += 1
|
||||
|
||||
|
||||
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 """
|
||||
|
||||
m = re.match("action=(greylist|pass), reason=(.*?), (?:delay=\d+, )?client_name=(.*), client_address=(.*), "
|
||||
"sender=(.*), recipient=(.*)",
|
||||
log)
|
||||
|
||||
if m:
|
||||
action, reason, client_name, client_address, sender, recipient = m.groups()
|
||||
key = (client_address, sender)
|
||||
@@ -101,14 +155,20 @@ def scan_postgrey_line(date, log, collector):
|
||||
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):
|
||||
""" Scan a postfix smtpd log line and extract interesting data """
|
||||
|
||||
# Check if the incomming mail was rejected
|
||||
|
||||
m = re.match("NOQUEUE: reject: RCPT from .*?: (.*?); from=<(.*?)> to=<(.*?)>", log)
|
||||
|
||||
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
|
||||
# skip this, if reported in the greylisting report
|
||||
if "Recipient address rejected: Greylisted" in message:
|
||||
return
|
||||
|
||||
@@ -122,15 +182,30 @@ def scan_postfix_smtpd_line(date, log, collector):
|
||||
if m:
|
||||
message = "domain blocked: " + m.group(2)
|
||||
|
||||
collector["rejected-mail"].setdefault(recipient, []).append( (date, sender, message) )
|
||||
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):
|
||||
""" Scan a postfix submission log line and extract interesting data """
|
||||
|
||||
m = re.match("([A-Z0-9]+): client=(\S+), sasl_method=PLAIN, sasl_username=(\S+)", log)
|
||||
|
||||
if m:
|
||||
procid, client, user = m.groups()
|
||||
# procid, client, user = m.groups()
|
||||
collector["activity-by-hour"]["smtp-sends"][date.hour] += 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
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
|
||||
|
||||
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):
|
||||
# run systems checks
|
||||
output.add_heading("System")
|
||||
@@ -61,33 +84,9 @@ def get_ssh_port():
|
||||
|
||||
def run_services_checks(env, output, pool):
|
||||
# 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
|
||||
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):
|
||||
if output2 is None: continue # skip check (e.g. no port was set, e.g. no sshd)
|
||||
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_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):
|
||||
# Check that SSH login with password is disabled. The openssh-server
|
||||
# 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")
|
||||
|
||||
check_ufw(env, output)
|
||||
|
||||
# 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.
|
||||
# See if we can reach one of Google's MTAs with a 5-second timeout.
|
||||
|
||||
@@ -106,6 +106,41 @@
|
||||
</table>
|
||||
</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>
|
||||
function show_aliases() {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<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>
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
</tbody>
|
||||
</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 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><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="/admin/munin">Munin Monitoring</a></li>
|
||||
<li><a href="/admin/munin" target="_blank">Munin Monitoring</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<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>
|
||||
|
||||
<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">
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
</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>
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
</div>
|
||||
</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>
|
||||
|
||||
|
||||
@@ -84,6 +84,48 @@
|
||||
</table>
|
||||
</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>
|
||||
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>";
|
||||
|
||||
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>"),
|
||||
"Set Password",
|
||||
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))
|
||||
|
||||
### 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
|
||||
-------------
|
||||
|
||||
@@ -80,7 +90,7 @@ The first step in resolving the destination server for an email address is perfo
|
||||
|
||||
### 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
|
||||
|
||||
@@ -101,7 +111,7 @@ Incoming Mail
|
||||
|
||||
### 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
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#########################################################
|
||||
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG=v0.18
|
||||
TAG=v0.19
|
||||
fi
|
||||
|
||||
# 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
|
||||
# machine has, so on a two-core machine that's 500 processes/100 users).
|
||||
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 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_key_file=$STORAGE_ROOT/ssl/ssl_private_key.pem \
|
||||
smtpd_tls_dh1024_param_file=$STORAGE_ROOT/ssl/dh2048.pem \
|
||||
smtpd_tls_protocols=\!SSLv2,\!SSLv3 \
|
||||
smtpd_tls_ciphers=medium \
|
||||
smtpd_tls_exclude_ciphers=aNULL \
|
||||
smtpd_tls_exclude_ciphers=aNULL,RC4 \
|
||||
smtpd_tls_received_header=yes
|
||||
|
||||
# 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
|
||||
# now see notices about trusted certs. The CA file is provided by the package `ca-certificates`.
|
||||
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_dns_support_level=dnssec \
|
||||
smtp_tls_CAfile=/etc/ssl/certs/ca-certificates.crt \
|
||||
|
||||
@@ -49,7 +49,7 @@ driver = sqlite
|
||||
connect = $db_path
|
||||
default_pass_scheme = SHA512-CRYPT
|
||||
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;
|
||||
EOF
|
||||
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
|
||||
|
||||
# Create an initial configuration file.
|
||||
TIMEZONE=$(cat /etc/timezone)
|
||||
instanceid=oc$(echo $PRIMARY_HOSTNAME | sha1sum | fold -w 10 | head -n 1)
|
||||
cat > $STORAGE_ROOT/owncloud/config.php <<EOF;
|
||||
<?php
|
||||
@@ -125,7 +124,6 @@ if [ ! -f $STORAGE_ROOT/owncloud/owncloud.db ]; then
|
||||
'mail_smtppassword' => '',
|
||||
'mail_from_address' => 'owncloud',
|
||||
'mail_domain' => '$PRIMARY_HOSTNAME',
|
||||
'logtimezone' => '$TIMEZONE',
|
||||
);
|
||||
?>
|
||||
EOF
|
||||
@@ -163,7 +161,11 @@ fi
|
||||
# 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.
|
||||
# * 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.
|
||||
TIMEZONE=$(cat /etc/timezone)
|
||||
CONFIG_TEMP=$(/bin/mktemp)
|
||||
php <<EOF > $CONFIG_TEMP && mv $CONFIG_TEMP $STORAGE_ROOT/owncloud/config.php;
|
||||
<?php
|
||||
@@ -175,6 +177,9 @@ include("$STORAGE_ROOT/owncloud/config.php");
|
||||
\$CONFIG['overwrite.cli.url'] = '/cloud';
|
||||
\$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 = ";
|
||||
var_export(\$CONFIG);
|
||||
echo ";";
|
||||
|
||||
@@ -47,15 +47,15 @@ if [ -e ~/.wgetrc ]; then
|
||||
exit
|
||||
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.
|
||||
#
|
||||
# Set ARM=1 to ignore this check if you have built the packages yourself. If you do this
|
||||
# you are on your own!
|
||||
ARCHITECTURE=$(uname -m)
|
||||
if [ "$ARCHITECTURE" != "x86_64" ]; then
|
||||
if [ "$ARCHITECTURE" != "x86_64" ] && [ "$ARCHITECTURE" != "i686" ]; 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"
|
||||
exit
|
||||
fi
|
||||
|
||||
@@ -291,10 +291,12 @@ restart_service resolvconf
|
||||
|
||||
# ### Fail2Ban Service
|
||||
|
||||
# Configure the Fail2Ban installation to prevent dumb bruce-force attacks against dovecot, postfix and ssh
|
||||
cat conf/fail2ban/jail.local \
|
||||
# Configure the Fail2Ban installation to prevent dumb bruce-force attacks against dovecot, postfix, ssh, etc.
|
||||
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" \
|
||||
> /etc/fail2ban/jail.local
|
||||
cp conf/fail2ban/dovecotimap.conf /etc/fail2ban/filter.d/dovecotimap.conf
|
||||
| sed "s#STORAGE_ROOT#$STORAGE_ROOT#" \
|
||||
> /etc/fail2ban/jail.d/mailinabox.conf
|
||||
cp -f conf/fail2ban/filter.d/* /etc/fail2ban/filter.d/
|
||||
|
||||
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.
|
||||
# Combine the Roundcube version number with the commit hash of vacation_sieve to track
|
||||
# whether we have the latest version.
|
||||
VERSION=1.1.5
|
||||
HASH=8A59D196EF0AA6D9C717B00699215135ABCB99CF
|
||||
VERSION=1.2.1
|
||||
HASH=81fbfba4683522f6e54006d0300a48e6da3f3bbd
|
||||
VACATION_SIEVE_VERSION=91ea6f52216390073d1f5b70b5f6bea0bfaee7e5
|
||||
PERSISTENT_LOGIN_VERSION=1e9d724476a370ce917a2fcd5b3217b0c306c24e
|
||||
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['temp_dir'] = '/tmp/roundcubemail/';
|
||||
\$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['imap_timeout'] = 15;
|
||||
\$config['smtp_server'] = 'tls://127.0.0.1';
|
||||
@@ -157,6 +157,12 @@ chmod 775 $STORAGE_ROOT/mail
|
||||
chown root.www-data $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.
|
||||
php5enmod mcrypt
|
||||
restart_service php5-fpm
|
||||
|
||||
195
tests/fail2ban.py
Normal file
195
tests/fail2ban.py
Normal file
@@ -0,0 +1,195 @@
|
||||
# Test that a box's fail2ban setting are working
|
||||
# correctly by attempting a bunch of failed logins.
|
||||
# Specify SSH login information the command line -
|
||||
# we use that to reset fail2ban after each test,
|
||||
# and we extract the hostname from that to open
|
||||
# connections to.
|
||||
######################################################################
|
||||
|
||||
import sys, os, time, functools
|
||||
|
||||
# parse command line
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: tests/fail2ban.py user@hostname")
|
||||
sys.exit(1)
|
||||
|
||||
ssh_user, hostname = sys.argv[1].split("@", 1)
|
||||
|
||||
# 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)
|
||||
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("ssh %s@%s \"%s\"" % (ssh_user, hostname, 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-SHA - 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-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
|
||||
@@ -43,8 +42,6 @@ PORT 25
|
||||
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
|
||||
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-SHA256 - 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
|
||||
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
|
||||
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
|
||||
AES128-SHA - 128 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
|
||||
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
|
||||
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
|
||||
|
||||
Should Not Offer: DHE-RSA-SEED-SHA, ECDHE-RSA-RC4-SHA, EDH-RSA-DES-CBC3-SHA, RC4-MD5, RC4-SHA, SEED-SHA
|
||||
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
|
||||
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
|
||||
* SSLV3 Cipher Suites:
|
||||
Server rejected all cipher suites.
|
||||
|
||||
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
|
||||
--------
|
||||
@@ -192,9 +163,6 @@ PORT 587
|
||||
CAMELLIA128-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:
|
||||
Preferred:
|
||||
ECDHE-RSA-AES256-SHA ECDH-256 bits 256 bits 250 2.0.0 Ok
|
||||
@@ -212,9 +180,12 @@ PORT 587
|
||||
CAMELLIA128-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
|
||||
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
|
||||
--------
|
||||
@@ -226,22 +197,22 @@ PORT 443
|
||||
Client-initiated Renegotiations: OK - Rejected
|
||||
Secure Renegotiation: OK - Supported
|
||||
|
||||
* HTTP Strict Transport Security:
|
||||
OK - HSTS header received: max-age=31536000
|
||||
* OpenSSL Heartbleed:
|
||||
OK - Not vulnerable to Heartbleed
|
||||
|
||||
* Session Resumption:
|
||||
With Session IDs: OK - Supported (5 successful, 0 failed, 0 errors, 5 total attempts).
|
||||
With TLS Session Tickets: OK - Supported
|
||||
|
||||
* OpenSSL Heartbleed:
|
||||
OK - Not vulnerable to Heartbleed
|
||||
* HTTP Strict Transport Security:
|
||||
OK - HSTS header received: max-age=31536000
|
||||
|
||||
Unhandled exception when processing --chrome_sha1:
|
||||
exceptions.TypeError - Incorrect padding
|
||||
|
||||
* SSLV2 Cipher Suites:
|
||||
Server rejected all cipher suites.
|
||||
|
||||
* Google Chrome SHA-1 Deprecation Status:
|
||||
OK - Leaf certificate expires before 2016.
|
||||
|
||||
* TLSV1_2 Cipher Suites:
|
||||
Preferred:
|
||||
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
|
||||
DES-CBC3-SHA - 112 bits HTTP 200 OK
|
||||
|
||||
* SSLV3 Cipher Suites:
|
||||
Server rejected all cipher suites.
|
||||
|
||||
* TLSV1 Cipher Suites:
|
||||
Preferred:
|
||||
ECDHE-RSA-AES128-SHA ECDH-256 bits 128 bits HTTP 200 OK
|
||||
@@ -283,9 +251,12 @@ PORT 443
|
||||
DHE-RSA-AES128-SHA DH-2048 bits 128 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)
|
||||
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
|
||||
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
|
||||
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: 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
|
||||
--------
|
||||
@@ -299,13 +270,13 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec
|
||||
* OpenSSL Heartbleed:
|
||||
OK - Not vulnerable to Heartbleed
|
||||
|
||||
* SSLV2 Cipher Suites:
|
||||
Server rejected all cipher suites.
|
||||
|
||||
* Session Resumption:
|
||||
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.
|
||||
|
||||
* SSLV2 Cipher Suites:
|
||||
Server rejected all cipher suites.
|
||||
|
||||
* TLSV1_2 Cipher Suites:
|
||||
Preferred:
|
||||
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
|
||||
AES128-SHA - 128 bits
|
||||
|
||||
* SSLV3 Cipher Suites:
|
||||
Server rejected all cipher suites.
|
||||
|
||||
* TLSV1 Cipher Suites:
|
||||
Preferred:
|
||||
ECDHE-RSA-AES256-SHA ECDH-384 bits 256 bits
|
||||
@@ -354,9 +322,12 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec
|
||||
CAMELLIA128-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
|
||||
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
|
||||
--------
|
||||
@@ -370,13 +341,13 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec
|
||||
* OpenSSL Heartbleed:
|
||||
OK - Not vulnerable to Heartbleed
|
||||
|
||||
* SSLV2 Cipher Suites:
|
||||
Server rejected all cipher suites.
|
||||
|
||||
* Session Resumption:
|
||||
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.
|
||||
|
||||
* SSLV2 Cipher Suites:
|
||||
Server rejected all cipher suites.
|
||||
|
||||
* TLSV1_2 Cipher Suites:
|
||||
Preferred:
|
||||
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
|
||||
AES128-SHA - 128 bits
|
||||
|
||||
* SSLV3 Cipher Suites:
|
||||
Server rejected all cipher suites.
|
||||
|
||||
* TLSV1 Cipher Suites:
|
||||
Preferred:
|
||||
ECDHE-RSA-AES256-SHA ECDH-384 bits 256 bits
|
||||
@@ -425,7 +393,10 @@ _nassl.OpenSSLError - error:140940F5:SSL routines:ssl3_read_bytes:unexpected rec
|
||||
CAMELLIA128-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
|
||||
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