openssh_cert - adding signature_algorithm option (#277)

* Initial Commit

* Update supported OpenSSH versions for RSA SHA-2 signed certs

* Updating 'regenerate' documentation
pull/282/head
Ajpantuso 2021-09-15 02:53:53 -04:00 committed by GitHub
parent 8521c96e8a
commit eea7bfc6bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 113 additions and 4 deletions

View File

@ -160,7 +160,8 @@ class KeygenCommand(object):
self._run_command = module.run_command
def generate_certificate(self, certificate_path, identifier, options, pkcs11_provider, principals,
serial_number, signing_key_path, type, time_parameters, use_agent, **kwargs):
serial_number, signature_algorithm, signing_key_path, type,
time_parameters, use_agent, **kwargs):
args = [self._bin_path, '-s', signing_key_path, '-P', '', '-I', identifier]
if options:
@ -178,6 +179,8 @@ class KeygenCommand(object):
args.extend(['-U'])
if time_parameters.validity_string:
args.extend(['-V', time_parameters.validity_string])
if signature_algorithm:
args.extend(['-t', signature_algorithm])
args.append(certificate_path)
return self._run_command(args, **kwargs)

View File

@ -555,6 +555,11 @@ class OpensshCertificate(object):
def signing_key(self):
return to_text(self._cert_info.signing_key_fingerprint())
@property
def signature_type(self):
signature_data = OpensshParser.signature_data(self.signature)
return to_text(signature_data['signature_type'])
@staticmethod
def _parse_cert_info(pub_key_type, parser):
cert_info = get_cert_info_object(pub_key_type)

View File

@ -201,8 +201,9 @@ class OpensshParser(object):
signature_blob = parser.string()
blob_parser = cls(signature_blob)
if signature_type == b'ssh-rsa':
if signature_type in (b'ssh-rsa', b'rsa-sha2-256', b'rsa-sha2-512'):
# https://datatracker.ietf.org/doc/html/rfc4253#section-6.6
# https://datatracker.ietf.org/doc/html/rfc8332#section-3
signature_data['s'] = cls._big_int(signature_blob, "big")
elif signature_type == b'ssh-dss':
# https://datatracker.ietf.org/doc/html/rfc4253#section-6.6

View File

@ -49,7 +49,7 @@ options:
- When C(fail) the task will fail if a certificate already exists at I(path) and does not
match the module's options.
- When C(partial_idempotence) an existing certificate will be regenerated based on
I(serial), I(type), I(valid_from), I(valid_to), I(valid_at), and I(principals).
I(serial), I(signature_algorithm), I(type), I(valid_from), I(valid_to), I(valid_at), and I(principals).
- When C(full_idempotence) I(identifier), I(options), I(public_key), and I(signing_key)
are also considered when compared against an existing certificate.
- C(always) is equivalent to I(force=true).
@ -62,6 +62,26 @@ options:
- always
default: partial_idempotence
version_added: 1.8.0
signature_algorithm:
description:
- As of OpenSSH 8.2 the SHA-1 signature algorithm for RSA keys has been disabled and C(ssh) will refuse
host certificates signed with the SHA-1 algorithm. OpenSSH 8.1 made C(rsa-sha2-512) the default algorithm
when acting as a CA and signing certificates with a RSA key. However, for OpenSSH versions less than 8.1
the SHA-2 signature algorithms, C(rsa-sha2-256) or C(rsa-sha2-512), must be specified using this option
if compatibility with newer C(ssh) clients is required. Conversely if hosts using OpenSSH version 8.2
or greater must remain compatible with C(ssh) clients using OpenSSH less than 7.2, then C(ssh-rsa)
can be used when generating host certificates (a corresponding change to the sshd_config to add C(ssh-rsa)
to the C(CASignatureAlgorithms) keyword is also required).
- Using any value for this option with a non-RSA I(signing_key) will cause this module to fail.
- "Note: OpenSSH versions prior to 7.2 do not support SHA-2 signature algorithms for RSA keys and OpenSSH
versions prior to 7.3 do not support SHA-2 signature algorithms for certificates."
- See U(https://www.openssh.com/txt/release-8.2) for more information.
type: str
choices:
- ssh-rsa
- rsa-sha2-256
- rsa-sha2-512
version_added: 1.10.0
signing_key:
description:
- The path to the private openssh key that is used for signing the public key in order to generate the certificate.
@ -269,6 +289,7 @@ class Certificate(OpensshModule):
self.public_key = self.module.params['public_key']
self.regenerate = self.module.params['regenerate'] if not self.module.params['force'] else 'always'
self.serial_number = self.module.params['serial_number']
self.signature_algorithm = self.module.params['signature_algorithm']
self.signing_key = self.module.params['signing_key']
self.state = self.module.params['state']
self.type = self.module.params['type']
@ -366,6 +387,7 @@ class Certificate(OpensshModule):
def _is_partially_valid(self):
return all([
set(self.original_data.principals) == set(self.principals),
self.original_data.signature_type == self.signature_algorithm if self.signature_algorithm else True,
self.original_data.serial == self.serial_number if self.serial_number is not None else True,
self.original_data.type == self.type,
self._compare_time_parameters(),
@ -425,7 +447,7 @@ class Certificate(OpensshModule):
self.ssh_keygen.generate_certificate(
key_copy, self.identifier, self.options, self.pkcs11_provider, self.principals, self.serial_number,
self.signing_key, self.type, self.time_parameters, self.use_agent,
self.signature_algorithm, self.signing_key, self.type, self.time_parameters, self.use_agent,
environ_update=dict(TZ="UTC"), check_rc=True
)
@ -485,6 +507,8 @@ def get_cert_dict(data):
result = data.to_dict()
result.pop('nonce')
result['signature_algorithm'] = data.signature_type
return result
@ -503,6 +527,7 @@ def main():
default='partial_idempotence',
choices=['never', 'fail', 'partial_idempotence', 'full_idempotence', 'always']
),
signature_algorithm=dict(type='str', choices=['ssh-rsa', 'rsa-sha2-256', 'rsa-sha2-512']),
signing_key=dict(type='path'),
serial_number=dict(type='int'),
state=dict(type='str', default='present', choices=['absent', 'present']),

View File

@ -20,6 +20,81 @@
valid_from: always
valid_to: forever
- block:
- name: Generate cert with updated signature algorithm
openssh_cert:
type: user
path: "{{ certificate_path }}"
public_key: "{{ public_key }}"
signing_key: "{{ signing_key }}"
signature_algorithm: rsa-sha2-256
valid_from: always
valid_to: forever
register: updated_signature_algorithm
- name: Assert signature algorithm update causes change
assert:
that:
- updated_signature_algorithm is changed
- name: Generate cert with updated signature algorithm (idempotent)
openssh_cert:
type: user
path: "{{ certificate_path }}"
public_key: "{{ public_key }}"
signing_key: "{{ signing_key }}"
signature_algorithm: rsa-sha2-256
valid_from: always
valid_to: forever
register: updated_signature_algorithm_idempotent
- name: Assert signature algorithm update is idempotent
assert:
that:
- updated_signature_algorithm_idempotent is not changed
- name: Generate cert with original signature algorithm
openssh_cert:
type: user
path: "{{ certificate_path }}"
public_key: "{{ public_key }}"
signing_key: "{{ signing_key }}"
signature_algorithm: ssh-rsa
valid_from: always
valid_to: forever
register: second_signature_algorithm
- name: Assert second signature algorithm update causes change
assert:
that:
- second_signature_algorithm is changed
- name: Omit signature algorithm
openssh_cert:
type: user
path: "{{ certificate_path }}"
public_key: "{{ public_key }}"
signing_key: "{{ signing_key }}"
valid_from: always
valid_to: forever
register: omitted_signature_algorithm
- name: Assert omitted_signature_algorithm does not cause change
assert:
that:
- omitted_signature_algorithm is not changed
- name: Revert to original certificate
openssh_cert:
type: user
path: "{{ certificate_path }}"
public_key: "{{ public_key }}"
signing_key: "{{ signing_key }}"
valid_from: always
valid_to: forever
regenerate: always
when: openssh_version is version("7.3", ">=")
- name: Generate cert with new signing key
openssh_cert:
type: user