Skip to content

Commit

Permalink
Flexible and dynamic service key/certificate fingerprints in MiGserve…
Browse files Browse the repository at this point in the history
…r.conf (#171)

Add support for expansion of external sources for the sftp , davs and
ftps key/cert fingerprints in `MiGserver.conf` in order to get rid of
tedious recurring manual fingerprint updates when relying on
_LetsEncrypt_ certificates, which come with frequent renewal.

Allows the `user_PROTO_key_sha256` configuration variables to e.g. be
set to
`FILE::/path/to/fingerprint`
in order to always read the current fingerprint from that file. It also
supports the usual
`FILE::/path/to/fingerprint$$/path/to/fast/cache/fingerprint`
for automatic caching in a fast memory-backed or similar cache location
to limit repeated file system reads.

Adjust the `migcheckssl` cron job helper to inform about this and
automatically write the combined pem and pub sha256 fingerprints in the
corresponding `combined.pem.sha256` and `combined.pub.sha256` files for
complete automation of the flow in that case.

The sftp key remains unchanged in that setup so one can keep that
fingerprint static in `MiGserver.conf,` or use that
`combined.pub.sha256` if changes are ever expected. Such changes are
typically rather inconvenient for clients as they have to find and
update their local known hosts file unless also delivered e.g. on
DNSSEC.

Updated unit test templates to fit `migcheckssl` changes.
  • Loading branch information
jonasbardino authored Jan 7, 2025
2 parents 46af52d + 07b73c4 commit 23ae234
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 23 deletions.
5 changes: 4 additions & 1 deletion mig/install/generateconfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# --- BEGIN_HEADER ---
#
# generateconfs - create custom MiG server configuration files
# Copyright (C) 2003-2024 The MiG Project lead by Brian Vinter
# Copyright (C) 2003-2025 The MiG Project
#
# This file is part of MiG.
#
Expand Down Expand Up @@ -145,7 +145,10 @@ def main(argv, _generate_confs=generate_confs, _print=print):
'ext_oidc_rewrite_cookie',
'dhparams_path',
'daemon_keycert',
'daemon_keycert_sha256',
'daemon_pubkey',
'daemon_pubkey_md5',
'daemon_pubkey_sha256',
'daemon_show_address',
'alias_field',
'peers_permit',
Expand Down
32 changes: 28 additions & 4 deletions mig/install/migcheckssl-template.sh.cronjob
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ server_crt="${domain_cert_path}/server.crt"
server_crt_ca_pem="${domain_cert_path}/server.crt.ca.pem"
server_key_crt_ca_pem="${domain_cert_path}/server.key.crt.ca.pem"
combined_pem="${domain_cert_path}/combined.pem"
combined_pem_sha256="${combined_pem}.sha256"
combined_pub="${domain_cert_path}/combined.pub"
combined_pub_sha256="${combined_pub}.sha256"
dhparams_pem="${cert_base}/dhparams.pem"
# use git latest or release version of getssl
getssl_version="release"
Expand Down Expand Up @@ -279,10 +281,32 @@ if [[ ${org_mtime} -ne ${new_mtime} && "${org_chksum}" != "${new_chksum}" ]]; th
fi
done
if [ -n "${migrid_subservices}" ]; then
sha256_fingerprint=$(openssl x509 -noout -fingerprint -sha256 -in ${combined_pem})
sha256_fingerprint=${sha256_fingerprint/SHA256 Fingerprint=/}
echo "Please update ftps and davs sha256 fingerprint in MiGserver.conf to:"
echo "${sha256_fingerprint}"
pem_sha256_fp=$(openssl x509 -noout -fingerprint -sha256 -in ${combined_pem})
pem_sha256_fp=${pem_sha256_fp/* Fingerprint=/}
echo "Please manually update ftps/davs sha256 fingerprint in MiGserver.conf to:"
echo "${pem_sha256_fp}"
echo "or point those configuration values to the latest fingerprint file with:"
echo "FILE::${combined_pem_sha256}"
echo "optionally appending '\$\$CACHE_PATH' for memory caching in CACHE_PATH."
echo "${pem_sha256_fp}" > ${combined_pem_sha256}
pub_md5_fp=$(ssh-keygen -l -E md5 -f ${combined_pub})
pub_md5_fp=${pub_md5_fp/* MD5:/}
pub_md5_fp=${pub_md5_fp/ */}
echo "Please verify that sftp md5 fingerprint in MiGserver.conf is:"
echo "${pub_md5_fp}"
echo "or point that configuration value to the latest fingerprint file with:"
echo "FILE::${combined_pub_md5}"
echo "optionally appending '\$\$CACHE_PATH' for memory caching in CACHE_PATH."
echo "${pub_md5_fp}" > ${combined_pub_md5}
pub_sha256_fp=$(ssh-keygen -l -f ${combined_pub})
pub_sha256_fp=${pub_sha256_fp/* SHA256:/}
pub_sha256_fp=${pub_sha256_fp/ */}
echo "Please verify that sftp sha256 fingerprint in MiGserver.conf is:"
echo "${pub_sha256_fp}"
echo "or point that configuration value to the latest fingerprint file with:"
echo "FILE::${combined_pub_sha256}"
echo "optionally appending '\$\$CACHE_PATH' for memory caching in CACHE_PATH."
echo "${pub_sha256_fp}" > ${combined_pub_sha256}
fi
fi

Expand Down
11 changes: 7 additions & 4 deletions mig/shared/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# --- BEGIN_HEADER ---
#
# configuration - configuration wrapper
# Copyright (C) 2003-2024 The MiG Project lead by Brian Vinter
# Copyright (C) 2003-2025 The MiG Project by the Science HPC Center at UCPH
#
# This file is part of MiG.
#
Expand Down Expand Up @@ -1134,7 +1134,8 @@ def reload_config(self, verbose, skip_log=False, disable_auth_log=False,
fingerprint = config.get('GLOBAL', 'user_sftp_key_md5')
self.user_sftp_key_md5 = fingerprint
if config.has_option('GLOBAL', 'user_sftp_key_sha256'):
fingerprint = config.get('GLOBAL', 'user_sftp_key_sha256')
fingerprint = expand_external_sources(
logger, config.get('GLOBAL', 'user_sftp_key_sha256'))
self.user_sftp_key_sha256 = fingerprint
if config.has_option('GLOBAL', 'user_sftp_key_from_dns'):
self.user_sftp_key_from_dns = config.getboolean(
Expand Down Expand Up @@ -1228,7 +1229,8 @@ def reload_config(self, verbose, skip_log=False, disable_auth_log=False,
self.user_davs_key = config.get('GLOBAL',
'user_davs_key')
if config.has_option('GLOBAL', 'user_davs_key_sha256'):
fingerprint = config.get('GLOBAL', 'user_davs_key_sha256')
fingerprint = expand_external_sources(
logger, config.get('GLOBAL', 'user_davs_key_sha256'))
self.user_davs_key_sha256 = fingerprint
if config.has_option('GLOBAL', 'user_davs_auth'):
self.user_davs_auth = config.get('GLOBAL',
Expand Down Expand Up @@ -1274,7 +1276,8 @@ def reload_config(self, verbose, skip_log=False, disable_auth_log=False,
self.user_ftps_key = config.get('GLOBAL',
'user_ftps_key')
if config.has_option('GLOBAL', 'user_ftps_key_sha256'):
fingerprint = config.get('GLOBAL', 'user_ftps_key_sha256')
fingerprint = expand_external_sources(
logger, config.get('GLOBAL', 'user_ftps_key_sha256'))
self.user_ftps_key_sha256 = fingerprint
if config.has_option('GLOBAL', 'user_ftps_auth'):
self.user_ftps_auth = config.get('GLOBAL',
Expand Down
39 changes: 29 additions & 10 deletions mig/shared/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# --- BEGIN_HEADER ---
#
# install - MiG server install helpers
# Copyright (C) 2003-2024 The MiG Project lead by Brian Vinter
# Copyright (C) 2003-2025 The MiG Project by the Science HPC Center at UCPH
#
# This file is part of MiG.
#
Expand Down Expand Up @@ -412,7 +412,10 @@ def generate_confs(
ext_oidc_rewrite_cookie='',
dhparams_path='',
daemon_keycert='',
daemon_keycert_sha256=keyword_auto,
daemon_pubkey='',
daemon_pubkey_md5=keyword_auto,
daemon_pubkey_sha256=keyword_auto,
daemon_pubkey_from_dns=False,
daemon_show_address='',
alias_field='',
Expand Down Expand Up @@ -730,7 +733,10 @@ def _generate_confs_prepare(
ext_oidc_rewrite_cookie,
dhparams_path,
daemon_keycert,
daemon_keycert_sha256,
daemon_pubkey,
daemon_pubkey_md5,
daemon_pubkey_sha256,
daemon_pubkey_from_dns,
daemon_show_address,
alias_field,
Expand Down Expand Up @@ -999,9 +1005,9 @@ def _generate_confs_prepare(
user_dict['__DHPARAMS_PATH__'] = dhparams_path
user_dict['__DAEMON_KEYCERT__'] = daemon_keycert
user_dict['__DAEMON_PUBKEY__'] = daemon_pubkey
user_dict['__DAEMON_KEYCERT_SHA256__'] = ''
user_dict['__DAEMON_PUBKEY_MD5__'] = ''
user_dict['__DAEMON_PUBKEY_SHA256__'] = ''
user_dict['__DAEMON_KEYCERT_SHA256__'] = daemon_keycert_sha256
user_dict['__DAEMON_PUBKEY_MD5__'] = daemon_pubkey_md5
user_dict['__DAEMON_PUBKEY_SHA256__'] = daemon_pubkey_sha256
user_dict['__DAEMON_PUBKEY_FROM_DNS__'] = "%s" % daemon_pubkey_from_dns
user_dict['__SFTP_PORT__'] = "%s" % sftp_port
user_dict['__SFTP_SUBSYS_PORT__'] = "%s" % sftp_subsys_port
Expand Down Expand Up @@ -1925,15 +1931,19 @@ def _generate_confs_prepare(
openssl dhparam 2048 -out %(__DHPARAMS_PATH__)s""" % user_dict)
sys.exit(1)

# Auto-fill fingerprints if daemon key is set
# Auto-fill fingerprints if daemon key is set with AUTO fingerprint
if user_dict['__DAEMON_KEYCERT__']:
if not os.path.isfile(os.path.expanduser("%(__DAEMON_KEYCERT__)s" %
user_dict)):
print("ERROR: requested daemon keycert file not found!")
print("""You can create it with:
openssl genrsa -out %(__DAEMON_KEYCERT__)s 2048""" % user_dict)
print("""You can create it e.g. with:
openssl genrsa -out %(__DAEMON_KEYCERT__)s 4096""" % user_dict)
sys.exit(1)
else:
user_dict['__DAEMON_KEYCERT_SHA256__'] = ''

if user_dict['__DAEMON_KEYCERT__'] and keyword_auto in \
(daemon_keycert_sha256, ):
key_path = os.path.expanduser(user_dict['__DAEMON_KEYCERT__'])
openssl_cmd = ["openssl", "x509", "-noout", "-fingerprint", "-sha256",
"-in", key_path]
Expand All @@ -1948,15 +1958,21 @@ def _generate_confs_prepare(
print("ERROR: failed to extract sha256 fingerprint of %s: %s" %
(key_path, exc))
daemon_keycert_sha256 = ''
user_dict['__DAEMON_KEYCERT_SHA256__'] = daemon_keycert_sha256
if daemon_keycert_sha256 == keyword_auto:
user_dict['__DAEMON_KEYCERT_SHA256__'] = daemon_keycert_sha256
if user_dict['__DAEMON_PUBKEY__']:
if not os.path.isfile(os.path.expanduser("%(__DAEMON_PUBKEY__)s" %
user_dict)):
print("ERROR: requested daemon pubkey file not found!")
print("""You can create it with:
ssh-keygen -f %(__DAEMON_KEYCERT__)s -y > %(__DAEMON_PUBKEY__)s""" % user_dict)
sys.exit(1)
else:
user_dict['__DAEMON_PUBKEY_MD5__'] = ''
user_dict['__DAEMON_PUBKEY_SHA256__'] = ''

if user_dict['__DAEMON_PUBKEY__'] and keyword_auto in \
(daemon_pubkey_md5, daemon_pubkey_sha256):
pubkey_path = os.path.expanduser(user_dict['__DAEMON_PUBKEY__'])
pubkey = read_file(pubkey_path, None)
if pubkey is None:
Expand All @@ -1974,9 +1990,12 @@ def _generate_confs_prepare(
except Exception as exc:
print("ERROR: failed to extract fingerprints of %s : %s" %
(pubkey_path, exc))
daemon_pubkey_md5 = ''
daemon_pubkey_sha256 = ''
user_dict['__DAEMON_PUBKEY_MD5__'] = daemon_pubkey_md5
user_dict['__DAEMON_PUBKEY_SHA256__'] = daemon_pubkey_sha256
if daemon_pubkey_md5 == keyword_auto:
user_dict['__DAEMON_PUBKEY_MD5__'] = daemon_pubkey_md5
if daemon_pubkey_sha256 == keyword_auto:
user_dict['__DAEMON_PUBKEY_SHA256__'] = daemon_pubkey_sha256

# Enable Debian/Ubuntu specific lines only there
if user_dict['__DISTRO__'].lower() in ('ubuntu', 'debian'):
Expand Down
32 changes: 28 additions & 4 deletions tests/fixture/confs-stdlocal/migcheckssl
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ server_crt="${domain_cert_path}/server.crt"
server_crt_ca_pem="${domain_cert_path}/server.crt.ca.pem"
server_key_crt_ca_pem="${domain_cert_path}/server.key.crt.ca.pem"
combined_pem="${domain_cert_path}/combined.pem"
combined_pem_sha256="${combined_pem}.sha256"
combined_pub="${domain_cert_path}/combined.pub"
combined_pub_sha256="${combined_pub}.sha256"
dhparams_pem="${cert_base}/dhparams.pem"
# use git latest or release version of getssl
getssl_version="release"
Expand Down Expand Up @@ -279,10 +281,32 @@ if [[ ${org_mtime} -ne ${new_mtime} && "${org_chksum}" != "${new_chksum}" ]]; th
fi
done
if [ -n "${migrid_subservices}" ]; then
sha256_fingerprint=$(openssl x509 -noout -fingerprint -sha256 -in ${combined_pem})
sha256_fingerprint=${sha256_fingerprint/SHA256 Fingerprint=/}
echo "Please update ftps and davs sha256 fingerprint in MiGserver.conf to:"
echo "${sha256_fingerprint}"
pem_sha256_fp=$(openssl x509 -noout -fingerprint -sha256 -in ${combined_pem})
pem_sha256_fp=${pem_sha256_fp/* Fingerprint=/}
echo "Please manually update ftps/davs sha256 fingerprint in MiGserver.conf to:"
echo "${pem_sha256_fp}"
echo "or point those configuration values to the latest fingerprint file with:"
echo "FILE::${combined_pem_sha256}"
echo "optionally appending '\$\$CACHE_PATH' for memory caching in CACHE_PATH."
echo "${pem_sha256_fp}" > ${combined_pem_sha256}
pub_md5_fp=$(ssh-keygen -l -E md5 -f ${combined_pub})
pub_md5_fp=${pub_md5_fp/* MD5:/}
pub_md5_fp=${pub_md5_fp/ */}
echo "Please verify that sftp md5 fingerprint in MiGserver.conf is:"
echo "${pub_md5_fp}"
echo "or point that configuration value to the latest fingerprint file with:"
echo "FILE::${combined_pub_md5}"
echo "optionally appending '\$\$CACHE_PATH' for memory caching in CACHE_PATH."
echo "${pub_md5_fp}" > ${combined_pub_md5}
pub_sha256_fp=$(ssh-keygen -l -f ${combined_pub})
pub_sha256_fp=${pub_sha256_fp/* SHA256:/}
pub_sha256_fp=${pub_sha256_fp/ */}
echo "Please verify that sftp sha256 fingerprint in MiGserver.conf is:"
echo "${pub_sha256_fp}"
echo "or point that configuration value to the latest fingerprint file with:"
echo "FILE::${combined_pub_sha256}"
echo "optionally appending '\$\$CACHE_PATH' for memory caching in CACHE_PATH."
echo "${pub_sha256_fp}" > ${combined_pub_sha256}
fi
fi

Expand Down

0 comments on commit 23ae234

Please sign in to comment.