openssh_cert: add serial_number param (#54653)
* [openssh_cert] cleanup the returned certificate info - Drop the certificate path - it is already present in rc.filename. - Drop the leading whitespace for all lines. Signed-off-by: Jakob Ackermann <das7pad@outlook.com> * [openssh_cert] add support for a certificate serial number Signed-off-by: Jakob Ackermann <das7pad@outlook.com> * [openssh_cert] fix lint error Signed-off-by: Jakob Ackermann <das7pad@outlook.com> * [openssh_cert] drop explicit default value Signed-off-by: Jakob Ackermann <das7pad@outlook.com> * [openssh_cert] enforce the specified or missing serial number Signed-off-by: Jakob Ackermann <das7pad@outlook.com> * [openssh_cert] passing no explicit serial number ignores any present one Signed-off-by: Jakob Ackermann <das7pad@outlook.com>
This commit is contained in:
parent
fa47bed71c
commit
21c8650180
2 changed files with 138 additions and 5 deletions
|
@ -108,6 +108,14 @@ options:
|
|||
description:
|
||||
- Specify the key identity when signing a public key. The identifier that is logged by the server when the certificate is used for authentication.
|
||||
type: str
|
||||
serial_number:
|
||||
description:
|
||||
- "Specify the certificate serial number.
|
||||
The serial number is logged by the server when the certificate is used for authentication.
|
||||
The certificate serial number may be used in a KeyRevocationList.
|
||||
The serial number may be omitted for checks, but must be specified again for a new certificate.
|
||||
Note: The default value set by ssh-keygen is 0."
|
||||
type: int
|
||||
|
||||
extends_documentation_fragment: files
|
||||
'''
|
||||
|
@ -216,6 +224,7 @@ class Certificate(object):
|
|||
self.public_key = module.params['public_key']
|
||||
self.path = module.params['path']
|
||||
self.identifier = module.params['identifier']
|
||||
self.serial_number = module.params['serial_number']
|
||||
self.valid_from = module.params['valid_from']
|
||||
self.valid_to = module.params['valid_to']
|
||||
self.valid_at = module.params['valid_at']
|
||||
|
@ -290,6 +299,9 @@ class Certificate(object):
|
|||
else:
|
||||
args.extend(['-I', ""])
|
||||
|
||||
if self.serial_number is not None:
|
||||
args.extend(['-z', str(self.serial_number)])
|
||||
|
||||
if self.principals:
|
||||
args.extend(['-n', ','.join(self.principals)])
|
||||
|
||||
|
@ -377,6 +389,7 @@ class Certificate(object):
|
|||
if principals == ["(none)"]:
|
||||
principals = None
|
||||
cert_type = re.findall("( user | host )", proc[1])[0].strip()
|
||||
serial_number = re.search(r"Serial: (\d+)", proc[1]).group(1)
|
||||
validity = re.findall("(from (\\d{4}-\\d{2}-\\d{2}T\\d{2}(:\\d{2}){2}) to (\\d{4}-\\d{2}-\\d{2}T\\d{2}(:\\d{2}){2}))", proc[1])
|
||||
if validity:
|
||||
if validity[0][1]:
|
||||
|
@ -402,6 +415,11 @@ class Certificate(object):
|
|||
file_args = module.load_file_common_arguments(module.params)
|
||||
return not module.set_fs_attributes_if_different(file_args, False)
|
||||
|
||||
def _check_serial_number():
|
||||
if self.serial_number is None:
|
||||
return True
|
||||
return self.serial_number == int(serial_number)
|
||||
|
||||
def _check_type():
|
||||
return self.type == cert_type
|
||||
|
||||
|
@ -441,10 +459,10 @@ class Certificate(object):
|
|||
|
||||
return False
|
||||
|
||||
if not perms_required:
|
||||
return _check_type() and _check_principals() and _check_validity(module)
|
||||
if perms_required and not _check_perms(module):
|
||||
return False
|
||||
|
||||
return _check_perms(module) and _check_type() and _check_principals() and _check_validity(module)
|
||||
return _check_type() and _check_principals() and _check_validity(module) and _check_serial_number()
|
||||
|
||||
def dump(self):
|
||||
|
||||
|
@ -456,9 +474,12 @@ class Certificate(object):
|
|||
for word in arr:
|
||||
if word in keywords:
|
||||
concated.append(string)
|
||||
string = ""
|
||||
string += " " + word
|
||||
string = word
|
||||
else:
|
||||
string += " " + word
|
||||
concated.append(string)
|
||||
# drop the certificate path
|
||||
concated.pop(0)
|
||||
return concated
|
||||
|
||||
def format_cert_info():
|
||||
|
@ -512,6 +533,7 @@ def main():
|
|||
public_key=dict(type='path'),
|
||||
path=dict(type='path', required=True),
|
||||
identifier=dict(type='str'),
|
||||
serial_number=dict(type='int'),
|
||||
valid_from=dict(type='str'),
|
||||
valid_to=dict(type='str'),
|
||||
valid_at=dict(type='str'),
|
||||
|
|
|
@ -239,6 +239,117 @@
|
|||
- "clear"
|
||||
valid_from: "2001-01-21"
|
||||
valid_to: "2019-01-21"
|
||||
- name: Generate cert without serial
|
||||
openssh_cert:
|
||||
type: user
|
||||
signing_key: '{{ output_dir }}/id_key'
|
||||
public_key: '{{ output_dir }}/id_key.pub'
|
||||
path: '{{ output_dir }}/id_cert_no_serial'
|
||||
valid_from: always
|
||||
valid_to: forever
|
||||
register: rc_no_serial_number
|
||||
- name: check default serial
|
||||
assert:
|
||||
that:
|
||||
- "'Serial: 0' in rc_no_serial_number.info"
|
||||
msg: OpenSSH user certificate contains the default serial number.
|
||||
- name: Generate cert without serial (idempotent)
|
||||
openssh_cert:
|
||||
type: user
|
||||
signing_key: '{{ output_dir }}/id_key'
|
||||
public_key: '{{ output_dir }}/id_key.pub'
|
||||
path: '{{ output_dir }}/id_cert_no_serial'
|
||||
valid_from: always
|
||||
valid_to: forever
|
||||
register: rc_no_serial_number_idempotent
|
||||
- name: check idempotent
|
||||
assert:
|
||||
that:
|
||||
- rc_no_serial_number_idempotent is not changed
|
||||
msg: OpenSSH certificate generation without serial number is idempotent.
|
||||
- name: Generate cert with serial 42
|
||||
openssh_cert:
|
||||
type: user
|
||||
signing_key: '{{ output_dir }}/id_key'
|
||||
public_key: '{{ output_dir }}/id_key.pub'
|
||||
path: '{{ output_dir }}/id_cert_serial_42'
|
||||
valid_from: always
|
||||
valid_to: forever
|
||||
serial_number: 42
|
||||
register: rc_serial_number
|
||||
- name: check serial 42
|
||||
assert:
|
||||
that:
|
||||
- "'Serial: 42' in rc_serial_number.info"
|
||||
msg: OpenSSH user certificate contains the serial number from the params.
|
||||
- name: Generate cert with serial 42 (idempotent)
|
||||
openssh_cert:
|
||||
type: user
|
||||
signing_key: '{{ output_dir }}/id_key'
|
||||
public_key: '{{ output_dir }}/id_key.pub'
|
||||
path: '{{ output_dir }}/id_cert_serial_42'
|
||||
valid_from: always
|
||||
valid_to: forever
|
||||
serial_number: 42
|
||||
register: rc_serial_number_idempotent
|
||||
- name: check idempotent
|
||||
assert:
|
||||
that:
|
||||
- rc_serial_number_idempotent is not changed
|
||||
msg: OpenSSH certificate generation with serial number is idempotent.
|
||||
- name: Generate cert with changed serial number
|
||||
openssh_cert:
|
||||
type: user
|
||||
signing_key: '{{ output_dir }}/id_key'
|
||||
public_key: '{{ output_dir }}/id_key.pub'
|
||||
path: '{{ output_dir }}/id_cert_serial_42'
|
||||
valid_from: always
|
||||
valid_to: forever
|
||||
serial_number: 1337
|
||||
register: rc_serial_number_changed
|
||||
- name: check changed
|
||||
assert:
|
||||
that:
|
||||
- rc_serial_number_changed is changed
|
||||
msg: OpenSSH certificate regenerated upon serial number change.
|
||||
- name: Generate cert with removed serial number
|
||||
openssh_cert:
|
||||
type: user
|
||||
signing_key: '{{ output_dir }}/id_key'
|
||||
public_key: '{{ output_dir }}/id_key.pub'
|
||||
path: '{{ output_dir }}/id_cert_serial_42'
|
||||
valid_from: always
|
||||
valid_to: forever
|
||||
serial_number: 0
|
||||
register: rc_serial_number_removed
|
||||
- name: check changed
|
||||
assert:
|
||||
that:
|
||||
- rc_serial_number_removed is changed
|
||||
msg: OpenSSH certificate regenerated upon serial number removal.
|
||||
- name: Generate a new cert with serial number
|
||||
openssh_cert:
|
||||
type: user
|
||||
signing_key: '{{ output_dir }}/id_key'
|
||||
public_key: '{{ output_dir }}/id_key.pub'
|
||||
path: '{{ output_dir }}/id_cert_serial_ignore'
|
||||
valid_from: always
|
||||
valid_to: forever
|
||||
serial_number: 42
|
||||
- name: Generate cert again, omitting the parameter serial_number (idempotent)
|
||||
openssh_cert:
|
||||
type: user
|
||||
signing_key: '{{ output_dir }}/id_key'
|
||||
public_key: '{{ output_dir }}/id_key.pub'
|
||||
path: '{{ output_dir }}/id_cert_serial_ignore'
|
||||
valid_from: always
|
||||
valid_to: forever
|
||||
register: rc_serial_number_ignored
|
||||
- name: check idempotent
|
||||
assert:
|
||||
that:
|
||||
- rc_serial_number_ignored is not changed
|
||||
msg: OpenSSH certificate generation with omitted serial number is idempotent.
|
||||
- name: Remove certificate (check mode)
|
||||
openssh_cert:
|
||||
state: absent
|
||||
|
|
Loading…
Reference in a new issue