/
githubmirror
/
salt
Обзор
Документация
Войти
/
githubmirror
/
salt
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
tests/pytests/functional/modules/test_ssh_pki.py
842 строки
31 KB
Daniel A. Wozniak
Fix merge-forward CI: Windows prepend_cmd test, FIPS test failures
05 май 2026, 13:55
05 май 2026, 13:55
56144cb
Код
Авторство
О чём код?
import datetime import stat import pytest import salt.exceptions try: import cryptography from cryptography.hazmat.primitives import serialization HAS_LIBS = True except ImportError: HAS_LIBS = False try: import bcrypt # pylint: disable=unused-import HAS_BCRYPT = True except ImportError: HAS_BCRYPT = False CRYPTOGRAPHY_VERSION = tuple(int(x) for x in cryptography.__version__.split(".")) pytestmark = [ pytest.mark.skipif(HAS_LIBS is False, reason="Needs cryptography library"), ] @pytest.fixture(scope="module") def minion_config_overrides(): return { "ssh_signing_policies": { "testhostpolicy": { "cert_type": "host", "valid_principals": ["from_host_signing_policy"], "allowed_extensions": [], "allowed_critical_options": [], }, "testuserpolicy": { "cert_type": "user", "valid_principals": ["from_user_signing_policy"], "critical_options": { "force-command": "echo hi", "no-port-forwarding": True, "verify-required": False, }, "extensions": { "permit-X11-forwarding": True, "permit-agent-forwarding": True, "permit-port-forwarding": True, "permit-pty": True, "permit-user-rc": False, }, }, "testprincipalspolicy": { "cert_type": "host", "valid_principals": ["a", "b", "c"], }, }, } @pytest.fixture def ssh(modules): yield modules.ssh_pki @pytest.fixture def ca_key(): return """\ -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcnNhAAAA AwEAAQAAAQEAmRii0ZT9KoL3aC9X7I8uQmcVGa4G7qOlyInNxGgKPC3s5+kshIVTMQfugd1irFT7 crF6Kamigq0S3uNBzpq/IxQBIRq8n7nHHFxiTgi+SK61XXMDdlbLsdXZxh7TkTMYPp32nmKUFDdd xAVQCGwFvzkV2tbtxpiRO6E/60b22Lpt3qS0JdSgSYp9q0IFv2iNR+Yp+Kovx/I8+RySdhgecpRr 16Qul+DqvEqNtXvNJv4uA1nh34GCKBvNa8JekCYhGWpiXKvDaJREEFfVtuL0YrgDCS6JmbqMreYq zsl8+QN9gEkt4JjDTQkEGUI7a4ZY+m69w2eN5eT0DebaNWGzkQAAA7gCJksAAiZLAAAAAAdzc2gt cnNhAAABAQCZGKLRlP0qgvdoL1fsjy5CZxUZrgbuo6XIic3EaAo8Lezn6SyEhVMxB+6B3WKsVPty sXopqaKCrRLe40HOmr8jFAEhGryfucccXGJOCL5IrrVdcwN2Vsux1dnGHtORMxg+nfaeYpQUN13E BVAIbAW/ORXa1u3GmJE7oT/rRvbYum3epLQl1KBJin2rQgW/aI1H5in4qi/H8jz5HJJ2GB5ylGvX pC6X4Oq8So21e80m/i4DWeHfgYIoG81rwl6QJiEZamJcq8NolEQQV9W24vRiuAMJLomZuoyt5irO yXz5A32ASS3gmMNNCQQZQjtrhlj6br3DZ43l5PQN5to1YbORAAAAAwEAAQAAAQAcJGIxonCTMvXl qeZAruUzAZ3oVYwiq+Rao7I2a2WOQGbvDnbHeXacabfXGWn9AbYjFCq/o9YirUvtutqq7tk5yoCW pEKOHelS9kx/ya2o0Ky4G99EDpps+0GH4LzFUR4gzIq7/KT5vl+3G77lfW3lA8pXqvCUdBEmY/LK /gV8OyffLxdQMIYCucpVpJq/wSiIYdd3DVK2TvlmUsR0DnvF5zo8/b7nvkALElXdA5G9NUL+k+XZ 0iDiWB0Ezokg5/VokhhhP8NcgiHeO00XHE6AAQ8rbUq9cwRgn0oqyC4ZYMc68MZOQhn2GKcYTRxG YbGC6xoIUWrvaQq1/L4NEDIRAAAAgB9FHj/pVd/7HDjkGF9YapBu4kvuv9AxGvOVOWWHIk0pQl/L vOEFj0gz4xl+BEdIDpX0RVS1p0wPD3h8uBKWQ5q2t4wOIzGidfaL+u/NHo4MPVC+cOCfgsRU7sy2 vN9kOjNpP7C3QTOzojq2vJ0zkJt+nZ3IA6OxyvS1Ebc0j88qAAAAgQDVjG+C+YyrYy9m5KBoj5T4 UEjNzgCr9t6ANSHObQjQxafRfeB4gT7vv65xna7Q4I8ATm2wITty1LETLNWbc2B4W5Mir8CnaUw0 P9Eyxql7VaTrHU1M3ee50Yquz1UEFLat1JXE/jMc2vs7s/W5Vd8uitom3uK8IkY/v3XOC4nk1QAA AIEAt4fCQs36AcsYngt7oboKBMCnaiFWfupvZTAC86kd7VcuF74r7IrC4U9Oohwl1l6jXU90p0Mm qJJROD1G3S1IFenRXQ6aIizaO7WkFRBeEuUtHwI0MbBq2cDjVzvRsu9qnLgMCeVMuBmj7pVG+Isf zgx0+GaEc+f1RlC0qh6eIc0AAAAAAQID -----END OPENSSH PRIVATE KEY-----""" @pytest.fixture def ca_key_enc(): return """\ -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAo0CeCU7RU21xt OjRqiR2WAAAAEAAAAAEAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQCZGKLRlP0qgvdoL1fsjy5C ZxUZrgbuo6XIic3EaAo8Lezn6SyEhVMxB+6B3WKsVPtysXopqaKCrRLe40HOmr8jFAEhGryfuccc XGJOCL5IrrVdcwN2Vsux1dnGHtORMxg+nfaeYpQUN13EBVAIbAW/ORXa1u3GmJE7oT/rRvbYum3e pLQl1KBJin2rQgW/aI1H5in4qi/H8jz5HJJ2GB5ylGvXpC6X4Oq8So21e80m/i4DWeHfgYIoG81r wl6QJiEZamJcq8NolEQQV9W24vRiuAMJLomZuoyt5irOyXz5A32ASS3gmMNNCQQZQjtrhlj6br3D Z43l5PQN5to1YbORAAADwIahmMW2HPaQ+r0WV3eT5lkbQjeTIkOo47PexilFof9ZYoOOk3AjcgYP 1SEhq4BXnMnpTVbRUkmzR7+od7tXrqVC9Eo3NuOwyjZwRJuHB+zPKpYqZ9SPOMRVYxx75yUmXnCu Hsm8zT9F1izm/KQOyoSlbcWK3uQSSvYHYAWku/5bc6+fCPbgFYlB1Bk+cxjeGOPD+qDWQd4IKXj/ W/KCE8ncjQIZkrX1fgPGJsxCJwNUwYpJUad2+8XaZJIAIur36tlyNw+1JmQHSGMhfLW1P+tDXAIc JJ+KpK/uVhYX5uOdER3/trwa6YQBHuUjo3sGrfYYTprd+WH0PbN2QJ9Vi2m5gJLY7CjCod7ecjbJ ntTN9xucis+I7eZZPHhd8MO9wNQkXRwy07ZPawExlv2EVDUsofHQxiuHkvPZHNq4eFDz906xIk0v n0X9IBrVtjg+bvnhl9XG86Ay5NAD7vCquqEqZAAHet24oi7yJmOkAJVA/0PaPzaALfp1a7noFFud FuC9PmmdTe0gL1m1pQki1fIFMk2dxB83pQiBJThn9C3KhbrtsCN00kWxb3Ezv2mFPNeEs3Q9W7r5 mHVuJfVDusXAdXlpp+EcQueZW1r0Qtnf0UbH57LiTt6S1MUEYn3jgIE/ynHOPTrv0RgE8udCq8/g 1gUcZwL8BLJVCu+Dllj4/l/s/E3BzaTCzmc48toAdbeoZ1+nzXBCez1i8QQZ57gQK7hVfNCqCUD9 yNw84panB1lkxvBxFmN05dKxeBCGY0Bpxnl5xb2ND+rASXCELSEnHxt+9FVEdjrlpyg+KUnuBort JmmK/VKmeW2yk+7kHDTefKpRiK7PNF1gUko15gzegq0YEGDewo/RT/c9BaYHZougg5HBjT6bUHaN 6x2Jc75z5ILXaeLrsavIyYuhMwpBrMYSPm/xNZvBxPiM+AyhCq2YAgS0eV3Asd4h7HB90NZHbwjc nvmCLpiZtn269dM1wZdYUGaPCf6Gmj5OojJVRhgaXD8SHOzvb5HqQnco3e+KiVrFCGvfn/S8CTnv CsQx2szAQcgkO16xsKZC8p1DAJSH+palgYRY2gMjXDzQN57RBGZPd2wwdSUc/iYXjUnXz782Nb/W a7MutFQiFnqr0WUXscoenXsxDYjU8D81hbqkxVXzX/uwOmux0ilPwX1RlvjB2TuZ0YjDBtdprOYf tXpvXY2joVcZBnblNTC+AMZQAza2xRdT5/VdNK7b6cdjQ00lo+OMRNeWLcjEL6JI8siEAywW/h69 Dft9BkNgrA== -----END OPENSSH PRIVATE KEY-----""" @pytest.fixture def ca_pub(): return ( "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCZGKLRlP0qgvdoL1fsjy5CZxUZrg" "buo6XIic3EaAo8Lezn6SyEhVMxB+6B3WKsVPtysXopqaKCrRLe40HOmr8jFAEhGryf" "ucccXGJOCL5IrrVdcwN2Vsux1dnGHtORMxg+nfaeYpQUN13EBVAIbAW/ORXa1u3GmJ" "E7oT/rRvbYum3epLQl1KBJin2rQgW/aI1H5in4qi/H8jz5HJJ2GB5ylGvXpC6X4Oq8" "So21e80m/i4DWeHfgYIoG81rwl6QJiEZamJcq8NolEQQV9W24vRiuAMJLomZuoyt5i" "rOyXz5A32ASS3gmMNNCQQZQjtrhlj6br3DZ43l5PQN5to1YbOR" ) @pytest.fixture def rsa_privkey(): return """\ -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcnNhAAAA AwEAAQAAAQEAt11Mkho+ChXlHC33p5I+K0r2wldrHV48T+5fHtuOrHNsaR01perekmDFQMvnhgDh P/ibFENrFgo/yg2MHh/SldgC6fQFqZr89uJ+/E63oZ3+EIvUdQWiN8jiPkgKTTOlMXh2DjuPPa6U C8hEZwnE5lg+/UN5BjiivZCbO0/Ll8/MXlPvSk3tdIao3twHJ4WwKYPotXwZ6LUM7HFloC/yPLfj /AQhz6GO1fT5wLobDefB8Wpg2bWPO0yLvKlDGbZWzn2xy7deP2WrjqUsgTM9PaUKLIVBEgB/Apw2 FUkNkM6lHGM1bBAsiY5L3ISqBIku1us2TvwRZRGKDQYYIm3mbwAAA7gckLhOHJC4TgAAAAdzc2gt cnNhAAABAQC3XUySGj4KFeUcLfenkj4rSvbCV2sdXjxP7l8e246sc2xpHTWl6t6SYMVAy+eGAOE/ +JsUQ2sWCj/KDYweH9KV2ALp9AWpmvz24n78Trehnf4Qi9R1BaI3yOI+SApNM6UxeHYOO489rpQL yERnCcTmWD79Q3kGOKK9kJs7T8uXz8xeU+9KTe10hqje3AcnhbApg+i1fBnotQzscWWgL/I8t+P8 BCHPoY7V9PnAuhsN58HxamDZtY87TIu8qUMZtlbOfbHLt14/ZauOpSyBMz09pQoshUESAH8CnDYV SQ2QzqUcYzVsECyJjkvchKoEiS7W6zZO/BFlEYoNBhgibeZvAAAAAwEAAQAAAQA6mf3Lv8lODdtQ GzzH+EwYJ/ge+jNIioJ6BTOvN/osESN3oJcxtJN3cqf34nLW99cFS928JyPFQndKivPZ+M+jhgrA XHWm09q+yHpPBpVXeJfnD9lRoQBMFc6AmyN3sua7ncUVHWHVE8NK1LFPwOaFu3Q+Gt9F5rnUHbAO z5zAomT10HfQd9yeDCdywAFsPklwbzmzv72ps3+CSG+ppRm+oh1GYYe7zWmX4SBPuTJbx9BTcuWd yQTvSUIamP/22gxoNMzNBkYu/WeXNpB5iHGazNMAIEdyJ9x8VimnUIyZeW/hIPXt5MUW/WfFOE8Z zzQ/+MTPF+uSfBC54tJQUvrxAAAAgD4wQaNkRY5jNZ3c4SUsrzwDDdbWH0SbTGLcnCWpd809apTN adIBI+vt74kZCYafOqOOldyeU3Akw/fc9+BoBrR62boAYTSWlWNFqsNcE0AjEJbO0fIm2ngr8Z+o vFp7fIyWQaIklmV0eyetAOz4InF+xC7SJqH2qm38kQvc0bdWAAAAgQDlg1w1HnyiBmooml3Ql5ln uq+Fv8XZ/xxfVxRY5MCugVAR9eLQcMUJ/s3o44VEu1N3GAe1Dge3HYSQMPtZHIIe2AG38u1wHR+t fFAQswoOD1Fn3O69CIG8kjzqdPLo12E+0+gJI5ki6lgAGEkVbQW4GsqVm4jBF0SPjUTfN5FRlwAA AIEAzIaKrsfUUAPBCLuYeiZGfSCiXtdVZ+Yo8ADMwHA7iMSWG6ushLNwcV3PqRjVbW3xfM+9OghX /zM6sratQmVpqhTBwQnyXBJp5SFT2n/XFYwkKhERBukBB/uEcI6M4HYhP8Z/N8lE7PNuY0ylnmrT Cy/i60jGoFHQaROUPbs2/OkAAAAAAQID -----END OPENSSH PRIVATE KEY-----""" @pytest.fixture def rsa_privkey_enc(): return """\ -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABA8ZLxKjCh9dRM4 7AmjoqGqAAAAEAAAAAEAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQC3XUySGj4KFeUcLfenkj4r SvbCV2sdXjxP7l8e246sc2xpHTWl6t6SYMVAy+eGAOE/+JsUQ2sWCj/KDYweH9KV2ALp9AWpmvz2 4n78Trehnf4Qi9R1BaI3yOI+SApNM6UxeHYOO489rpQLyERnCcTmWD79Q3kGOKK9kJs7T8uXz8xe U+9KTe10hqje3AcnhbApg+i1fBnotQzscWWgL/I8t+P8BCHPoY7V9PnAuhsN58HxamDZtY87TIu8 qUMZtlbOfbHLt14/ZauOpSyBMz09pQoshUESAH8CnDYVSQ2QzqUcYzVsECyJjkvchKoEiS7W6zZO /BFlEYoNBhgibeZvAAADwDiKlu/+uo3XsapQBgGxZOSTHY1bqXZG1qh1EaE0DxXl0RQApJkZCutV 2wWN8VaCNbtdcLgDTqC8WMHd/QzseJBWabnIUHZXYsCbU9BmcfjUhCQ0kyN2zSb4YmsM73PqMX6I 5usU0nVAV/UhBSMg7tIzMVBQzYLYl+eypW9usp5HYb8dw/vMhQY+a1AYqIpe9vZlPohso8U2Q9bL LiMNjMSSWcgh2b3i/LbixzHTGSibF9/izntSem/jxnoZ27R1r97KcuxPF3uKfPlrlpsmzNqhVi0d 4YWMttG3EUrpsGHUG5Ylp8bXnfMfmFkmkiEGjU/Gf30tiKlFznQ4G9luYn7h4Rs8vx9U7Xolhz22 yqJ0FxvvY9HYIhK6WTqOdPKE7O1cy1cn9Rne6RG4ALmETjbVh1dMCIKBFaIYztWIoT87XJrH3piD xRlMQog7Cim/a2zd1l/G+iyEDBoVUzWx5WNJBiJgt9ZbYEJz1cJiWFDJhzWO1mFXYzNaJdfUdL43 xZjU+sxCxXuV+ps6nBjz9CmnI7hkNAIoa9lBNUo8uWds2DnNm0QU48bliVtxcLcjAdpZev5H1Hfx bPgHDrja3F0fbtJaoadIn/xh8c0CTtOCKgjS1up9YnB2AL+bFoX0vcl13kRvuxnpUDaxQ2xehETR SBupMQmGcdSSOUhLIF4/QJFijwMHNhUcIJqgzhT256z/dEKxtofi4Q9TiRjDT4uENO9Uy/FbZWAI zaW3TLPZAnTLTIishfCOOm0yb9XmuxM4hLgGpqAHSxEvSqMXS4fe+BIdj2i5cOsB/N3//OE1yQ1M qeMqTyvXEbK+PAH65rdhE6xyeJFeKV+fil7dyZX41R6FUtZ9ls3AXcmqFIq463n7MvUGkl8zqMvz EzH8oCQRJ1Taly9JsLgCd7UbHpokeQeMYKyd5+muYvBu0qX2HPD0exJ5uJSxSqq+0NrlDKmY1INb /XcPBjxVtKsXoTRfh/vs+TcLxwYbAWymWCdX3k4maa7oJ3ZOc0L5/ypeVUaecW4PcyXlYzEAfIwK nFAirfDVSpjBochJfNBbhCgmtdmq3nUh8CI7AExJmVrWvEbJ6klJMVet0m50mfqyhBI5idw2YfKs qxOoAdt3IZIxmGG+pyDeeFB8X9O5RF1BVnFsqNPHEqdRtVD7qPSYBaZDhtgFVJbstwW3+///nyLP 5B5yBiGVm0aFXpFcVeCHYX817crRC+0h/HA5fECosGRMZFZEAJh5CHivuKNvhseKdnWXLwkfIpuO Ojnp0x6Zow== -----END OPENSSH PRIVATE KEY-----""" @pytest.fixture def rsa_pubkey(): return ( "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3XUySGj4KFeUcLfenkj4rSvbCV2s" "dXjxP7l8e246sc2xpHTWl6t6SYMVAy+eGAOE/+JsUQ2sWCj/KDYweH9KV2ALp9AWpmv" "z24n78Trehnf4Qi9R1BaI3yOI+SApNM6UxeHYOO489rpQLyERnCcTmWD79Q3kGOKK9k" "Js7T8uXz8xeU+9KTe10hqje3AcnhbApg+i1fBnotQzscWWgL/I8t+P8BCHPoY7V9PnA" "uhsN58HxamDZtY87TIu8qUMZtlbOfbHLt14/ZauOpSyBMz09pQoshUESAH8CnDYVSQ2" "QzqUcYzVsECyJjkvchKoEiS7W6zZO/BFlEYoNBhgibeZv" ) @pytest.fixture def ec_privkey(): return """\ -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS1zaGEy LW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQScOqN2HTar84e+l3Er4Ti0ZsY3nbR9RkRsgZb0Flie lc8SN/zIHSLroaJ21ofSqfu+mazGpGFWkNo34zSbBW5+AAAAoEaJqOBGiajgAAAAE2VjZHNhLXNo YTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJw6o3YdNqvzh76XcSvhOLRmxjedtH1GRGyBlvQW WJ6VzxI3/MgdIuuhonbWh9Kp+76ZrMakYVaQ2jfjNJsFbn4AAAAhAL7DzNqGQYRNLOeXUt/t+DFz R4+26CwTk8SDLHiIt2dpAAAAAAECAwQFBgc= -----END OPENSSH PRIVATE KEY-----""" @pytest.fixture def ec_pubkey(): return ( "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNT" "YAAABBBJw6o3YdNqvzh76XcSvhOLRmxjedtH1GRGyBlvQWWJ6VzxI3/MgdIuuhonbW" "h9Kp+76ZrMakYVaQ2jfjNJsFbn4=" ) @pytest.fixture def ed25519_privkey(): return """\ -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZWQyNTUx OQAAACDRCIjV5alJaolYOvZSZMzH8gVgGp7LsKlum8O86Me5EQAAAIidIDmZnSA5mQAAAAtzc2gt ZWQyNTUxOQAAACDRCIjV5alJaolYOvZSZMzH8gVgGp7LsKlum8O86Me5EQAAAEBc5nqUPbCxESlT vH72xFJW6XGhm9Sm4CbrmcdfhucVdtEIiNXlqUlqiVg69lJkzMfyBWAansuwqW6bw7zox7kRAAAA AAECAwQF -----END OPENSSH PRIVATE KEY-----""" @pytest.fixture def ed25519_pubkey(): return "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINEIiNXlqUlqiVg69lJkzMfyBWAansuwqW6bw7zox7kR" @pytest.fixture def cert_exts(): return ( "ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5" "jb20AAAAgAutZE2GohcxGXD1rmPbhaLcN5uLcWjBk0KQIXOcDr5oAAAADAQABAAABAQC" "3XUySGj4KFeUcLfenkj4rSvbCV2sdXjxP7l8e246sc2xpHTWl6t6SYMVAy+eGAOE/+Js" "UQ2sWCj/KDYweH9KV2ALp9AWpmvz24n78Trehnf4Qi9R1BaI3yOI+SApNM6UxeHYOO48" "9rpQLyERnCcTmWD79Q3kGOKK9kJs7T8uXz8xeU+9KTe10hqje3AcnhbApg+i1fBnotQz" "scWWgL/I8t+P8BCHPoY7V9PnAuhsN58HxamDZtY87TIu8qUMZtlbOfbHLt14/ZauOpSy" "BMz09pQoshUESAH8CnDYVSQ2QzqUcYzVsECyJjkvchKoEiS7W6zZO/BFlEYoNBhgibeZ" "vA2htsw1e8lQAAAABAAAADXNhbHRzdGFja3Rlc3QAAAARAAAABHNhbHQAAAAFc3RhY2s" "AAAAAZJvNIgAAAABknR6iAAAAOgAAAA1mb3JjZS1jb21tYW5kAAAACwAAAAdlY2hvIGh" "pAAAAEm5vLXBvcnQtZm9yd2FyZGluZwAAAAAAAAA8AAAAFXBlcm1pdC1YMTEtZm9yd2F" "yZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAAAAAARcAAAA" "Hc3NoLXJzYQAAAAMBAAEAAAEBAJkYotGU/SqC92gvV+yPLkJnFRmuBu6jpciJzcRoCjw" "t7OfpLISFUzEH7oHdYqxU+3KxeimpooKtEt7jQc6avyMUASEavJ+5xxxcYk4IvkiutV1" "zA3ZWy7HV2cYe05EzGD6d9p5ilBQ3XcQFUAhsBb85FdrW7caYkTuhP+tG9ti6bd6ktCX" "UoEmKfatCBb9ojUfmKfiqL8fyPPkcknYYHnKUa9ekLpfg6rxKjbV7zSb+LgNZ4d+Bgig" "bzWvCXpAmIRlqYlyrw2iURBBX1bbi9GK4AwkuiZm6jK3mKs7JfPkDfYBJLeCYw00JBBl" "CO2uGWPpuvcNnjeXk9A3m2jVhs5EAAAEUAAAADHJzYS1zaGEyLTUxMgAAAQAUJpzjwKC" "Ib2oHLo4q6v6qN37ssRmprwtAVJaV9FmAgczroMyWiL3P5O7U4TLPzluODVRSzpI1K5j" "oeIheQQ24npcErmxVj3xkk4uFsaTgyAamQfnudlseltmNfzqYX6LStFco27bBM6x1V3p" "TPbOfd8m1g1JZEH8yGCt8J4zpVNs6NWTJ862PqdlQKYaZcfEdDyuW0AemSug7YXBe6XU" "6F5v55T8W4sGy3yLn9HWMFyvdtnH91Y8NzsC3JgQfuImPDmdH0hjg0Fr/9579EUEIiq3" "IHN7S4e2vrj7lIreSxPeKnkQNvrjmrPojLD9atastKTalIriT7mtfqzOTih7V" ) @pytest.fixture def cert_exts_read(): return { "cert_type": "user", "key_id": "saltstacktest", "key_size": 2048, "key_type": "rsa", "serial_number": "03:68:6D:B3:0D:5E:F2:54", "issuer_public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCZGKLRlP0qgvdoL1fsjy5CZxUZrgbuo6XIic3EaAo8Lezn6SyEhVMxB+6B3WKsVPtysXopqaKCrRLe40HOmr8jFAEhGryfucccXGJOCL5IrrVdcwN2Vsux1dnGHtORMxg+nfaeYpQUN13EBVAIbAW/ORXa1u3GmJE7oT/rRvbYum3epLQl1KBJin2rQgW/aI1H5in4qi/H8jz5HJJ2GB5ylGvXpC6X4Oq8So21e80m/i4DWeHfgYIoG81rwl6QJiEZamJcq8NolEQQV9W24vRiuAMJLomZuoyt5irOyXz5A32ASS3gmMNNCQQZQjtrhlj6br3DZ43l5PQN5to1YbOR", "issuer_public_key_fingerprints": { "md5": "e1:6c:9c:1b:33:22:dd:aa:6f:7e:07:80:f0:92:95:58", "sha256": "AkjrJzyLdJXTj+36AVeT7UeK0efm1OMPu8ovT3UNHU0=", }, "not_before": "2023-06-28 06:03:14", "not_after": "2023-06-29 06:03:14", "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3XUySGj4KFeUcLfenkj4rSvbCV2sdXjxP7l8e246sc2xpHTWl6t6SYMVAy+eGAOE/+JsUQ2sWCj/KDYweH9KV2ALp9AWpmvz24n78Trehnf4Qi9R1BaI3yOI+SApNM6UxeHYOO489rpQLyERnCcTmWD79Q3kGOKK9kJs7T8uXz8xeU+9KTe10hqje3AcnhbApg+i1fBnotQzscWWgL/I8t+P8BCHPoY7V9PnAuhsN58HxamDZtY87TIu8qUMZtlbOfbHLt14/ZauOpSyBMz09pQoshUESAH8CnDYVSQ2QzqUcYzVsECyJjkvchKoEiS7W6zZO/BFlEYoNBhgibeZv", "public_key_fingerprints": { "md5": "b0:c2:ae:34:55:8a:27:d5:87:cd:a7:3c:54:ca:59:6d", "sha256": "24C6IQIPizP36/mQRNth/L3rTHHM6JgpppKQj7U+xAA=", }, "critical_options": {"force-command": "echo hi", "no-port-forwarding": True}, "extensions": {"permit-X11-forwarding": True, "permit-agent-forwarding": True}, "valid_principals": ["salt", "stack"], } def _assert_cert_basic(res, cert_type, pubkey, ca_pubkey, valid_principals=None): cert = _get_cert(res) if isinstance(cert_type, str): cert_type = getattr(serialization.SSHCertificateType, cert_type.upper()) assert cert.type is cert_type assert cert.key_id == b"success" assert _encode_pubkey(cert.public_key()) == pubkey assert _encode_pubkey(cert.signature_key()) == ca_pubkey assert cert.valid_principals == (valid_principals or []) return cert @pytest.mark.parametrize( "algo", [ "rsa", "ec", pytest.param("ed25519", marks=pytest.mark.skip_on_fips_enabled_platform), ], ) @pytest.mark.parametrize("cert_type", ["user", "host"]) def test_create_certificate_from_privkey(ssh, cert_type, ca_key, ca_pub, algo, request): privkey = request.getfixturevalue(f"{algo}_privkey") pubkey = request.getfixturevalue(f"{algo}_pubkey") res = ssh.create_certificate( cert_type=cert_type, signing_private_key=ca_key, private_key=privkey, key_id="success", all_principals=True, ) _assert_cert_basic(res, cert_type, pubkey, ca_pub) @pytest.mark.skipif( HAS_BCRYPT is False, reason="Encrypted keys require the bcrypt library" ) @pytest.mark.parametrize("cert_type", ["user", "host"]) def test_create_certificate_from_encrypted_privkey( ssh, ca_key, ca_pub, rsa_privkey_enc, rsa_pubkey, cert_type ): res = ssh.create_certificate( cert_type=cert_type, signing_private_key=ca_key, private_key=rsa_privkey_enc, private_key_passphrase="hunter1", key_id="success", all_principals=True, ) _assert_cert_basic(res, cert_type, rsa_pubkey, ca_pub) @pytest.mark.skipif( HAS_BCRYPT is False, reason="Encrypted keys require the bcrypt library" ) @pytest.mark.parametrize("cert_type", ["user", "host"]) def test_create_certificate_from_encrypted_privkey_with_encrypted_privkey( ssh, ca_key_enc, ca_pub, rsa_privkey_enc, rsa_pubkey, cert_type ): res = ssh.create_certificate( cert_type=cert_type, signing_private_key=ca_key_enc, signing_private_key_passphrase="correct horse battery staple", private_key=rsa_privkey_enc, private_key_passphrase="hunter1", key_id="success", all_principals=True, ) _assert_cert_basic(res, cert_type, rsa_pubkey, ca_pub) @pytest.mark.parametrize("cert_type", ["user", "host"]) @pytest.mark.parametrize( "algo", [ "rsa", "ec", pytest.param("ed25519", marks=pytest.mark.skip_on_fips_enabled_platform), ], ) def test_create_certificate_from_pubkey(ssh, ca_key, ca_pub, algo, request, cert_type): pubkey = request.getfixturevalue(f"{algo}_pubkey") res = ssh.create_certificate( cert_type=cert_type, signing_private_key=ca_key, public_key=pubkey, key_id="success", all_principals=True, ) _assert_cert_basic(res, cert_type, pubkey, ca_pub) def test_create_certificate_write_to_path(ssh, ca_key, rsa_pubkey, ca_pub, tmp_path): tgt = tmp_path / "cert" ssh.create_certificate( cert_type="user", public_key=rsa_pubkey, signing_private_key=ca_key, key_id="success", all_principals=True, path=str(tgt), ) assert tgt.exists() _assert_cert_basic( tgt.read_bytes(), serialization.SSHCertificateType.USER, rsa_pubkey, ca_pub ) def test_create_certificate_write_to_path_overwrite( ssh, ca_key, rsa_pubkey, ca_pub, tmp_path ): tgt = tmp_path / "cert" tgt.write_text("occupied") assert tgt.exists() ssh.create_certificate( cert_type="user", overwrite=True, public_key=rsa_pubkey, signing_private_key=ca_key, key_id="success", all_principals=True, path=str(tgt), ) _assert_cert_basic( tgt.read_bytes(), serialization.SSHCertificateType.USER, rsa_pubkey, ca_pub ) def test_create_certificate_write_to_path_overwrite_false( ssh, ca_key, rsa_pubkey, tmp_path ): tgt = tmp_path / "cert" tgt.write_text("occupied") assert tgt.exists() with pytest.raises( salt.exceptions.CommandExecutionError, match=".*exists and overwrite was set to false.*", ): ssh.create_certificate( "user", overwrite=False, public_key=rsa_pubkey, signing_private_key=ca_key, key_id="success", all_principals=True, path=str(tgt), ) assert tgt.read_text() == "occupied" def test_create_certificate_raw(ssh, ca_key, ca_pub, rsa_pubkey): res = ssh.create_certificate( cert_type="user", raw=True, public_key=rsa_pubkey, signing_private_key=ca_key, key_id="success", all_principals=True, ) assert isinstance(res, bytes) _assert_cert_basic(res, serialization.SSHCertificateType.USER, rsa_pubkey, ca_pub) def test_create_certificate_with_critical_options(ssh, ca_key, ca_pub, rsa_pubkey): options = { "force-command": "echo hi", "no-port-forwarding": True, "verify-required": False, } expected_options = {b"force-command": b"echo hi", b"no-port-forwarding": b""} res = ssh.create_certificate( cert_type="user", signing_private_key=ca_key, public_key=rsa_pubkey, critical_options=options, key_id="success", all_principals=True, ) cert = _assert_cert_basic( res, serialization.SSHCertificateType.USER, rsa_pubkey, ca_pub ) assert cert.critical_options == expected_options def test_create_certificate_with_extensions(ssh, ca_key, ca_pub, rsa_pubkey): extensions = { "permit-X11-forwarding": True, "permit-agent-forwarding": True, "permit-port-forwarding": True, "permit-pty": True, "permit-user-rc": False, } expected_extensions = { b"permit-X11-forwarding": b"", b"permit-agent-forwarding": b"", b"permit-port-forwarding": b"", b"permit-pty": b"", } res = ssh.create_certificate( cert_type="user", signing_private_key=ca_key, public_key=rsa_pubkey, extensions=extensions, key_id="success", all_principals=True, ) cert = _assert_cert_basic( res, serialization.SSHCertificateType.USER, rsa_pubkey, ca_pub ) assert cert.extensions == expected_extensions def test_create_certificate_with_signing_policy_host(ssh, ca_pub, ca_key, rsa_pubkey): res = ssh.create_certificate( cert_type="user", signing_policy="testhostpolicy", signing_private_key=ca_key, public_key=rsa_pubkey, key_id="success", valid_principals=["from_call"], extensions={"permit-agent-forwarding": True}, critical_options={"force-command": "rm -rf /"}, ) cert = _assert_cert_basic( res, serialization.SSHCertificateType.HOST, rsa_pubkey, ca_pub, [b"from_host_signing_policy"], ) assert cert.extensions == {} assert cert.critical_options == {} def test_create_certificate_with_signing_policy_user(ssh, ca_pub, ca_key, rsa_pubkey): critical_options = { "force-command": "rm -rf /", "verify-required": True, "source-address": "13.37.13.37", } expected_options = { b"force-command": b"echo hi", b"no-port-forwarding": b"", b"source-address": b"13.37.13.37", } extensions = {"permit-user-rc": True, "no-touch-required": True} expected_extensions = { b"permit-X11-forwarding": b"", b"permit-agent-forwarding": b"", b"permit-port-forwarding": b"", b"permit-pty": b"", b"no-touch-required": b"", } res = ssh.create_certificate( cert_type="host", signing_policy="testuserpolicy", signing_private_key=ca_key, public_key=rsa_pubkey, key_id="success", valid_principals=["from_call"], critical_options=critical_options, extensions=extensions, ) cert = _assert_cert_basic( res, serialization.SSHCertificateType.USER, rsa_pubkey, ca_pub, [b"from_user_signing_policy"], ) assert cert.critical_options == expected_options assert cert.extensions == expected_extensions def test_create_certificate_with_signing_policy_principals_is_subset( ssh, ca_key, ca_pub, rsa_pubkey ): """ Only principals listed in the policy should be allowed """ res = ssh.create_certificate( signing_policy="testprincipalspolicy", signing_private_key=ca_key, public_key=rsa_pubkey, valid_principals=["a", "c", "d"], key_id="success", ) cert = _get_cert(res) assert cert.type is serialization.SSHCertificateType.HOST assert cert.key_id == b"success" assert _encode_pubkey(cert.public_key()) == rsa_pubkey assert _encode_pubkey(cert.signature_key()) == ca_pub assert set(cert.valid_principals) == {b"a", b"c"} def test_create_certificate_not_before_not_after(ssh, ca_pub, ca_key, rsa_pubkey): fmt = "%Y-%m-%d %H:%M:%S" not_before = "2022-12-21 13:37:10" not_after = "2032-12-21 13:37:10" res = ssh.create_certificate( cert_type="host", not_before=not_before, not_after=not_after, signing_private_key=ca_key, public_key=rsa_pubkey, key_id="success", all_principals=True, ) cert = _assert_cert_basic( res, serialization.SSHCertificateType.HOST, rsa_pubkey, ca_pub ) assert cert.valid_after == datetime.datetime.strptime(not_before, fmt).timestamp() assert cert.valid_before == datetime.datetime.strptime(not_after, fmt).timestamp() @pytest.mark.parametrize("sn", [3735928559, "DE:AD:BE:EF", "deadbeef"]) def test_create_certificate_explicit_serial_number(ssh, ca_pub, ca_key, rsa_pubkey, sn): res = ssh.create_certificate( cert_type="host", serial_number=sn, signing_private_key=ca_key, public_key=rsa_pubkey, key_id="success", all_principals=True, ) cert = _assert_cert_basic( res, serialization.SSHCertificateType.HOST, rsa_pubkey, ca_pub ) assert cert.serial == 3735928559 def test_create_certificate_copypath(ssh, rsa_pubkey, ca_pub, ca_key, tmp_path): res = ssh.create_certificate( cert_type="host", signing_private_key=ca_key, public_key=rsa_pubkey, key_id="success", all_principals=True, copypath=str(tmp_path), ) cert = _assert_cert_basic( res, serialization.SSHCertificateType.HOST, rsa_pubkey, ca_pub ) target_path = tmp_path / f"{cert.serial:x}.crt" assert target_path.exists() assert target_path.read_bytes() == cert.public_bytes() @pytest.mark.slow_test @pytest.mark.parametrize( "algo", [ "rsa", "ec", pytest.param("ed25519", marks=pytest.mark.skip_on_fips_enabled_platform), ], ) def test_create_private_key(ssh, algo): res = ssh.create_private_key(algo=algo) priv, pub = res["private_key"], res["public_key"] assert priv.startswith("-----BEGIN OPENSSH PRIVATE KEY-----") # ensure it can be loaded assert ssh.get_public_key(priv) == pub @pytest.mark.skipif( HAS_BCRYPT is False, reason="Encrypted keys require the bcrypt library" ) @pytest.mark.slow_test @pytest.mark.parametrize( "algo", [ "rsa", "ec", pytest.param("ed25519", marks=pytest.mark.skip_on_fips_enabled_platform), ], ) def test_create_private_key_with_passphrase(ssh, algo): passphrase = "hunter2" res = ssh.create_private_key(algo=algo, passphrase=passphrase) priv, pub = res["private_key"], res["public_key"] assert priv.startswith("-----BEGIN OPENSSH PRIVATE KEY-----") # ensure it can be loaded assert ssh.get_public_key(priv, passphrase=passphrase) == pub def test_create_private_key_write_to_path(ssh, tmp_path): tgt = tmp_path / "pk" pub = tgt.with_suffix(".pub") ssh.create_private_key(path=str(tgt)) assert tgt.exists() assert pub.exists() assert tgt.read_text().startswith("-----BEGIN OPENSSH PRIVATE KEY-----") assert stat.S_IMODE(tgt.stat().st_mode) == 0o0600 assert ssh.get_public_key(str(tgt)) == pub.read_text() def test_create_private_key_write_to_path_overwrite(ssh, tmp_path): tgt = tmp_path / "pk" tgt.write_text("occupied") assert tgt.exists() ssh.create_private_key(overwrite=True, path=str(tgt)) assert tgt.read_text().startswith("-----BEGIN OPENSSH PRIVATE KEY-----") # ensure it can be loaded ssh.get_private_key_size(str(tgt)) def test_create_private_key_write_to_path_not_overwrite(ssh, tmp_path): tgt = tmp_path / "pk" tgt.write_text("occupied") assert tgt.exists() with pytest.raises( salt.exceptions.CommandExecutionError, match=".*exists and overwrite was set to false.*", ): ssh.create_private_key(overwrite=False, path=str(tgt)) assert tgt.read_text() == "occupied" def test_create_private_key_raw(ssh): res = ssh.create_private_key(raw=True) assert isinstance(res, bytes) assert res.startswith(b"-----BEGIN OPENSSH PRIVATE KEY-----") # ensure it can be loaded ssh.get_private_key_size(res) @pytest.mark.parametrize( "algo,expected", [ ("rsa", 2048), ("ec", 256), pytest.param("ed25519", None, marks=pytest.mark.skip_on_fips_enabled_platform), ], ) def test_get_private_key_size(ssh, algo, expected, request): privkey = request.getfixturevalue(f"{algo}_privkey") res = ssh.get_private_key_size(privkey) assert res == expected @pytest.mark.parametrize("source", ["rsa_privkey", "rsa_pubkey", "cert_exts"]) def test_get_public_key(ssh, source, request, rsa_pubkey): src = request.getfixturevalue(source) res = ssh.get_public_key(src) assert res == rsa_pubkey @pytest.mark.skip_on_fips_enabled_platform def test_read_certificate(ssh, cert_exts, cert_exts_read): res = ssh.read_certificate(cert_exts) assert res == cert_exts_read @pytest.mark.parametrize( "priv,pub,expected", [ ("rsa_privkey", "cert_exts", True), ("ec_privkey", "ec_pubkey", True), pytest.param( "ed25519_privkey", "ed25519_pubkey", True, marks=pytest.mark.skip_on_fips_enabled_platform, ), pytest.param( "ed25519_privkey", "ec_pubkey", False, marks=pytest.mark.skip_on_fips_enabled_platform, ), ("rsa_privkey", "ca_pub", False), ], ) def test_verify_private_key(ssh, priv, pub, expected, request): priv = request.getfixturevalue(priv) pub = request.getfixturevalue(pub) assert ssh.verify_private_key(priv, pub) is expected @pytest.mark.parametrize( "algo", [ "rsa", "ec", pytest.param("ed25519", marks=pytest.mark.skip_on_fips_enabled_platform), ], ) def test_verify_signature(ssh, algo, request): wrong_privkey = request.getfixturevalue(f"{algo}_privkey") privkey = ssh.create_private_key(algo=algo)["private_key"] privkey_to_sign = ssh.create_private_key()["private_key"] cert = ssh.create_certificate( cert_type="host", all_principals=True, private_key=privkey_to_sign, signing_private_key=privkey, ) assert ssh.verify_signature(cert, privkey) assert not ssh.verify_signature(cert, wrong_privkey) @pytest.fixture def fresh_cert(ssh, rsa_privkey, ca_key): return ssh.create_certificate( cert_type="host", private_key=rsa_privkey, signing_private_key=ca_key, ttl="1d", key_id="fresh", all_principals=True, ) @pytest.mark.parametrize( "ttl,expected", [(0, False), (60, False), ("1h", False), ("2d", True)] ) def test_expires(ssh, fresh_cert, ttl, expected): assert ssh.expires(fresh_cert, ttl=ttl) is expected def _get_cert(cert): if isinstance(cert, str): cert = cert.encode() ret = serialization.load_ssh_public_identity(cert) if not isinstance(ret, serialization.SSHCertificate): raise ValueError(f"Expected SSHCertificate, got {ret.__class__.__name__}") return ret def _encode_pubkey(public_key): return public_key.public_bytes( encoding=serialization.Encoding.OpenSSH, format=serialization.PublicFormat.OpenSSH, ).decode()