Skip to content

Commit 23ae234

Browse files
authored
Flexible and dynamic service key/certificate fingerprints in MiGserver.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.
2 parents 46af52d + 07b73c4 commit 23ae234

File tree

5 files changed

+96
-23
lines changed

5 files changed

+96
-23
lines changed

mig/install/generateconfs.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# --- BEGIN_HEADER ---
55
#
66
# generateconfs - create custom MiG server configuration files
7-
# Copyright (C) 2003-2024 The MiG Project lead by Brian Vinter
7+
# Copyright (C) 2003-2025 The MiG Project
88
#
99
# This file is part of MiG.
1010
#
@@ -145,7 +145,10 @@ def main(argv, _generate_confs=generate_confs, _print=print):
145145
'ext_oidc_rewrite_cookie',
146146
'dhparams_path',
147147
'daemon_keycert',
148+
'daemon_keycert_sha256',
148149
'daemon_pubkey',
150+
'daemon_pubkey_md5',
151+
'daemon_pubkey_sha256',
149152
'daemon_show_address',
150153
'alias_field',
151154
'peers_permit',

mig/install/migcheckssl-template.sh.cronjob

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ server_crt="${domain_cert_path}/server.crt"
4040
server_crt_ca_pem="${domain_cert_path}/server.crt.ca.pem"
4141
server_key_crt_ca_pem="${domain_cert_path}/server.key.crt.ca.pem"
4242
combined_pem="${domain_cert_path}/combined.pem"
43+
combined_pem_sha256="${combined_pem}.sha256"
4344
combined_pub="${domain_cert_path}/combined.pub"
45+
combined_pub_sha256="${combined_pub}.sha256"
4446
dhparams_pem="${cert_base}/dhparams.pem"
4547
# use git latest or release version of getssl
4648
getssl_version="release"
@@ -279,10 +281,32 @@ if [[ ${org_mtime} -ne ${new_mtime} && "${org_chksum}" != "${new_chksum}" ]]; th
279281
fi
280282
done
281283
if [ -n "${migrid_subservices}" ]; then
282-
sha256_fingerprint=$(openssl x509 -noout -fingerprint -sha256 -in ${combined_pem})
283-
sha256_fingerprint=${sha256_fingerprint/SHA256 Fingerprint=/}
284-
echo "Please update ftps and davs sha256 fingerprint in MiGserver.conf to:"
285-
echo "${sha256_fingerprint}"
284+
pem_sha256_fp=$(openssl x509 -noout -fingerprint -sha256 -in ${combined_pem})
285+
pem_sha256_fp=${pem_sha256_fp/* Fingerprint=/}
286+
echo "Please manually update ftps/davs sha256 fingerprint in MiGserver.conf to:"
287+
echo "${pem_sha256_fp}"
288+
echo "or point those configuration values to the latest fingerprint file with:"
289+
echo "FILE::${combined_pem_sha256}"
290+
echo "optionally appending '\$\$CACHE_PATH' for memory caching in CACHE_PATH."
291+
echo "${pem_sha256_fp}" > ${combined_pem_sha256}
292+
pub_md5_fp=$(ssh-keygen -l -E md5 -f ${combined_pub})
293+
pub_md5_fp=${pub_md5_fp/* MD5:/}
294+
pub_md5_fp=${pub_md5_fp/ */}
295+
echo "Please verify that sftp md5 fingerprint in MiGserver.conf is:"
296+
echo "${pub_md5_fp}"
297+
echo "or point that configuration value to the latest fingerprint file with:"
298+
echo "FILE::${combined_pub_md5}"
299+
echo "optionally appending '\$\$CACHE_PATH' for memory caching in CACHE_PATH."
300+
echo "${pub_md5_fp}" > ${combined_pub_md5}
301+
pub_sha256_fp=$(ssh-keygen -l -f ${combined_pub})
302+
pub_sha256_fp=${pub_sha256_fp/* SHA256:/}
303+
pub_sha256_fp=${pub_sha256_fp/ */}
304+
echo "Please verify that sftp sha256 fingerprint in MiGserver.conf is:"
305+
echo "${pub_sha256_fp}"
306+
echo "or point that configuration value to the latest fingerprint file with:"
307+
echo "FILE::${combined_pub_sha256}"
308+
echo "optionally appending '\$\$CACHE_PATH' for memory caching in CACHE_PATH."
309+
echo "${pub_sha256_fp}" > ${combined_pub_sha256}
286310
fi
287311
fi
288312

mig/shared/configuration.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# --- BEGIN_HEADER ---
55
#
66
# configuration - configuration wrapper
7-
# Copyright (C) 2003-2024 The MiG Project lead by Brian Vinter
7+
# Copyright (C) 2003-2025 The MiG Project by the Science HPC Center at UCPH
88
#
99
# This file is part of MiG.
1010
#
@@ -1134,7 +1134,8 @@ def reload_config(self, verbose, skip_log=False, disable_auth_log=False,
11341134
fingerprint = config.get('GLOBAL', 'user_sftp_key_md5')
11351135
self.user_sftp_key_md5 = fingerprint
11361136
if config.has_option('GLOBAL', 'user_sftp_key_sha256'):
1137-
fingerprint = config.get('GLOBAL', 'user_sftp_key_sha256')
1137+
fingerprint = expand_external_sources(
1138+
logger, config.get('GLOBAL', 'user_sftp_key_sha256'))
11381139
self.user_sftp_key_sha256 = fingerprint
11391140
if config.has_option('GLOBAL', 'user_sftp_key_from_dns'):
11401141
self.user_sftp_key_from_dns = config.getboolean(
@@ -1228,7 +1229,8 @@ def reload_config(self, verbose, skip_log=False, disable_auth_log=False,
12281229
self.user_davs_key = config.get('GLOBAL',
12291230
'user_davs_key')
12301231
if config.has_option('GLOBAL', 'user_davs_key_sha256'):
1231-
fingerprint = config.get('GLOBAL', 'user_davs_key_sha256')
1232+
fingerprint = expand_external_sources(
1233+
logger, config.get('GLOBAL', 'user_davs_key_sha256'))
12321234
self.user_davs_key_sha256 = fingerprint
12331235
if config.has_option('GLOBAL', 'user_davs_auth'):
12341236
self.user_davs_auth = config.get('GLOBAL',
@@ -1274,7 +1276,8 @@ def reload_config(self, verbose, skip_log=False, disable_auth_log=False,
12741276
self.user_ftps_key = config.get('GLOBAL',
12751277
'user_ftps_key')
12761278
if config.has_option('GLOBAL', 'user_ftps_key_sha256'):
1277-
fingerprint = config.get('GLOBAL', 'user_ftps_key_sha256')
1279+
fingerprint = expand_external_sources(
1280+
logger, config.get('GLOBAL', 'user_ftps_key_sha256'))
12781281
self.user_ftps_key_sha256 = fingerprint
12791282
if config.has_option('GLOBAL', 'user_ftps_auth'):
12801283
self.user_ftps_auth = config.get('GLOBAL',

mig/shared/install.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# --- BEGIN_HEADER ---
55
#
66
# install - MiG server install helpers
7-
# Copyright (C) 2003-2024 The MiG Project lead by Brian Vinter
7+
# Copyright (C) 2003-2025 The MiG Project by the Science HPC Center at UCPH
88
#
99
# This file is part of MiG.
1010
#
@@ -412,7 +412,10 @@ def generate_confs(
412412
ext_oidc_rewrite_cookie='',
413413
dhparams_path='',
414414
daemon_keycert='',
415+
daemon_keycert_sha256=keyword_auto,
415416
daemon_pubkey='',
417+
daemon_pubkey_md5=keyword_auto,
418+
daemon_pubkey_sha256=keyword_auto,
416419
daemon_pubkey_from_dns=False,
417420
daemon_show_address='',
418421
alias_field='',
@@ -730,7 +733,10 @@ def _generate_confs_prepare(
730733
ext_oidc_rewrite_cookie,
731734
dhparams_path,
732735
daemon_keycert,
736+
daemon_keycert_sha256,
733737
daemon_pubkey,
738+
daemon_pubkey_md5,
739+
daemon_pubkey_sha256,
734740
daemon_pubkey_from_dns,
735741
daemon_show_address,
736742
alias_field,
@@ -999,9 +1005,9 @@ def _generate_confs_prepare(
9991005
user_dict['__DHPARAMS_PATH__'] = dhparams_path
10001006
user_dict['__DAEMON_KEYCERT__'] = daemon_keycert
10011007
user_dict['__DAEMON_PUBKEY__'] = daemon_pubkey
1002-
user_dict['__DAEMON_KEYCERT_SHA256__'] = ''
1003-
user_dict['__DAEMON_PUBKEY_MD5__'] = ''
1004-
user_dict['__DAEMON_PUBKEY_SHA256__'] = ''
1008+
user_dict['__DAEMON_KEYCERT_SHA256__'] = daemon_keycert_sha256
1009+
user_dict['__DAEMON_PUBKEY_MD5__'] = daemon_pubkey_md5
1010+
user_dict['__DAEMON_PUBKEY_SHA256__'] = daemon_pubkey_sha256
10051011
user_dict['__DAEMON_PUBKEY_FROM_DNS__'] = "%s" % daemon_pubkey_from_dns
10061012
user_dict['__SFTP_PORT__'] = "%s" % sftp_port
10071013
user_dict['__SFTP_SUBSYS_PORT__'] = "%s" % sftp_subsys_port
@@ -1925,15 +1931,19 @@ def _generate_confs_prepare(
19251931
openssl dhparam 2048 -out %(__DHPARAMS_PATH__)s""" % user_dict)
19261932
sys.exit(1)
19271933

1928-
# Auto-fill fingerprints if daemon key is set
1934+
# Auto-fill fingerprints if daemon key is set with AUTO fingerprint
19291935
if user_dict['__DAEMON_KEYCERT__']:
19301936
if not os.path.isfile(os.path.expanduser("%(__DAEMON_KEYCERT__)s" %
19311937
user_dict)):
19321938
print("ERROR: requested daemon keycert file not found!")
1933-
print("""You can create it with:
1934-
openssl genrsa -out %(__DAEMON_KEYCERT__)s 2048""" % user_dict)
1939+
print("""You can create it e.g. with:
1940+
openssl genrsa -out %(__DAEMON_KEYCERT__)s 4096""" % user_dict)
19351941
sys.exit(1)
1942+
else:
1943+
user_dict['__DAEMON_KEYCERT_SHA256__'] = ''
19361944

1945+
if user_dict['__DAEMON_KEYCERT__'] and keyword_auto in \
1946+
(daemon_keycert_sha256, ):
19371947
key_path = os.path.expanduser(user_dict['__DAEMON_KEYCERT__'])
19381948
openssl_cmd = ["openssl", "x509", "-noout", "-fingerprint", "-sha256",
19391949
"-in", key_path]
@@ -1948,15 +1958,21 @@ def _generate_confs_prepare(
19481958
print("ERROR: failed to extract sha256 fingerprint of %s: %s" %
19491959
(key_path, exc))
19501960
daemon_keycert_sha256 = ''
1951-
user_dict['__DAEMON_KEYCERT_SHA256__'] = daemon_keycert_sha256
1961+
if daemon_keycert_sha256 == keyword_auto:
1962+
user_dict['__DAEMON_KEYCERT_SHA256__'] = daemon_keycert_sha256
19521963
if user_dict['__DAEMON_PUBKEY__']:
19531964
if not os.path.isfile(os.path.expanduser("%(__DAEMON_PUBKEY__)s" %
19541965
user_dict)):
19551966
print("ERROR: requested daemon pubkey file not found!")
19561967
print("""You can create it with:
19571968
ssh-keygen -f %(__DAEMON_KEYCERT__)s -y > %(__DAEMON_PUBKEY__)s""" % user_dict)
19581969
sys.exit(1)
1970+
else:
1971+
user_dict['__DAEMON_PUBKEY_MD5__'] = ''
1972+
user_dict['__DAEMON_PUBKEY_SHA256__'] = ''
19591973

1974+
if user_dict['__DAEMON_PUBKEY__'] and keyword_auto in \
1975+
(daemon_pubkey_md5, daemon_pubkey_sha256):
19601976
pubkey_path = os.path.expanduser(user_dict['__DAEMON_PUBKEY__'])
19611977
pubkey = read_file(pubkey_path, None)
19621978
if pubkey is None:
@@ -1974,9 +1990,12 @@ def _generate_confs_prepare(
19741990
except Exception as exc:
19751991
print("ERROR: failed to extract fingerprints of %s : %s" %
19761992
(pubkey_path, exc))
1993+
daemon_pubkey_md5 = ''
19771994
daemon_pubkey_sha256 = ''
1978-
user_dict['__DAEMON_PUBKEY_MD5__'] = daemon_pubkey_md5
1979-
user_dict['__DAEMON_PUBKEY_SHA256__'] = daemon_pubkey_sha256
1995+
if daemon_pubkey_md5 == keyword_auto:
1996+
user_dict['__DAEMON_PUBKEY_MD5__'] = daemon_pubkey_md5
1997+
if daemon_pubkey_sha256 == keyword_auto:
1998+
user_dict['__DAEMON_PUBKEY_SHA256__'] = daemon_pubkey_sha256
19801999

19812000
# Enable Debian/Ubuntu specific lines only there
19822001
if user_dict['__DISTRO__'].lower() in ('ubuntu', 'debian'):

tests/fixture/confs-stdlocal/migcheckssl

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ server_crt="${domain_cert_path}/server.crt"
4040
server_crt_ca_pem="${domain_cert_path}/server.crt.ca.pem"
4141
server_key_crt_ca_pem="${domain_cert_path}/server.key.crt.ca.pem"
4242
combined_pem="${domain_cert_path}/combined.pem"
43+
combined_pem_sha256="${combined_pem}.sha256"
4344
combined_pub="${domain_cert_path}/combined.pub"
45+
combined_pub_sha256="${combined_pub}.sha256"
4446
dhparams_pem="${cert_base}/dhparams.pem"
4547
# use git latest or release version of getssl
4648
getssl_version="release"
@@ -279,10 +281,32 @@ if [[ ${org_mtime} -ne ${new_mtime} && "${org_chksum}" != "${new_chksum}" ]]; th
279281
fi
280282
done
281283
if [ -n "${migrid_subservices}" ]; then
282-
sha256_fingerprint=$(openssl x509 -noout -fingerprint -sha256 -in ${combined_pem})
283-
sha256_fingerprint=${sha256_fingerprint/SHA256 Fingerprint=/}
284-
echo "Please update ftps and davs sha256 fingerprint in MiGserver.conf to:"
285-
echo "${sha256_fingerprint}"
284+
pem_sha256_fp=$(openssl x509 -noout -fingerprint -sha256 -in ${combined_pem})
285+
pem_sha256_fp=${pem_sha256_fp/* Fingerprint=/}
286+
echo "Please manually update ftps/davs sha256 fingerprint in MiGserver.conf to:"
287+
echo "${pem_sha256_fp}"
288+
echo "or point those configuration values to the latest fingerprint file with:"
289+
echo "FILE::${combined_pem_sha256}"
290+
echo "optionally appending '\$\$CACHE_PATH' for memory caching in CACHE_PATH."
291+
echo "${pem_sha256_fp}" > ${combined_pem_sha256}
292+
pub_md5_fp=$(ssh-keygen -l -E md5 -f ${combined_pub})
293+
pub_md5_fp=${pub_md5_fp/* MD5:/}
294+
pub_md5_fp=${pub_md5_fp/ */}
295+
echo "Please verify that sftp md5 fingerprint in MiGserver.conf is:"
296+
echo "${pub_md5_fp}"
297+
echo "or point that configuration value to the latest fingerprint file with:"
298+
echo "FILE::${combined_pub_md5}"
299+
echo "optionally appending '\$\$CACHE_PATH' for memory caching in CACHE_PATH."
300+
echo "${pub_md5_fp}" > ${combined_pub_md5}
301+
pub_sha256_fp=$(ssh-keygen -l -f ${combined_pub})
302+
pub_sha256_fp=${pub_sha256_fp/* SHA256:/}
303+
pub_sha256_fp=${pub_sha256_fp/ */}
304+
echo "Please verify that sftp sha256 fingerprint in MiGserver.conf is:"
305+
echo "${pub_sha256_fp}"
306+
echo "or point that configuration value to the latest fingerprint file with:"
307+
echo "FILE::${combined_pub_sha256}"
308+
echo "optionally appending '\$\$CACHE_PATH' for memory caching in CACHE_PATH."
309+
echo "${pub_sha256_fp}" > ${combined_pub_sha256}
286310
fi
287311
fi
288312

0 commit comments

Comments
 (0)