Skip to content

Commit e135282

Browse files
committed
Manually merge PR132 to tune SFTP security settings for modern OS versions and try harder to mitigate Terrapin issue on some legacy systems
git-svn-id: svn+ssh://svn.code.sf.net/p/migrid/code/trunk@6149 b75ad72c-e7d7-11dd-a971-7dbc132099af
1 parent d68489b commit e135282

File tree

5 files changed

+113
-66
lines changed

5 files changed

+113
-66
lines changed

mig/install/openssh-MiG-sftp-subsys-template.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ HostKey __MIG_CERTS__/__SFTP_SUBSYS_ADDRESS__/server.key
3838

3939
# IMPORTANT: these are *generated* hardened values based on generateconf
4040
# invocation. Any permanent changes need to be made there.
41+
HostKeyAlgorithms __OPENSSH_HOSTKEYALGOS__
4142
KexAlgorithms __OPENSSH_KEXALGOS__
4243
Ciphers __OPENSSH_CIPHERS__
4344
MACs __OPENSSH_MACS__

mig/server/grid_sftp.py

Lines changed: 65 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,9 @@
8787
from mig.shared.base import invisible_path, force_utf8, force_unicode
8888
from mig.shared.conf import get_configuration_object
8989
from mig.shared.defaults import keyword_auto, STRONG_SSH_KEXALGOS, \
90-
STRONG_SSH_CIPHERS, STRONG_SSH_MACS, STRONG_SSH_LEGACY_KEXALGOS, \
91-
STRONG_SSH_LEGACY_MACS
90+
STRONG_SSH_CIPHERS, STRONG_SSH_MACS, LEGACY_SSH_KEXALGOS, \
91+
LEGACY_SSH_CIPHERS, LEGACY_SSH_MACS, FALLBACK_SSH_KEXALGOS, \
92+
FALLBACK_SSH_CIPHERS, FALLBACK_SSH_MACS
9293
from mig.shared.fileio import check_write_access, user_chroot_exceptions, \
9394
read_file
9495
from mig.shared.gdp.all import project_open, project_close, project_log
@@ -1518,59 +1519,85 @@ def accept_client(client, addr, root_dir, host_rsa_key, conf={}):
15181519
default_max_packet_size=max_packet_size)
15191520
# Restrict transport to strong ciphers+kex+digests used in OpenSSH
15201521
transport_security = transport.get_security_options()
1521-
recommended_ciphers = STRONG_SSH_CIPHERS.split(',')
1522+
strong_ciphers = STRONG_SSH_CIPHERS.split(',')
1523+
legacy_ciphers = LEGACY_SSH_CIPHERS.split(',')
1524+
fallback_ciphers = FALLBACK_SSH_CIPHERS.split(',')
15221525
available_ciphers = transport_security.ciphers
1523-
strong_ciphers = [i for i in recommended_ciphers if i in available_ciphers]
1524-
# logger.debug("TLS ciphers available %s, used %s" % (available_ciphers,
1525-
# strong_ciphers))
1526-
if strong_ciphers:
1527-
transport_security.ciphers = strong_ciphers
1526+
best_ciphers = [i for i in strong_ciphers if i in available_ciphers]
1527+
medium_ciphers = [i for i in legacy_ciphers if i in available_ciphers]
1528+
weak_ciphers = [i for i in fallback_ciphers if i in available_ciphers]
1529+
# logger.debug("TLS ciphers available %s: best %s, medium %s, weak %s" %
1530+
# (available_ciphers, best_ciphers, medium_ciphers, weak_ciphers))
1531+
if best_ciphers:
1532+
# logger.debug("Using only strong ciphers: %s" %
1533+
# ', '.join(best_ciphers))
1534+
transport_security.ciphers = best_ciphers
1535+
elif medium_ciphers:
1536+
logger.info("Rely on best available legacy ciphers: %s" %
1537+
', '.join(medium_ciphers))
1538+
transport_security.ciphers = medium_ciphers
1539+
elif weak_ciphers:
1540+
logger.warning("Force best available weak ciphers: %s" %
1541+
', '.join(weak_ciphers))
1542+
transport_security.ciphers = weak_ciphers
15281543
else:
1529-
logger.warning("No strong TLS ciphers available!")
1530-
logger.info("You need a recent paramiko for best security")
1544+
logger.warning("No safe TLS ciphers available!")
1545+
logger.info("You need a modern paramiko for proper security")
15311546
# NOTE: paramiko doesn't yet implement strong modern kex algos - use legacy
15321547
# A number of paramiko tickets indicate plans and interest for adding the
15331548
# strong curve25519-sha256@libssh.org eventually, but progress looks
15341549
# stalled. Until then our best alternative appears to be the legacy
15351550
# diffie-hellman-group-exchange-sha256 fallback, which should be safe as
15361551
# long as the moduli size tuning of e.g. ssh-audit is applied:
15371552
# http://cert.europa.eu/static/WhitePapers/CERT-EU-SWP_16-002_Weaknesses%20in%20Diffie-Hellman%20Key%20v1_0.pdf
1538-
recommended_kex = STRONG_SSH_KEXALGOS.split(',')
1539-
fallback_kex = STRONG_SSH_LEGACY_KEXALGOS.split(',')
1553+
strong_kex = STRONG_SSH_KEXALGOS.split(',')
1554+
legacy_kex = LEGACY_SSH_KEXALGOS.split(',')
1555+
fallback_kex = FALLBACK_SSH_KEXALGOS.split(',')
15401556
available_kex = transport_security.kex
1541-
strong_kex = [i for i in recommended_kex if i in available_kex]
1542-
medium_kex = [i for i in fallback_kex if i in available_kex]
1543-
# logger.debug("TLS kex available %s, used %s (or fallback to %s)" %
1544-
# (available_kex, strong_kex, medium_kex))
1545-
if strong_kex:
1546-
# logger.debug("Using only strong key exchange algorithms: %s" %
1547-
# ', '.join(strong_kex))
1548-
transport_security.kex = strong_kex
1557+
best_kex = [i for i in strong_kex if i in available_kex]
1558+
medium_kex = [i for i in legacy_kex if i in available_kex]
1559+
weak_kex = [i for i in fallback_kex if i in available_kex]
1560+
# logger.debug("TLS kex available %s: best %s, medium %s, weak %s" %
1561+
# (available_kex, best_kex, medium_kex, weak_kex))
1562+
if best_kex:
1563+
# logger.debug("Using only strong kex algorithms: %s" %
1564+
# ', '.join(best_kex))
1565+
transport_security.kex = best_kex
15491566
elif medium_kex:
1550-
# logger.debug("Using only medium strength key exchange algorithms: %s" %
1551-
# ', '.join(medium_kex))
1567+
logger.info("Rely on best available legacy kex algorithms: %s" %
1568+
', '.join(medium_kex))
15521569
transport_security.kex = medium_kex
1570+
elif weak_kex:
1571+
logger.warning("Force best available weak kex algorithms: %s" %
1572+
', '.join(weak_kex))
1573+
transport_security.kex = weak_kex
15531574
else:
1554-
logger.warning("No strong TLS key exchange algorithm available!")
1555-
logger.info("You need a recent paramiko for best security")
1556-
recommended_digests = STRONG_SSH_MACS.split(',')
1557-
fallback_digests = STRONG_SSH_LEGACY_MACS.split(',')
1575+
logger.warning("No safe TLS key exchange algorithm available!")
1576+
logger.info("You need a modern paramiko for proper security")
1577+
strong_digests = STRONG_SSH_MACS.split(',')
1578+
legacy_digests = LEGACY_SSH_MACS.split(',')
1579+
fallback_digests = FALLBACK_SSH_MACS.split(',')
15581580
available_digests = transport_security.digests
1559-
strong_digests = [i for i in recommended_digests if i in available_digests]
1560-
medium_digests = [i for i in fallback_digests if i in available_digests]
1561-
# logger.debug("TLS digests available %s, used %s (or fallback to %s)" %
1562-
# (available_digests, strong_digests, medium_digests))
1563-
if strong_digests:
1564-
# logger.debug("Using only strong message auth codes: %s" %
1565-
# ', '.join(strong_digests))
1566-
transport_security.digests = strong_digests
1581+
best_digests = [i for i in strong_digests if i in available_digests]
1582+
medium_digests = [i for i in legacy_digests if i in available_digests]
1583+
weak_digests = [i for i in fallback_digests if i in available_digests]
1584+
# logger.debug("TLS digests available %s: best %s, medium %s, weak %s" %
1585+
# (available_digests, best_digests, medium_digests, weak_digests))
1586+
if best_digests:
1587+
# logger.debug("Using only strong digests: %s" %
1588+
# ', '.join(best_digests))
1589+
transport_security.digests = best_digests
15671590
elif medium_digests:
1568-
# logger.debug("Using only medium strength message auth codes: %s" %
1569-
# ', '.join(medium_digests))
1591+
logger.info("Rely on best available legacy digests: %s" %
1592+
', '.join(medium_digests))
15701593
transport_security.digests = medium_digests
1594+
elif weak_digests:
1595+
logger.warning("Force best available weak digests: %s" %
1596+
', '.join(weak_digests))
1597+
transport_security.digests = weak_digests
15711598
else:
1572-
logger.warning("No strong TLS digest algorithm available!")
1573-
logger.info("You need paramiko 1.16 or later for best security")
1599+
logger.warning("No safe TLS digest algorithm available!")
1600+
logger.info("You need a modern paramiko for proper security")
15741601

15751602
# Default forces re-keying after every 512MB or same number of packets.
15761603
# We bump that to reduce the slowing effect of those: it's a security

mig/shared/defaults.py

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -451,33 +451,41 @@
451451
# TODO: add curve 'X25519' as first choice once we reach openssl-1.1?
452452
STRONG_TLS_CURVES = "prime256v1:secp384r1:secp521r1"
453453

454-
# Strong SSH key-exchange (Kex), cipher and message auth code (MAC) settings to
455-
# allow in OpenSSH and native Paramiko SFTP daemons (on OpenSSH format).
454+
# SSH hostkey, key-exchange (Kex), cipher and message auth code (MAC) settings
455+
# to allow in OpenSSH and native Paramiko SFTP daemons (on OpenSSH format).
456456
# NOTE: harden in line with Mozilla recommendations for modern versions:
457457
# https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Configuration
458458
# Additional hardening based on https://github.com/arthepsy/ssh-audit
459459
# Please note that the DH GroupX KexAlgorithms require OpenSSH 7.3+, but that
460460
# older versions can relatively safely fall back to instead use the
461461
# diffie-hellman-group-exchange-sha256 as long as the moduli tuning from
462462
# https://infosec.mozilla.org/guidelines/openssh is applied.
463-
# Tested to work with popular recent clients on the main platforms:
463+
# NOTE: DH group14 may also have weak modulus so leave it out.
464+
# Tested to work with popular clients on the main platforms:
464465
# OpenSSH-6.6.1+, LFTP-4.4.13+, FileZilla-3.24+, WinSCP-5.13.3+ and PuTTY-0.70+
465-
# NOTE: CentOS-6 still comes with OpenSSH-5.3 without strong Kex+MAC support
466-
# thus it's necessary to fake legacy ssh version to support any such clients.
467-
STRONG_SSH_KEXALGOS = "curve25519-sha256@libssh.org,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512"
468-
BEST_SSH_LEGACY_KEXALGOS = "curve25519-sha256@libssh.org"
469-
SAFE_SSH_LEGACY_KEXALGOS = "diffie-hellman-group-exchange-sha256"
470-
STRONG_SSH_LEGACY_KEXALGOS = ",".join([BEST_SSH_LEGACY_KEXALGOS,
471-
SAFE_SSH_LEGACY_KEXALGOS])
466+
# NOTE: CentOS 7 comes with OpenSSH-7.4 with medium Kex+MAC support
467+
# but it's necessary to fake legacy ssh version to support older clients.
468+
# NOTE: ssh-rsa is the deprecated SHA1 based RSA host key exchange. Rely on the
469+
# modern rsa-sha2-(512|256) algorithms instead where available. Unfortunately
470+
# ssh-rsa itself cannot be removed on legacy systems without also removing the
471+
# modern SHA256 versions, too. So keep it last and let client pick best there.
472+
STRONG_SSH_HOSTKEYALGOS = "ssh-ed25519,rsa-sha2-512,rsa-sha2-256"
473+
LEGACY_SSH_HOSTKEYALGOS = ",".join([STRONG_SSH_HOSTKEYALGOS, "ssh-rsa"])
474+
FALLBACK_SSH_HOSTKEYALGOS = LEGACY_SSH_HOSTKEYALGOS
475+
STRONG_SSH_KEXALGOS = "curve25519-sha256@libssh.org,diffie-hellman-group18-sha512,diffie-hellman-group16-sha512"
476+
# NOTE: fall back to relatively safe DH group-exchange-sha256 on old paramiko etc.
477+
LEGACY_SSH_KEXALGOS = ",".join([STRONG_SSH_KEXALGOS,
478+
"diffie-hellman-group-exchange-sha256"])
479+
FALLBACK_SSH_KEXALGOS = LEGACY_SSH_KEXALGOS
472480
STRONG_SSH_CIPHERS = "chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr"
473-
# NOTE: strong cipher support go way back - just reuse
474-
STRONG_SSH_LEGACY_CIPHERS = BEST_SSH_LEGACY_CIPHERS = SAFE_SSH_LEGACY_CIPHERS = STRONG_SSH_CIPHERS
481+
# NOTE: avoid chacha20-poly1305@openssh.com to mitigate Terrapin issue on old servers
482+
LEGACY_SSH_CIPHERS = "aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr"
483+
FALLBACK_SSH_CIPHERS = LEGACY_SSH_CIPHERS
475484
STRONG_SSH_MACS = "hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com"
476-
# NOTE: extend strong MACS with the best possible alternatives on old paramiko
485+
LEGACY_SSH_MACS = STRONG_SSH_MACS
486+
# NOTE: fall back to safe MACS with the best possible alternatives on ancient paramiko
477487
# to avoid falling back to really bad ones
478-
BEST_SSH_LEGACY_MACS = STRONG_SSH_MACS
479-
SAFE_SSH_LEGACY_MACS = "hmac-sha2-512,hmac-sha2-256"
480-
STRONG_SSH_LEGACY_MACS = ",".join([BEST_SSH_LEGACY_MACS, SAFE_SSH_LEGACY_MACS])
488+
FALLBACK_SSH_MACS = ",".join([LEGACY_SSH_MACS, "hmac-sha2-512,hmac-sha2-256"])
481489

482490
# Detect and ban cracking attempts and unauthorized vulnerability scans
483491
# A pattern to match usernames unambiguously identifying cracking attempts

mig/shared/install.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,12 @@
5252

5353
from mig.shared.defaults import default_http_port, default_https_port, \
5454
auth_openid_mig_db, auth_openid_ext_db, MIG_BASE, STRONG_TLS_CIPHERS, \
55-
STRONG_TLS_CURVES, STRONG_SSH_KEXALGOS, STRONG_SSH_LEGACY_KEXALGOS, \
56-
STRONG_SSH_CIPHERS, STRONG_SSH_LEGACY_CIPHERS, STRONG_SSH_MACS, \
57-
STRONG_SSH_LEGACY_MACS, CRACK_USERNAME_REGEX, CRACK_WEB_REGEX, \
58-
keyword_any, keyword_auto
55+
STRONG_TLS_CURVES, STRONG_SSH_HOSTKEYALGOS, STRONG_SSH_KEXALGOS, \
56+
STRONG_SSH_CIPHERS, STRONG_SSH_MACS, LEGACY_SSH_HOSTKEYALGOS, \
57+
LEGACY_SSH_KEXALGOS, LEGACY_SSH_CIPHERS, LEGACY_SSH_MACS, \
58+
FALLBACK_SSH_HOSTKEYALGOS, FALLBACK_SSH_KEXALGOS, FALLBACK_SSH_CIPHERS, \
59+
FALLBACK_SSH_MACS, CRACK_USERNAME_REGEX, CRACK_WEB_REGEX, keyword_any, \
60+
keyword_auto
5961
from mig.shared.compat import ensure_native_string
6062
from mig.shared.fileio import read_file, read_file_lines, write_file, \
6163
write_file_lines
@@ -1134,16 +1136,24 @@ def _generate_confs_prepare(
11341136
user_dict['__APACHE_CURVES__'] = STRONG_TLS_CURVES
11351137

11361138
# We use raw string comparison here which seems to work alright for X.Y.Z
1137-
if user_dict['__OPENSSH_VERSION__'] >= "7.3":
1138-
# Use current strong Kex/Cipher/MAC settings for openssh >=7.3
1139+
if user_dict['__OPENSSH_VERSION__'] >= "8.0":
1140+
# Use current strong HostKey/Kex/Cipher/MAC settings for openssh >=8.0
1141+
user_dict['__OPENSSH_HOSTKEYALGOS__'] = STRONG_SSH_HOSTKEYALGOS
11391142
user_dict['__OPENSSH_KEXALGOS__'] = STRONG_SSH_KEXALGOS
11401143
user_dict['__OPENSSH_CIPHERS__'] = STRONG_SSH_CIPHERS
11411144
user_dict['__OPENSSH_MACS__'] = STRONG_SSH_MACS
1145+
elif user_dict['__OPENSSH_VERSION__'] >= "7.4":
1146+
# Fall back to best legacy HostKey/Kex/Cipher/MAC for openssh >=7.4
1147+
user_dict['__OPENSSH_HOSTKEYALGOS__'] = LEGACY_SSH_HOSTKEYALGOS
1148+
user_dict['__OPENSSH_KEXALGOS__'] = LEGACY_SSH_KEXALGOS
1149+
user_dict['__OPENSSH_CIPHERS__'] = LEGACY_SSH_CIPHERS
1150+
user_dict['__OPENSSH_MACS__'] = LEGACY_SSH_MACS
11421151
else:
1143-
# Fall back to legacy Kex/Cipher/MAC for openssh <7.3
1144-
user_dict['__OPENSSH_KEXALGOS__'] = STRONG_SSH_LEGACY_KEXALGOS
1145-
user_dict['__OPENSSH_CIPHERS__'] = STRONG_SSH_LEGACY_CIPHERS
1146-
user_dict['__OPENSSH_MACS__'] = STRONG_SSH_LEGACY_MACS
1152+
# Fall back to best available HostKey/Kex/Cipher/MAC for openssh <7.4
1153+
user_dict['__OPENSSH_HOSTKEYALGOS__'] = FALLBACK_SSH_HOSTKEYALGOS
1154+
user_dict['__OPENSSH_KEXALGOS__'] = FALLBACK_SSH_KEXALGOS
1155+
user_dict['__OPENSSH_CIPHERS__'] = FALLBACK_SSH_CIPHERS
1156+
user_dict['__OPENSSH_MACS__'] = FALLBACK_SSH_MACS
11471157

11481158
# We know that login with one of these common usernames is a password
11491159
# cracking attempt since our own username format differs.

tests/fixture/confs-stdlocal/sshd_config-MiG-sftp-subsys

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ HostKey /home/mig/certs//server.key
3838

3939
# IMPORTANT: these are *generated* hardened values based on generateconf
4040
# invocation. Any permanent changes need to be made there.
41-
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512
42-
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
41+
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
42+
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,diffie-hellman-group-exchange-sha256
43+
Ciphers aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
4344
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
4445

4546
# Logging

0 commit comments

Comments
 (0)