"Fossies" - the Fresh Open Source Software Archive 
Member "salt-3002.2/salt/states/x509.py" (18 Nov 2020, 28056 Bytes) of package /linux/misc/salt-3002.2.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "x509.py" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
3002.1_vs_3002.2.
1 """
2 Manage X509 Certificates
3
4 .. versionadded:: 2015.8.0
5
6 :depends: M2Crypto
7
8 This module can enable managing a complete PKI infrastructure including creating private keys, CAs,
9 certificates and CRLs. It includes the ability to generate a private key on a server, and have the
10 corresponding public key sent to a remote CA to create a CA signed certificate. This can be done in
11 a secure manner, where private keys are always generated locally and never moved across the network.
12
13 Here is a simple example scenario. In this example ``ca`` is the ca server,
14 and ``www`` is a web server that needs a certificate signed by ``ca``.
15
16 For remote signing, peers must be permitted to remotely call the
17 :mod:`sign_remote_certificate <salt.modules.x509.sign_remote_certificate>` function.
18
19
20 /etc/salt/master.d/peer.conf
21
22 .. code-block:: yaml
23
24 peer:
25 .*:
26 - x509.sign_remote_certificate
27
28
29 /srv/salt/top.sls
30
31 .. code-block:: yaml
32
33 base:
34 '*':
35 - cert
36 'ca':
37 - ca
38 'www':
39 - www
40
41
42 This state creates the CA key, certificate and signing policy. It also publishes the certificate to
43 the mine where it can be easily retrieved by other minions.
44
45 /srv/salt/ca.sls
46
47 .. code-block:: yaml
48
49 salt-minion:
50 service.running:
51 - enable: True
52 - watch:
53 - file: /etc/salt/minion.d/x509.conf
54
55 /etc/salt/minion.d/x509.conf:
56 file.managed:
57 - source: salt://x509.conf
58
59 /etc/pki:
60 file.directory
61
62 /etc/pki/issued_certs:
63 file.directory
64
65 /etc/pki/ca.crt:
66 x509.private_key_managed:
67 - name: /etc/pki/ca.key
68 - bits: 4096
69 - backup: True
70
71 /etc/pki/ca.crt:
72 x509.certificate_managed:
73 - signing_private_key: /etc/pki/ca.key
74 - CN: ca.example.com
75 - C: US
76 - ST: Utah
77 - L: Salt Lake City
78 - basicConstraints: "critical CA:true"
79 - keyUsage: "critical cRLSign, keyCertSign"
80 - subjectKeyIdentifier: hash
81 - authorityKeyIdentifier: keyid,issuer:always
82 - days_valid: 3650
83 - days_remaining: 0
84 - backup: True
85 - require:
86 - file: /etc/pki
87
88
89 The signing policy defines properties that override any property requested or included in a CRL. It also
90 can define a restricted list of minions which are allowed to remotely invoke this signing policy.
91
92 /srv/salt/x509.conf
93
94 .. code-block:: yaml
95
96 mine_functions:
97 x509.get_pem_entries: [/etc/pki/ca.crt]
98
99 x509_signing_policies:
100 www:
101 - minions: 'www'
102 - signing_private_key: /etc/pki/ca.key
103 - signing_cert: /etc/pki/ca.crt
104 - C: US
105 - ST: Utah
106 - L: Salt Lake City
107 - basicConstraints: "critical CA:false"
108 - keyUsage: "critical keyEncipherment"
109 - subjectKeyIdentifier: hash
110 - authorityKeyIdentifier: keyid,issuer:always
111 - days_valid: 90
112 - copypath: /etc/pki/issued_certs/
113
114
115 This state will instruct all minions to trust certificates signed by our new CA.
116 Using Jinja to strip newlines from the text avoids dealing with newlines in the rendered YAML,
117 and the :mod:`sign_remote_certificate <salt.states.x509.sign_remote_certificate>` state will
118 handle properly formatting the text before writing the output.
119
120 /srv/salt/cert.sls
121
122 .. code-block:: jinja
123
124 /usr/local/share/ca-certificates:
125 file.directory
126
127 /usr/local/share/ca-certificates/intca.crt:
128 x509.pem_managed:
129 - text: {{ salt['mine.get']('ca', 'x509.get_pem_entries')['ca']['/etc/pki/ca.crt']|replace('\\n', '') }}
130
131
132 This state creates a private key then requests a certificate signed by ca according to the www policy.
133
134 /srv/salt/www.sls
135
136 .. code-block:: yaml
137
138 /etc/pki/www.crt:
139 x509.private_key_managed:
140 - name: /etc/pki/www.key
141 - bits: 4096
142 - backup: True
143
144 /etc/pki/www.crt:
145 x509.certificate_managed:
146 - ca_server: ca
147 - signing_policy: www
148 - public_key: /etc/pki/www.key
149 - CN: www.example.com
150 - days_remaining: 30
151 - backup: True
152
153 This other state creates a private key then requests a certificate signed by ca
154 according to the www policy but adds a strict date range for the certificate to
155 be considered valid.
156
157 /srv/salt/www-time-limited.sls
158
159 .. code-block:: yaml
160
161 /etc/pki/www-time-limited.crt:
162 x509.certificate_managed:
163 - ca_server: ca
164 - signing_policy: www
165 - public_key: /etc/pki/www-time-limited.key
166 - CN: www.example.com
167 - not_before: 2019-05-05 00:00:00
168 - not_after: 2020-05-05 14:30:00
169 - backup: True
170
171 """
172
173 import copy
174 import datetime
175 import logging
176 import os
177 import re
178
179 import salt.exceptions
180 import salt.utils.versions
181
182 try:
183 from M2Crypto.RSA import RSAError
184 except ImportError:
185 pass
186
187 log = logging.getLogger(__name__)
188
189
190 def __virtual__():
191 """
192 only load this module if the corresponding execution module is loaded
193 """
194 if "x509.get_pem_entry" in __salt__:
195 return "x509"
196 else:
197 return (False, "Could not load x509 state: m2crypto unavailable")
198
199
200 def _revoked_to_list(revs):
201 """
202 Turn the mess of OrderedDicts and Lists into a list of dicts for
203 use in the CRL module.
204 """
205 list_ = []
206
207 for rev in revs:
208 for props in rev.values():
209 dict_ = {}
210 for prop in props:
211 for propname, val in prop.items():
212 if isinstance(val, datetime.datetime):
213 val = val.strftime("%Y-%m-%d %H:%M:%S")
214 dict_[propname] = val
215 list_.append(dict_)
216
217 return list_
218
219
220 def _get_file_args(name, **kwargs):
221 valid_file_args = [
222 "user",
223 "group",
224 "mode",
225 "makedirs",
226 "dir_mode",
227 "backup",
228 "create",
229 "follow_symlinks",
230 "check_cmd",
231 ]
232 file_args = {}
233 extra_args = {}
234 for k, v in kwargs.items():
235 if k in valid_file_args:
236 file_args[k] = v
237 else:
238 extra_args[k] = v
239 file_args["name"] = name
240 return file_args, extra_args
241
242
243 def _check_private_key(name, bits=2048, passphrase=None, new=False, overwrite=False):
244 current_bits = 0
245 if os.path.isfile(name):
246 try:
247 current_bits = __salt__["x509.get_private_key_size"](
248 private_key=name, passphrase=passphrase
249 )
250 except salt.exceptions.SaltInvocationError:
251 pass
252 except RSAError:
253 if not overwrite:
254 raise salt.exceptions.CommandExecutionError(
255 "The provided passphrase cannot decrypt the private key."
256 )
257
258 return current_bits == bits and not new
259
260
261 def private_key_managed(
262 name,
263 bits=2048,
264 passphrase=None,
265 cipher="aes_128_cbc",
266 new=False,
267 overwrite=False,
268 verbose=True,
269 **kwargs
270 ):
271 """
272 Manage a private key's existence.
273
274 name:
275 Path to the private key
276
277 bits:
278 Key length in bits. Default 2048.
279
280 passphrase:
281 Passphrase for encrypting the private key.
282
283 cipher:
284 Cipher for encrypting the private key.
285
286 new:
287 Always create a new key. Defaults to ``False``.
288 Combining new with :mod:`prereq <salt.states.requsities.preqreq>`
289 can allow key rotation whenever a new certificate is generated.
290
291 overwrite:
292 Overwrite an existing private key if the provided passphrase cannot decrypt it.
293
294 verbose:
295 Provide visual feedback on stdout, dots while key is generated.
296 Default is True.
297
298 .. versionadded:: 2016.11.0
299
300 kwargs:
301 Any kwargs supported by file.managed are supported.
302
303 Example:
304
305 The JINJA templating in this example ensures a private key is generated if the file doesn't exist
306 and that a new private key is generated whenever the certificate that uses it is to be renewed.
307
308 .. code-block:: jinja
309
310 /etc/pki/www.key:
311 x509.private_key_managed:
312 - bits: 4096
313 - new: True
314 {% if salt['file.file_exists']('/etc/pki/www.key') -%}
315 - prereq:
316 - x509: /etc/pki/www.crt
317 {%- endif %}
318 """
319 file_args, kwargs = _get_file_args(name, **kwargs)
320 new_key = False
321 if _check_private_key(
322 name, bits=bits, passphrase=passphrase, new=new, overwrite=overwrite
323 ):
324 file_args["contents"] = __salt__["x509.get_pem_entry"](
325 name, pem_type="RSA PRIVATE KEY"
326 )
327 else:
328 new_key = True
329 file_args["contents"] = __salt__["x509.create_private_key"](
330 text=True, bits=bits, passphrase=passphrase, cipher=cipher, verbose=verbose
331 )
332
333 # Ensure the key contents are a string before passing it along
334 file_args["contents"] = salt.utils.stringutils.to_str(file_args["contents"])
335
336 ret = __states__["file.managed"](**file_args)
337 if ret["changes"] and new_key:
338 ret["changes"] = {"new": "New private key generated"}
339
340 return ret
341
342
343 def csr_managed(name, **kwargs):
344 """
345 Manage a Certificate Signing Request
346
347 name:
348 Path to the CSR
349
350 properties:
351 The properties to be added to the certificate request, including items like subject, extensions
352 and public key. See above for valid properties.
353
354 kwargs:
355 Any arguments supported by :py:func:`file.managed <salt.states.file.managed>` are supported.
356
357 Example:
358
359 .. code-block:: yaml
360
361 /etc/pki/mycert.csr:
362 x509.csr_managed:
363 - private_key: /etc/pki/mycert.key
364 - CN: www.example.com
365 - C: US
366 - ST: Utah
367 - L: Salt Lake City
368 - keyUsage: 'critical dataEncipherment'
369 """
370 try:
371 old = __salt__["x509.read_csr"](name)
372 except salt.exceptions.SaltInvocationError:
373 old = "{} is not a valid csr.".format(name)
374
375 file_args, kwargs = _get_file_args(name, **kwargs)
376 file_args["contents"] = __salt__["x509.create_csr"](text=True, **kwargs)
377
378 ret = __states__["file.managed"](**file_args)
379 if ret["changes"]:
380 new = __salt__["x509.read_csr"](file_args["contents"])
381 if old != new:
382 ret["changes"] = {"Old": old, "New": new}
383
384 return ret
385
386
387 def _certificate_info_matches(cert_info, required_cert_info, check_serial=False):
388 """
389 Return true if the provided certificate information matches the
390 required certificate information, i.e. it has the required common
391 name, subject alt name, organization, etc.
392
393 cert_info should be a dict as returned by x509.read_certificate.
394 required_cert_info should be a dict as returned by x509.create_certificate with testrun=True.
395 """
396 # don't modify the incoming dicts
397 cert_info = copy.deepcopy(cert_info)
398 required_cert_info = copy.deepcopy(required_cert_info)
399
400 ignored_keys = [
401 "Not Before",
402 "Not After",
403 "MD5 Finger Print",
404 "SHA1 Finger Print",
405 "SHA-256 Finger Print",
406 # The integrity of the issuer is checked elsewhere
407 "Issuer Public Key",
408 ]
409 for key in ignored_keys:
410 cert_info.pop(key, None)
411 required_cert_info.pop(key, None)
412
413 if not check_serial:
414 cert_info.pop("Serial Number", None)
415 required_cert_info.pop("Serial Number", None)
416 try:
417 cert_info["X509v3 Extensions"]["authorityKeyIdentifier"] = re.sub(
418 r"serial:([0-9A-F]{2}:)*[0-9A-F]{2}",
419 "serial:--",
420 cert_info["X509v3 Extensions"]["authorityKeyIdentifier"],
421 )
422 required_cert_info["X509v3 Extensions"]["authorityKeyIdentifier"] = re.sub(
423 r"serial:([0-9A-F]{2}:)*[0-9A-F]{2}",
424 "serial:--",
425 required_cert_info["X509v3 Extensions"]["authorityKeyIdentifier"],
426 )
427 except KeyError:
428 pass
429
430 diff = []
431 for k, v in required_cert_info.items():
432 # cert info comes as byte string
433 if isinstance(v, str):
434 v = salt.utils.stringutils.to_bytes(v)
435 try:
436 if v != cert_info[k]:
437 if k == "Subject Hash":
438 # If we failed the subject hash check but the subject matches, then this is
439 # likely a certificate generated under Python 2 where sorting differs and thus
440 # the hash also differs
441 if required_cert_info["Subject"] != cert_info["Subject"]:
442 diff.append(k)
443 elif k == "Issuer Hash":
444 # If we failed the issuer hash check but the issuer matches, then this is
445 # likely a certificate generated under Python 2 where sorting differs and thus
446 # the hash also differs
447 if required_cert_info["Issuer"] != cert_info["Issuer"]:
448 diff.append(k)
449 elif k == "X509v3 Extensions":
450 v_ext = v.copy()
451 cert_info_ext = cert_info[k].copy()
452 # DirName depends on ordering which was different on certificates created
453 # under Python 2. Remove that from the comparisson
454 try:
455 v_ext["authorityKeyIdentifier"] = re.sub(
456 r"DirName:([^\n]+)",
457 "Dirname:--",
458 v_ext["authorityKeyIdentifier"],
459 )
460 cert_info_ext["authorityKeyIdentifier"] = re.sub(
461 r"DirName:([^\n]+)",
462 "Dirname:--",
463 cert_info_ext["authorityKeyIdentifier"],
464 )
465 except KeyError:
466 pass
467 if v_ext != cert_info_ext:
468 diff.append(k)
469 else:
470 diff.append(k)
471 except KeyError:
472 diff.append(k)
473
474 return len(diff) == 0, diff
475
476
477 def _certificate_days_remaining(cert_info):
478 """
479 Get the days remaining on a certificate, defaulting to 0 if an error occurs.
480 """
481 try:
482 expiry = cert_info["Not After"]
483 return (
484 datetime.datetime.strptime(expiry, "%Y-%m-%d %H:%M:%S")
485 - datetime.datetime.now()
486 ).days
487 except KeyError:
488 return 0
489
490
491 def _certificate_is_valid(name, days_remaining, append_certs, **cert_spec):
492 """
493 Return True if the given certificate file exists, is a certificate, matches the given specification,
494 and has the required days remaining.
495
496 If False, also provide a message explaining why.
497 """
498 if not os.path.isfile(name):
499 return False, "{} does not exist".format(name), {}
500
501 try:
502 cert_info = __salt__["x509.read_certificate"](certificate=name)
503 required_cert_info = __salt__["x509.create_certificate"](
504 testrun=True, **cert_spec
505 )
506 if not isinstance(required_cert_info, dict):
507 raise salt.exceptions.SaltInvocationError(
508 "Unable to create new certificate: x509 module error: {}".format(
509 required_cert_info
510 )
511 )
512
513 try:
514 issuer_public_key = required_cert_info["Issuer Public Key"]
515 # Verify the certificate has been signed by the ca_server or private_signing_key
516 if not __salt__["x509.verify_signature"](name, issuer_public_key):
517 errmsg = (
518 "Certificate is not signed by private_signing_key"
519 if "signing_private_key" in cert_spec
520 else "Certificate is not signed by the requested issuer"
521 )
522 return False, errmsg, cert_info
523 except KeyError:
524 return (
525 False,
526 "New certificate does not include signing information",
527 cert_info,
528 )
529
530 matches, diff = _certificate_info_matches(
531 cert_info, required_cert_info, check_serial="serial_number" in cert_spec
532 )
533 if not matches:
534 return (
535 False,
536 "Certificate properties are different: {}".format(", ".join(diff)),
537 cert_info,
538 )
539
540 actual_days_remaining = _certificate_days_remaining(cert_info)
541 if days_remaining != 0 and actual_days_remaining < days_remaining:
542 return (
543 False,
544 "Certificate needs renewal: {} days remaining but it needs to be at least {}".format(
545 actual_days_remaining, days_remaining
546 ),
547 cert_info,
548 )
549
550 return True, "", cert_info
551 except salt.exceptions.SaltInvocationError as e:
552 return False, "{} is not a valid certificate: {}".format(name, str(e)), {}
553
554
555 def _certificate_file_managed(ret, file_args):
556 """
557 Run file.managed and merge the result with an existing return dict.
558 The overall True/False result will be the result of the file.managed call.
559 """
560 file_ret = __states__["file.managed"](**file_args)
561
562 ret["result"] = file_ret["result"]
563 if ret["result"]:
564 ret["comment"] = "Certificate {} is valid and up to date".format(ret["name"])
565 else:
566 ret["comment"] = file_ret["comment"]
567
568 if file_ret["changes"]:
569 ret["changes"] = {"File": file_ret["changes"]}
570
571 return ret
572
573
574 def certificate_managed(
575 name, days_remaining=90, append_certs=None, managed_private_key=None, **kwargs
576 ):
577 """
578 Manage a Certificate
579
580 name
581 Path to the certificate
582
583 days_remaining : 90
584 Recreate the certificate if the number of days remaining on it
585 are less than this number. The value should be less than
586 ``days_valid``, otherwise the certificate will be recreated
587 every time the state is run. A value of 0 disables automatic
588 renewal.
589
590 append_certs:
591 A list of certificates to be appended to the managed file.
592 They must be valid PEM files, otherwise an error will be thrown.
593
594 managed_private_key:
595 Has no effect since v2016.11 and will be removed in Salt Aluminium.
596 Use a separate x509.private_key_managed call instead.
597
598 kwargs:
599 Any arguments supported by :py:func:`x509.create_certificate
600 <salt.modules.x509.create_certificate>` or :py:func:`file.managed
601 <salt.states.file.managed>` are supported.
602
603 not_before:
604 Initial validity date for the certificate. This date must be specified
605 in the format '%Y-%m-%d %H:%M:%S'.
606
607 .. versionadded:: 3001
608 not_after:
609 Final validity date for the certificate. This date must be specified in
610 the format '%Y-%m-%d %H:%M:%S'.
611
612 .. versionadded:: 3001
613
614 Examples:
615
616 .. code-block:: yaml
617
618 /etc/pki/ca.crt:
619 x509.certificate_managed:
620 - signing_private_key: /etc/pki/ca.key
621 - CN: ca.example.com
622 - C: US
623 - ST: Utah
624 - L: Salt Lake City
625 - basicConstraints: "critical CA:true"
626 - keyUsage: "critical cRLSign, keyCertSign"
627 - subjectKeyIdentifier: hash
628 - authorityKeyIdentifier: keyid,issuer:always
629 - days_valid: 3650
630 - days_remaining: 0
631 - backup: True
632
633
634 .. code-block:: yaml
635
636 /etc/ssl/www.crt:
637 x509.certificate_managed:
638 - ca_server: pki
639 - signing_policy: www
640 - public_key: /etc/ssl/www.key
641 - CN: www.example.com
642 - days_valid: 90
643 - days_remaining: 30
644 - backup: True
645
646 """
647 if "path" in kwargs:
648 name = kwargs.pop("path")
649
650 if "ca_server" in kwargs and "signing_policy" not in kwargs:
651 raise salt.exceptions.SaltInvocationError(
652 "signing_policy must be specified if ca_server is."
653 )
654
655 if (
656 "public_key" not in kwargs
657 and "signing_private_key" not in kwargs
658 and "csr" not in kwargs
659 ):
660 raise salt.exceptions.SaltInvocationError(
661 "public_key, signing_private_key, or csr must be specified."
662 )
663
664 if managed_private_key:
665 salt.utils.versions.warn_until(
666 "Aluminium",
667 "Passing 'managed_private_key' to x509.certificate_managed has no effect and "
668 "will be removed Salt Aluminium. Use a separate x509.private_key_managed call instead.",
669 )
670
671 ret = {"name": name, "result": False, "changes": {}, "comment": ""}
672
673 is_valid, invalid_reason, current_cert_info = _certificate_is_valid(
674 name, days_remaining, append_certs, **kwargs
675 )
676
677 if is_valid:
678 file_args, extra_args = _get_file_args(name, **kwargs)
679
680 return _certificate_file_managed(ret, file_args)
681
682 if __opts__["test"]:
683 file_args, extra_args = _get_file_args(name, **kwargs)
684 # Use empty contents for file.managed in test mode.
685 # We don't want generate a new certificate, even in memory,
686 # for security reasons.
687 # Using an empty string instead of omitting it will at least
688 # show the old certificate in the diff.
689 file_args["contents"] = ""
690
691 ret = _certificate_file_managed(ret, file_args)
692
693 ret["result"] = None
694 ret["comment"] = "Certificate {} will be created".format(name)
695 ret["changes"]["Status"] = {
696 "Old": invalid_reason,
697 "New": "Certificate will be valid and up to date",
698 }
699 return ret
700
701 contents = __salt__["x509.create_certificate"](text=True, **kwargs)
702 # Check the module actually returned a cert and not an error message as a string
703 try:
704 __salt__["x509.read_certificate"](contents)
705 except salt.exceptions.SaltInvocationError as e:
706 ret["result"] = False
707 ret[
708 "comment"
709 ] = "An error occurred creating the certificate {}. The result returned from x509.create_certificate is not a valid PEM file:\n{}".format(
710 name, str(e)
711 )
712 return ret
713
714 if not append_certs:
715 append_certs = []
716 for append_file in append_certs:
717 try:
718 append_file_contents = __salt__["x509.get_pem_entry"](
719 append_file, pem_type="CERTIFICATE"
720 )
721 contents += append_file_contents
722 except salt.exceptions.SaltInvocationError as e:
723 ret["result"] = False
724 ret[
725 "comment"
726 ] = "{} is not a valid certificate file, cannot append it to the certificate {}.\nThe error returned by the x509 module was:\n{}".format(
727 append_file, name, str(e)
728 )
729 return ret
730
731 file_args, extra_args = _get_file_args(name, **kwargs)
732 file_args["contents"] = contents
733
734 ret = _certificate_file_managed(ret, file_args)
735
736 if ret["result"]:
737 ret["changes"]["Certificate"] = {
738 "Old": current_cert_info,
739 "New": __salt__["x509.read_certificate"](certificate=name),
740 }
741 ret["changes"]["Status"] = {
742 "Old": invalid_reason,
743 "New": "Certificate is valid and up to date",
744 }
745
746 return ret
747
748
749 def crl_managed(
750 name,
751 signing_private_key,
752 signing_private_key_passphrase=None,
753 signing_cert=None,
754 revoked=None,
755 days_valid=100,
756 digest="",
757 days_remaining=30,
758 include_expired=False,
759 **kwargs
760 ):
761 """
762 Manage a Certificate Revocation List
763
764 name
765 Path to the certificate
766
767 signing_private_key
768 The private key that will be used to sign the CRL. This is
769 usually your CA's private key.
770
771 signing_private_key_passphrase
772 Passphrase to decrypt the private key.
773
774 signing_cert
775 The certificate of the authority that will be used to sign the CRL.
776 This is usually your CA's certificate.
777
778 revoked
779 A list of certificates to revoke. Must include either a serial number or a
780 the certificate itself. Can optionally include the revocation date and
781 notAfter date from the certificate. See example below for details.
782
783 days_valid : 100
784 The number of days the certificate should be valid for.
785
786 digest
787 The digest to use for signing the CRL. This has no effect on versions
788 of pyOpenSSL less than 0.14.
789
790 days_remaining : 30
791 The CRL should be automatically recreated if there are less than
792 ``days_remaining`` days until the CRL expires. Set to 0 to disable
793 automatic renewal.
794
795 include_expired : False
796 If ``True``, include expired certificates in the CRL.
797
798 kwargs
799 Any arguments supported by :py:func:`file.managed <salt.states.file.managed>` are supported.
800
801 Example:
802
803 .. code-block:: yaml
804
805 /etc/pki/ca.crl:
806 x509.crl_managed:
807 - signing_private_key: /etc/pki/myca.key
808 - signing_cert: /etc/pki/myca.crt
809 - revoked:
810 - compromized_Web_key:
811 - certificate: /etc/pki/certs/badweb.crt
812 - revocation_date: 2015-03-01 00:00:00
813 - reason: keyCompromise
814 - terminated_vpn_user:
815 - serial_number: D6:D2:DC:D8:4D:5C:C0:F4
816 - not_after: 2016-01-01 00:00:00
817 - revocation_date: 2015-02-25 00:00:00
818 - reason: cessationOfOperation
819 """
820 if revoked is None:
821 revoked = []
822
823 revoked = _revoked_to_list(revoked)
824
825 current_days_remaining = 0
826 current_comp = {}
827
828 if os.path.isfile(name):
829 try:
830 current = __salt__["x509.read_crl"](crl=name)
831 current_comp = current.copy()
832 current_comp.pop("Last Update")
833 current_notafter = current_comp.pop("Next Update")
834 current_days_remaining = (
835 datetime.datetime.strptime(current_notafter, "%Y-%m-%d %H:%M:%S")
836 - datetime.datetime.now()
837 ).days
838 if days_remaining == 0:
839 days_remaining = current_days_remaining - 1
840 except salt.exceptions.SaltInvocationError:
841 current = "{} is not a valid CRL.".format(name)
842 else:
843 current = "{} does not exist.".format(name)
844
845 new_crl = __salt__["x509.create_crl"](
846 text=True,
847 signing_private_key=signing_private_key,
848 signing_private_key_passphrase=signing_private_key_passphrase,
849 signing_cert=signing_cert,
850 revoked=revoked,
851 days_valid=days_valid,
852 digest=digest,
853 include_expired=include_expired,
854 )
855
856 new = __salt__["x509.read_crl"](crl=new_crl)
857 new_comp = new.copy()
858 new_comp.pop("Last Update")
859 new_comp.pop("Next Update")
860
861 file_args, kwargs = _get_file_args(name, **kwargs)
862 new_crl_created = False
863 if (
864 current_comp == new_comp
865 and current_days_remaining > days_remaining
866 and __salt__["x509.verify_crl"](name, signing_cert)
867 ):
868 file_args["contents"] = __salt__["x509.get_pem_entry"](
869 name, pem_type="X509 CRL"
870 )
871 else:
872 new_crl_created = True
873 file_args["contents"] = new_crl
874
875 ret = __states__["file.managed"](**file_args)
876 if new_crl_created:
877 ret["changes"] = {"Old": current, "New": __salt__["x509.read_crl"](crl=new_crl)}
878 return ret
879
880
881 def pem_managed(name, text, backup=False, **kwargs):
882 """
883 Manage the contents of a PEM file directly with the content in text, ensuring correct formatting.
884
885 name:
886 The path to the file to manage
887
888 text:
889 The PEM formatted text to write.
890
891 kwargs:
892 Any arguments supported by :py:func:`file.managed <salt.states.file.managed>` are supported.
893 """
894 file_args, kwargs = _get_file_args(name, **kwargs)
895 file_args["contents"] = __salt__["x509.get_pem_entry"](text=text)
896
897 return __states__["file.managed"](**file_args)