1
0

test_x509.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. # -*- coding: utf-8 -*-
  2. from __future__ import absolute_import, unicode_literals
  3. import datetime
  4. import hashlib
  5. import logging
  6. import os
  7. import pprint
  8. import textwrap
  9. import pytest
  10. import salt.utils.files
  11. from tests.support.case import ModuleCase
  12. from tests.support.helpers import slowTest, with_tempfile
  13. from tests.support.mixins import SaltReturnAssertsMixin
  14. from tests.support.runtests import RUNTIME_VARS
  15. from tests.support.unit import skipIf
  16. try:
  17. import M2Crypto # pylint: disable=W0611
  18. HAS_M2CRYPTO = True
  19. except ImportError:
  20. HAS_M2CRYPTO = False
  21. log = logging.getLogger(__name__)
  22. @pytest.mark.usefixtures("salt_sub_minion")
  23. @skipIf(not HAS_M2CRYPTO, "Skip when no M2Crypto found")
  24. class x509Test(ModuleCase, SaltReturnAssertsMixin):
  25. @classmethod
  26. def setUpClass(cls):
  27. cert_path = os.path.join(RUNTIME_VARS.BASE_FILES, "x509_test.crt")
  28. with salt.utils.files.fopen(cert_path) as fp:
  29. cls.x509_cert_text = fp.read()
  30. def setUp(self):
  31. with salt.utils.files.fopen(
  32. os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "signing_policies.sls"), "w"
  33. ) as fp:
  34. fp.write(
  35. textwrap.dedent(
  36. """\
  37. x509_signing_policies:
  38. ca_policy:
  39. - minions: '*'
  40. - signing_private_key: {0}/pki/ca.key
  41. - signing_cert: {0}/pki/ca.crt
  42. - O: Test Company
  43. - basicConstraints: "CA:false"
  44. - keyUsage: "critical digitalSignature, keyEncipherment"
  45. - extendedKeyUsage: "critical serverAuth, clientAuth"
  46. - subjectKeyIdentifier: hash
  47. - authorityKeyIdentifier: keyid
  48. - days_valid: 730
  49. - copypath: {0}/pki
  50. compound_match:
  51. - minions: 'G@x509_test_grain:correct_value'
  52. - signing_private_key: {0}/pki/ca.key
  53. - signing_cert: {0}/pki/ca.crt
  54. - O: Test Company
  55. - basicConstraints: "CA:false"
  56. - keyUsage: "critical digitalSignature, keyEncipherment"
  57. - extendedKeyUsage: "critical serverAuth, clientAuth"
  58. - subjectKeyIdentifier: hash
  59. - authorityKeyIdentifier: keyid
  60. - days_valid: 730
  61. - copypath: {0}/pki
  62. """.format(
  63. RUNTIME_VARS.TMP
  64. )
  65. )
  66. )
  67. with salt.utils.files.fopen(
  68. os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "top.sls"), "w"
  69. ) as fp:
  70. fp.write(
  71. textwrap.dedent(
  72. """\
  73. base:
  74. '*':
  75. - signing_policies
  76. """
  77. )
  78. )
  79. self.run_function("saltutil.refresh_pillar")
  80. self.run_function(
  81. "grains.set", ["x509_test_grain", "correct_value"], minion_tgt="sub_minion"
  82. )
  83. self.run_function(
  84. "grains.set", ["x509_test_grain", "not_correct_value"], minion_tgt="minion"
  85. )
  86. def tearDown(self):
  87. os.remove(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "signing_policies.sls"))
  88. os.remove(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "top.sls"))
  89. certs_path = os.path.join(RUNTIME_VARS.TMP, "pki")
  90. if os.path.exists(certs_path):
  91. salt.utils.files.rm_rf(certs_path)
  92. self.run_function("saltutil.refresh_pillar")
  93. self.run_function("grains.delkey", ["x509_test_grain"], minion_tgt="sub_minion")
  94. self.run_function("grains.delkey", ["x509_test_grain"], minion_tgt="minion")
  95. def run_function(self, *args, **kwargs): # pylint: disable=arguments-differ
  96. ret = super(x509Test, self).run_function(*args, **kwargs)
  97. return ret
  98. @staticmethod
  99. def file_checksum(path):
  100. hash = hashlib.sha1()
  101. with salt.utils.files.fopen(path, "rb") as f:
  102. for block in iter(lambda: f.read(4096), b""):
  103. hash.update(block)
  104. return hash.hexdigest()
  105. @with_tempfile(suffix=".pem", create=False)
  106. @slowTest
  107. def test_issue_49027(self, pemfile):
  108. ret = self.run_state("x509.pem_managed", name=pemfile, text=self.x509_cert_text)
  109. assert isinstance(ret, dict), ret
  110. ret = ret[next(iter(ret))]
  111. assert ret.get("result") is True, ret
  112. with salt.utils.files.fopen(pemfile) as fp:
  113. result = fp.readlines()
  114. self.assertEqual(self.x509_cert_text.splitlines(True), result)
  115. @with_tempfile(suffix=".crt", create=False)
  116. @with_tempfile(suffix=".key", create=False)
  117. @slowTest
  118. def test_issue_49008(self, keyfile, crtfile):
  119. ret = self.run_function(
  120. "state.apply",
  121. ["issue-49008"],
  122. pillar={"keyfile": keyfile, "crtfile": crtfile},
  123. )
  124. assert isinstance(ret, dict), ret
  125. for state_result in ret.values():
  126. assert state_result["result"] is True, state_result
  127. assert os.path.exists(keyfile)
  128. assert os.path.exists(crtfile)
  129. @slowTest
  130. def test_cert_signing(self):
  131. ret = self.run_function(
  132. "state.apply", ["x509.cert_signing"], pillar={"tmp_dir": RUNTIME_VARS.TMP}
  133. )
  134. key = "x509_|-test_crt_|-{}/pki/test.crt_|-certificate_managed".format(
  135. RUNTIME_VARS.TMP
  136. )
  137. assert key in ret
  138. assert "changes" in ret[key]
  139. assert "Certificate" in ret[key]["changes"]
  140. assert "New" in ret[key]["changes"]["Certificate"]
  141. def test_cert_issue_not_before_not_after(self):
  142. ret = self.run_function(
  143. "state.apply",
  144. ["test_cert_not_before_not_after"],
  145. pillar={"tmp_dir": RUNTIME_VARS.TMP},
  146. )
  147. key = "x509_|-test_crt_|-{}/pki/test.crt_|-certificate_managed".format(
  148. RUNTIME_VARS.TMP
  149. )
  150. assert key in ret
  151. assert "changes" in ret[key]
  152. assert "Certificate" in ret[key]["changes"]
  153. assert "New" in ret[key]["changes"]["Certificate"]
  154. assert "Not Before" in ret[key]["changes"]["Certificate"]["New"]
  155. assert "Not After" in ret[key]["changes"]["Certificate"]["New"]
  156. not_before = ret[key]["changes"]["Certificate"]["New"]["Not Before"]
  157. not_after = ret[key]["changes"]["Certificate"]["New"]["Not After"]
  158. assert not_before == "2019-05-05 00:00:00"
  159. assert not_after == "2020-05-05 14:30:00"
  160. def test_cert_issue_not_before(self):
  161. ret = self.run_function(
  162. "state.apply",
  163. ["test_cert_not_before"],
  164. pillar={"tmp_dir": RUNTIME_VARS.TMP},
  165. )
  166. key = "x509_|-test_crt_|-{}/pki/test.crt_|-certificate_managed".format(
  167. RUNTIME_VARS.TMP
  168. )
  169. assert key in ret
  170. assert "changes" in ret[key]
  171. assert "Certificate" in ret[key]["changes"]
  172. assert "New" in ret[key]["changes"]["Certificate"]
  173. assert "Not Before" in ret[key]["changes"]["Certificate"]["New"]
  174. assert "Not After" in ret[key]["changes"]["Certificate"]["New"]
  175. not_before = ret[key]["changes"]["Certificate"]["New"]["Not Before"]
  176. assert not_before == "2019-05-05 00:00:00"
  177. def test_cert_issue_not_after(self):
  178. ret = self.run_function(
  179. "state.apply", ["test_cert_not_after"], pillar={"tmp_dir": RUNTIME_VARS.TMP}
  180. )
  181. key = "x509_|-test_crt_|-{}/pki/test.crt_|-certificate_managed".format(
  182. RUNTIME_VARS.TMP
  183. )
  184. assert key in ret
  185. assert "changes" in ret[key]
  186. assert "Certificate" in ret[key]["changes"]
  187. assert "New" in ret[key]["changes"]["Certificate"]
  188. assert "Not Before" in ret[key]["changes"]["Certificate"]["New"]
  189. assert "Not After" in ret[key]["changes"]["Certificate"]["New"]
  190. not_after = ret[key]["changes"]["Certificate"]["New"]["Not After"]
  191. assert not_after == "2020-05-05 14:30:00"
  192. @with_tempfile(suffix=".crt", create=False)
  193. @with_tempfile(suffix=".key", create=False)
  194. def test_issue_41858(self, keyfile, crtfile):
  195. ret_key = "x509_|-test_crt_|-{0}_|-certificate_managed".format(crtfile)
  196. signing_policy = "no_such_policy"
  197. ret = self.run_function(
  198. "state.apply",
  199. ["issue-41858.gen_cert"],
  200. pillar={
  201. "keyfile": keyfile,
  202. "crtfile": crtfile,
  203. "tmp_dir": RUNTIME_VARS.TMP,
  204. },
  205. )
  206. self.assertTrue(ret[ret_key]["result"])
  207. cert_sum = self.file_checksum(crtfile)
  208. ret = self.run_function(
  209. "state.apply",
  210. ["issue-41858.check"],
  211. pillar={
  212. "keyfile": keyfile,
  213. "crtfile": crtfile,
  214. "signing_policy": signing_policy,
  215. },
  216. )
  217. self.assertFalse(ret[ret_key]["result"])
  218. # self.assertSaltCommentRegexpMatches(ret[ret_key], "Signing policy {0} does not exist".format(signing_policy))
  219. self.assertEqual(self.file_checksum(crtfile), cert_sum)
  220. @with_tempfile(suffix=".crt", create=False)
  221. @with_tempfile(suffix=".key", create=False)
  222. def test_compound_match_minion_have_correct_grain_value(self, keyfile, crtfile):
  223. ret_key = "x509_|-test_crt_|-{0}_|-certificate_managed".format(crtfile)
  224. signing_policy = "compound_match"
  225. ret = self.run_function(
  226. "state.apply",
  227. ["x509_compound_match.gen_ca"],
  228. pillar={"tmp_dir": RUNTIME_VARS.TMP},
  229. )
  230. # sub_minion have grain set and CA is on other minion
  231. # CA minion have same grain with incorrect value
  232. ret = self.run_function(
  233. "state.apply",
  234. ["x509_compound_match.check"],
  235. minion_tgt="sub_minion",
  236. pillar={
  237. "keyfile": keyfile,
  238. "crtfile": crtfile,
  239. "signing_policy": signing_policy,
  240. },
  241. )
  242. self.assertTrue(ret[ret_key]["result"])
  243. @with_tempfile(suffix=".crt", create=False)
  244. @with_tempfile(suffix=".key", create=False)
  245. def test_compound_match_ca_have_correct_grain_value(self, keyfile, crtfile):
  246. self.run_function(
  247. "grains.set", ["x509_test_grain", "correct_value"], minion_tgt="minion"
  248. )
  249. self.run_function(
  250. "grains.set",
  251. ["x509_test_grain", "not_correct_value"],
  252. minion_tgt="sub_minion",
  253. )
  254. ret_key = "x509_|-test_crt_|-{0}_|-certificate_managed".format(crtfile)
  255. signing_policy = "compound_match"
  256. self.run_function(
  257. "state.apply",
  258. ["x509_compound_match.gen_ca"],
  259. pillar={"tmp_dir": RUNTIME_VARS.TMP},
  260. )
  261. ret = self.run_function(
  262. "state.apply",
  263. ["x509_compound_match.check"],
  264. minion_tgt="sub_minion",
  265. pillar={
  266. "keyfile": keyfile,
  267. "crtfile": crtfile,
  268. "signing_policy": signing_policy,
  269. },
  270. )
  271. self.assertFalse(ret[ret_key]["result"])
  272. @with_tempfile(suffix=".crt", create=False)
  273. @with_tempfile(suffix=".key", create=False)
  274. def test_self_signed_cert(self, keyfile, crtfile):
  275. """
  276. Self-signed certificate, no CA.
  277. Run the state twice to confirm the cert is only created once
  278. and its contents don't change.
  279. """
  280. first_run = self.run_function(
  281. "state.apply",
  282. ["x509.self_signed"],
  283. pillar={"keyfile": keyfile, "crtfile": crtfile},
  284. )
  285. key = "x509_|-self_signed_cert_|-{}_|-certificate_managed".format(crtfile)
  286. self.assertIn("New", first_run[key]["changes"]["Certificate"])
  287. self.assertEqual(
  288. "Certificate is valid and up to date",
  289. first_run[key]["changes"]["Status"]["New"],
  290. )
  291. self.assertTrue(os.path.exists(crtfile), "Certificate was not created.")
  292. with salt.utils.files.fopen(crtfile, "r") as first_cert:
  293. cert_contents = first_cert.read()
  294. second_run = self.run_function(
  295. "state.apply",
  296. ["x509.self_signed"],
  297. pillar={"keyfile": keyfile, "crtfile": crtfile},
  298. )
  299. self.assertEqual({}, second_run[key]["changes"])
  300. with salt.utils.files.fopen(crtfile, "r") as second_cert:
  301. self.assertEqual(
  302. cert_contents,
  303. second_cert.read(),
  304. "Certificate contents should not have changed.",
  305. )
  306. @with_tempfile(suffix=".crt", create=False)
  307. @with_tempfile(suffix=".key", create=False)
  308. def test_old_self_signed_cert_is_recreated(self, keyfile, crtfile):
  309. """
  310. Self-signed certificate, no CA.
  311. First create a cert that expires in 30 days, then recreate
  312. the cert because the second state run requires days_remaining
  313. to be at least 90.
  314. """
  315. first_run = self.run_function(
  316. "state.apply",
  317. ["x509.self_signed_expiry"],
  318. pillar={
  319. "keyfile": keyfile,
  320. "crtfile": crtfile,
  321. "days_valid": 30,
  322. "days_remaining": 10,
  323. },
  324. )
  325. key = "x509_|-self_signed_cert_|-{0}_|-certificate_managed".format(crtfile)
  326. self.assertEqual(
  327. "Certificate is valid and up to date",
  328. first_run[key]["changes"]["Status"]["New"],
  329. )
  330. expiry = datetime.datetime.strptime(
  331. first_run[key]["changes"]["Certificate"]["New"]["Not After"],
  332. "%Y-%m-%d %H:%M:%S",
  333. )
  334. self.assertEqual(29, (expiry - datetime.datetime.now()).days)
  335. self.assertTrue(os.path.exists(crtfile), "Certificate was not created.")
  336. with salt.utils.files.fopen(crtfile, "r") as first_cert:
  337. cert_contents = first_cert.read()
  338. second_run = self.run_function(
  339. "state.apply",
  340. ["x509.self_signed_expiry"],
  341. pillar={
  342. "keyfile": keyfile,
  343. "crtfile": crtfile,
  344. "days_valid": 180,
  345. "days_remaining": 90,
  346. },
  347. )
  348. self.assertEqual(
  349. "Certificate needs renewal: 29 days remaining but it needs to be at least 90",
  350. second_run[key]["changes"]["Status"]["Old"],
  351. )
  352. expiry = datetime.datetime.strptime(
  353. second_run[key]["changes"]["Certificate"]["New"]["Not After"],
  354. "%Y-%m-%d %H:%M:%S",
  355. )
  356. self.assertEqual(179, (expiry - datetime.datetime.now()).days)
  357. with salt.utils.files.fopen(crtfile, "r") as second_cert:
  358. self.assertNotEqual(
  359. cert_contents,
  360. second_cert.read(),
  361. "Certificate contents should have changed.",
  362. )
  363. @with_tempfile(suffix=".crt", create=False)
  364. @with_tempfile(suffix=".key", create=False)
  365. def test_mismatched_self_signed_cert_is_recreated(self, keyfile, crtfile):
  366. """
  367. Self-signed certificate, no CA.
  368. First create a cert, then run the state again with a different
  369. subjectAltName. The cert should be recreated.
  370. Finally, run once more with the same subjectAltName as the
  371. second run. Nothing should change.
  372. """
  373. first_run = self.run_function(
  374. "state.apply",
  375. ["x509.self_signed_different_properties"],
  376. pillar={
  377. "keyfile": keyfile,
  378. "crtfile": crtfile,
  379. "subjectAltName": "DNS:alt.service.local",
  380. },
  381. )
  382. key = "x509_|-self_signed_cert_|-{0}_|-certificate_managed".format(crtfile)
  383. self.assertEqual(
  384. "Certificate is valid and up to date",
  385. first_run[key]["changes"]["Status"]["New"],
  386. )
  387. sans = first_run[key]["changes"]["Certificate"]["New"]["X509v3 Extensions"][
  388. "subjectAltName"
  389. ]
  390. self.assertEqual("DNS:alt.service.local", sans)
  391. self.assertTrue(os.path.exists(crtfile), "Certificate was not created.")
  392. with salt.utils.files.fopen(crtfile, "r") as first_cert:
  393. first_cert_contents = first_cert.read()
  394. second_run_pillar = {
  395. "keyfile": keyfile,
  396. "crtfile": crtfile,
  397. "subjectAltName": "DNS:alt1.service.local, DNS:alt2.service.local",
  398. }
  399. second_run = self.run_function(
  400. "state.apply",
  401. ["x509.self_signed_different_properties"],
  402. pillar=second_run_pillar,
  403. )
  404. self.assertEqual(
  405. "Certificate properties are different: X509v3 Extensions",
  406. second_run[key]["changes"]["Status"]["Old"],
  407. )
  408. sans = second_run[key]["changes"]["Certificate"]["New"]["X509v3 Extensions"][
  409. "subjectAltName"
  410. ]
  411. self.assertEqual("DNS:alt1.service.local, DNS:alt2.service.local", sans)
  412. with salt.utils.files.fopen(crtfile, "r") as second_cert:
  413. second_cert_contents = second_cert.read()
  414. self.assertNotEqual(
  415. first_cert_contents,
  416. second_cert_contents,
  417. "Certificate contents should have changed.",
  418. )
  419. third_run = self.run_function(
  420. "state.apply",
  421. ["x509.self_signed_different_properties"],
  422. pillar=second_run_pillar,
  423. )
  424. self.assertEqual({}, third_run[key]["changes"])
  425. with salt.utils.files.fopen(crtfile, "r") as third_cert:
  426. self.assertEqual(
  427. second_cert_contents,
  428. third_cert.read(),
  429. "Certificate contents should not have changed.",
  430. )
  431. @with_tempfile(suffix=".crt", create=False)
  432. @with_tempfile(suffix=".key", create=False)
  433. def test_certificate_managed_with_managed_private_key_does_not_error(
  434. self, keyfile, crtfile
  435. ):
  436. """
  437. Test using the deprecated managed_private_key arg in certificate_managed does not throw an error.
  438. TODO: Remove this test in Aluminium when the arg is removed.
  439. """
  440. self.run_state("x509.private_key_managed", name=keyfile, bits=4096)
  441. ret = self.run_state(
  442. "x509.certificate_managed",
  443. name=crtfile,
  444. CN="localhost",
  445. signing_private_key=keyfile,
  446. managed_private_key={"name": keyfile, "bits": 4096},
  447. )
  448. key = "x509_|-{0}_|-{0}_|-certificate_managed".format(crtfile)
  449. self.assertEqual(True, ret[key]["result"])
  450. @with_tempfile(suffix=".crt", create=False)
  451. @with_tempfile(suffix=".key", create=False)
  452. def test_file_properties_are_updated(self, keyfile, crtfile):
  453. """
  454. Self-signed certificate, no CA.
  455. First create a cert, then run the state again with different
  456. file mode. The cert should not be recreated, but the file
  457. should be updated.
  458. Finally, run once more with the same file mode as the second
  459. run. Nothing should change.
  460. """
  461. first_run = self.run_function(
  462. "state.apply",
  463. ["x509.self_signed_different_properties"],
  464. pillar={"keyfile": keyfile, "crtfile": crtfile, "fileMode": "0755"},
  465. )
  466. key = "x509_|-self_signed_cert_|-{0}_|-certificate_managed".format(crtfile)
  467. self.assertEqual(
  468. "Certificate is valid and up to date",
  469. first_run[key]["changes"]["Status"]["New"],
  470. )
  471. self.assertTrue(os.path.exists(crtfile), "Certificate was not created.")
  472. self.assertEqual("0755", oct(os.stat(crtfile).st_mode)[-4:])
  473. second_run_pillar = {
  474. "keyfile": keyfile,
  475. "crtfile": crtfile,
  476. "mode": "0600",
  477. }
  478. second_run = self.run_function(
  479. "state.apply",
  480. ["x509.self_signed_different_properties"],
  481. pillar=second_run_pillar,
  482. )
  483. self.assertEqual("0600", oct(os.stat(crtfile).st_mode)[-4:])
  484. third_run = self.run_function(
  485. "state.apply",
  486. ["x509.self_signed_different_properties"],
  487. pillar=second_run_pillar,
  488. )
  489. self.assertEqual({}, third_run[key]["changes"])
  490. self.assertEqual("0600", oct(os.stat(crtfile).st_mode)[-4:])
  491. @with_tempfile(suffix=".crt", create=False)
  492. @with_tempfile(suffix=".key", create=False)
  493. def test_file_managed_failure(self, keyfile, crtfile):
  494. """
  495. Test that a failure in the file.managed call marks the state
  496. call as failed.
  497. """
  498. crtfile_pieces = os.path.split(crtfile)
  499. bad_crtfile = os.path.join(
  500. crtfile_pieces[0], "deeply/nested", crtfile_pieces[1]
  501. )
  502. ret = self.run_function(
  503. "state.apply",
  504. ["x509.self_signed_file_error"],
  505. pillar={"keyfile": keyfile, "crtfile": bad_crtfile},
  506. )
  507. key = "x509_|-self_signed_cert_|-{0}_|-certificate_managed".format(bad_crtfile)
  508. self.assertFalse(ret[key]["result"], "State should have failed.")
  509. self.assertEqual({}, ret[key]["changes"])
  510. self.assertFalse(
  511. os.path.exists(crtfile), "Certificate should not have been created."
  512. )
  513. @with_tempfile(suffix=".crt", create=False)
  514. @with_tempfile(suffix=".key", create=False)
  515. def test_py2_generated_cert_is_not_recreated(self, keyfile, crtfile):
  516. keyfile_contents = textwrap.dedent(
  517. """\
  518. -----BEGIN RSA PRIVATE KEY-----
  519. MIIEpAIBAAKCAQEAp5PQyx5NlYrfzd7vU/Xb2YR5qbWWtpWWoKmJC1gML5v5DBI7
  520. +p/kAHNNmK8uqHXTaI4N/zgarfjrg4zceq2Du7pP0xiCAYolhFqF78ibxNrN4OkT
  521. UPm2kM88iJ8Z14Yph8ueSxLIlujCGaEFhr6wRzTj4T9b+0Bb/PZHI2t5YwtIooVM
  522. EFCBFkt4bb004tO0D9q0CPPVT2AsGmxnY43Aj3Epy++kqmaWj1hIucSprkDrAXFS
  523. WacBQPFQ8XctnL2Z1Q6CJ5WUNrW8ohAJ9RJkwjiqbZTwYIPSSrl+FO3XqDY70SxU
  524. 3xDeqhU4zvyjxJ8w9SPqTUu/C3BZtRBT9dCBEQIDAQABAoIBAQCZvS23u1RYVrEe
  525. sWGF+LA67aOkg9kCJ1iqiv8UrjF32DNy1KO8OcY2d5H/+u/mUzqh2HmU5QbtBsoi
  526. xS9dSSTrLHGhbAGRogjrVRU9uCDYSBjLN2mmR4IrdkTF3pkZtpcRY0gU/eWTNXUl
  527. iCmGxhj5KtfJxZQAfLon6FW5dBdIOgxSCJhvRq0zFpWJZFGWWkBExDfeNg//0fCU
  528. UbjRjGacP/+R6FSJa6tevzgR7tIIapm1dY/ofPXIXsZGo1R87fRgLI1D+e84Jdds
  529. /U0bKzPOgAjcC1b262lJ8058pjG/nqWC0YUfpIJUVv2ciJpH3Ha+90526InLAUXA
  530. RWe1Z2YxAoGBANqACEKvUbxENu+XxQj0SI1co4SRTOvgbrSQGL61rDY6PvY/bOqC
  531. JeR0KC3MN6e7fx52tsl/eqP9iyExUpO9b0BCnGg967MivJXWUxhUdOL/r2ceQBqD
  532. DiPVZCFsjeNdSNihnNctAig9Po3GEUWE0ikHr3NcD+wXTnhnIEjJ/fltAoGBAMRW
  533. dIcOiuDLm/oDLNCpwEO4m63ymbUgeOj2cZhKMTqFmspnKnuCU1U/A8cuQcs1gydL
  534. 7MzxVP7MZDIEqT5gGj3eyuVMAmKbvLFR2NctDIDjaUs6oz0J9NGByPNjXaYr4uMd
  535. EZrxD8gLZ/G+/7eKsCgBA9ksSydDo00Vf/qAsmO1AoGBANWqc+l59eyrrCj5egU6
  536. lKQf3gsp51WV/8v0SS5dC41vwdgdx80+/fz8FbpLRHVypWlN34sFbRFmQ6Juz/iH
  537. O35UZQyO2KkxI8dGcbWOCUtditHExBzo4W/rIWKJ++pFc5Hb4DqO2dgto7kR4hvg
  538. OX9D869UbIGLfQHCntBvLju1AoGAHpcl0sEmTD4NEFgcTGqWZTbHMsQAxOLJU+rJ
  539. 6iNtJiQY6P5H9TRqDXci/I6te57bz2yZ+ZiEWKq51b06LVjF3evviuhb2sdPEAWj
  540. lmsTbqWAC1OYiXMarOXezGUn+zMNR7uIua5jehSk3lqW9x7psWHvGpA3KWf1cpYt
  541. +XbB1J0CgYBCSjALTv4dcn+CtS3kqb806z8H9MSZznUwSmcgvwCR5sqwLAUk1xRn
  542. hEqXbC1RGee3Xqv9mXPDK2LirpdRYi9Jr9ApZkrSkeaXSd2d4cy2ujUT0c7P8JrD
  543. i6QXb+HaFeBuS5ulYDmo4mIbCysuTsgrLzplViUy3xUQv23M/Eh1gw==
  544. -----END RSA PRIVATE KEY-----
  545. """
  546. )
  547. crtfile_contents = textwrap.dedent(
  548. """\
  549. -----BEGIN CERTIFICATE-----
  550. MIIEhTCCA22gAwIBAgIIUijHgif6VJUwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV
  551. BAYTAkJFMRgwFgYDVQQDDA9FeGFtcGxlIFJvb3QgQ0ExETAPBgNVBAcMCEthcGVs
  552. bGVuMRAwDgYDVQQIDAdBbnR3ZXJwMRAwDgYDVQQKDAdFeGFtcGxlMSIwIAYJKoZI
  553. hvcNAQkBFhNjZXJ0YWRtQGV4YW1wbGUub3JnMB4XDTIwMDYxNjA3Mzk1OVoXDTMw
  554. MDYxNDA3Mzk1OVowgYIxCzAJBgNVBAYTAkJFMRgwFgYDVQQDDA9FeGFtcGxlIFJv
  555. b3QgQ0ExETAPBgNVBAcMCEthcGVsbGVuMRAwDgYDVQQIDAdBbnR3ZXJwMRAwDgYD
  556. VQQKDAdFeGFtcGxlMSIwIAYJKoZIhvcNAQkBFhNjZXJ0YWRtQGV4YW1wbGUub3Jn
  557. MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp5PQyx5NlYrfzd7vU/Xb
  558. 2YR5qbWWtpWWoKmJC1gML5v5DBI7+p/kAHNNmK8uqHXTaI4N/zgarfjrg4zceq2D
  559. u7pP0xiCAYolhFqF78ibxNrN4OkTUPm2kM88iJ8Z14Yph8ueSxLIlujCGaEFhr6w
  560. RzTj4T9b+0Bb/PZHI2t5YwtIooVMEFCBFkt4bb004tO0D9q0CPPVT2AsGmxnY43A
  561. j3Epy++kqmaWj1hIucSprkDrAXFSWacBQPFQ8XctnL2Z1Q6CJ5WUNrW8ohAJ9RJk
  562. wjiqbZTwYIPSSrl+FO3XqDY70SxU3xDeqhU4zvyjxJ8w9SPqTUu/C3BZtRBT9dCB
  563. EQIDAQABo4H8MIH5MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
  564. A1UdDgQWBBTmNsYLuQTxpANgTuw7LRn1qHJsjzCBtgYDVR0jBIGuMIGrgBTmNsYL
  565. uQTxpANgTuw7LRn1qHJsj6GBiKSBhTCBgjELMAkGA1UEBhMCQkUxGDAWBgNVBAMM
  566. D0V4YW1wbGUgUm9vdCBDQTERMA8GA1UEBwwIS2FwZWxsZW4xEDAOBgNVBAgMB0Fu
  567. dHdlcnAxEDAOBgNVBAoMB0V4YW1wbGUxIjAgBgkqhkiG9w0BCQEWE2NlcnRhZG1A
  568. ZXhhbXBsZS5vcmeCCFIox4In+lSVMA0GCSqGSIb3DQEBCwUAA4IBAQBnC1/kK+xr
  569. Vjr5Y2YRjyjm4e8I/nTU+RX2p5K+Yth3CqWO3JuDiV/31UMtPl832n2GWSgXG2pP
  570. B52oeuCP4Re76jqhOmJWY3CKPji+Rs16wj199i9AAcwhSF0rpi5+Fi84HtP3q6pH
  571. cuzZfIPW44aJ5l4k+QvTLoWzr0XujMFcYzI45i3SJqTMs8xdIP5YLN8JXtQSPw9Z
  572. 8/nBKbPj7WTUC9cj9Cw2bz+wTpdRF4XCsUF3Vpl9fP7SK8yvv0I85LZnWQx1eQlv
  573. COAM5HWxUT9bWgv18zXdYkc6VLw6ufQSxxuhLMjJxuK27Ny/F18/xYLRTVnse36d
  574. tPJrseUPmvIK
  575. -----END CERTIFICATE-----
  576. """
  577. )
  578. slsfile = textwrap.dedent(
  579. """\
  580. {%- set ca_key_path = '"""
  581. + keyfile
  582. + """' %}
  583. {%- set ca_crt_path = '"""
  584. + crtfile
  585. + """' %}
  586. certificate.authority::private-key:
  587. x509.private_key_managed:
  588. - name: {{ ca_key_path }}
  589. - backup: True
  590. certificate.authority::certificate:
  591. x509.certificate_managed:
  592. - name: {{ ca_crt_path }}
  593. - signing_private_key: {{ ca_key_path }}
  594. - CN: Example Root CA
  595. - O: Example
  596. - C: BE
  597. - ST: Antwerp
  598. - L: Kapellen
  599. - Email: certadm@example.org
  600. - basicConstraints: "critical CA:true"
  601. - keyUsage: "critical cRLSign, keyCertSign"
  602. - subjectKeyIdentifier: hash
  603. - authorityKeyIdentifier: keyid,issuer:always
  604. - days_valid: 3650
  605. - days_remaining: 0
  606. - backup: True
  607. - require:
  608. - x509: certificate.authority::private-key
  609. """
  610. )
  611. with salt.utils.files.fopen(
  612. os.path.join(RUNTIME_VARS.TMP_STATE_TREE, "cert.sls"), "w"
  613. ) as wfh:
  614. wfh.write(slsfile)
  615. # Generate the certificate twice.
  616. # On the first run, no key nor cert exist.
  617. ret = self.run_function("state.sls", ["cert"])
  618. log.debug(
  619. "First state run ret dictionary:\n%s", pprint.pformat(list(ret.values()))
  620. )
  621. for state_run_id, state_run_details in ret.items():
  622. if state_run_id.endswith("private_key_managed"):
  623. assert state_run_details["result"]
  624. assert "new" in state_run_details["changes"]
  625. if state_run_id.endswith("certificate_managed"):
  626. assert state_run_details["result"]
  627. assert "Certificate" in state_run_details["changes"]
  628. assert "New" in state_run_details["changes"]["Certificate"]
  629. assert "Status" in state_run_details["changes"]
  630. assert "New" in state_run_details["changes"]["Status"]
  631. # On the second run, they exist and should not trigger any modification
  632. ret = self.run_function("state.sls", ["cert"])
  633. log.debug(
  634. "Second state run ret dictionary:\n%s", pprint.pformat(list(ret.values()))
  635. )
  636. for state_run_id, state_run_details in ret.items():
  637. if state_run_id.endswith("private_key_managed"):
  638. assert state_run_details["result"]
  639. assert state_run_details["changes"] == {}
  640. if state_run_id.endswith("certificate_managed"):
  641. assert state_run_details["result"]
  642. assert state_run_details["changes"] == {}
  643. # Now we repleace they key and cert contents with the contents of the above
  644. # call, but under Py2
  645. with salt.utils.files.fopen(keyfile, "w") as wfh:
  646. wfh.write(keyfile_contents)
  647. with salt.utils.files.fopen(keyfile) as rfh:
  648. log.debug("Written keyfile, %r, contents:\n%s", keyfile, rfh.read())
  649. with salt.utils.files.fopen(crtfile, "w") as wfh:
  650. wfh.write(crtfile_contents)
  651. with salt.utils.files.fopen(crtfile) as rfh:
  652. log.debug("Written crtfile, %r, contents:\n%s", crtfile, rfh.read())
  653. # We should not trigger any modification
  654. ret = self.run_function("state.sls", ["cert"])
  655. log.debug(
  656. "Third state run ret dictionary:\n%s", pprint.pformat(list(ret.values()))
  657. )
  658. for state_run_id, state_run_details in ret.items():
  659. if state_run_id.endswith("private_key_managed"):
  660. assert state_run_details["result"]
  661. assert state_run_details["changes"] == {}
  662. if state_run_id.endswith("certificate_managed"):
  663. assert state_run_details["result"]
  664. assert state_run_details["changes"] == {}