community.crypto icon indicating copy to clipboard operation
community.crypto copied to clipboard

Add support for SSH certificates using ecdsa-sk or ed25519-sk public keys

Open jnohlgard opened this issue 1 year ago • 1 comments

SUMMARY

Fixes #796

ISSUE TYPE
  • Feature Pull Request
COMPONENT NAME

module_utils.openssh.certificate

ADDITIONAL INFORMATION

The openssh.certificate module can both sign using FIDO2 SSH keys and create signatures for FIDO2 SSH keys, but the overall task results in a failure status because it can not understand the certificate it has created. This PR adds two new types of public keys and certificates to the load function which match the ssh-keygen key types ecdsa-sk and ed25519-sk.

Steps to reproduce

This requires a hardware FIDO2 key, e.g. Yubikey.

Create FIDO2 keys using ssh-keygen -t ecdsa-sk -f /tmp/id_ecdsa_sk -N '' and ssh-keygen -t ecdsa-sk -f /tmp/id_ed25519_sk -N '', then use the example below to sign the ECDSA key using the Ed25519 key.

sign-key.yml
---
- name: Sign OpenSSH user key
  hosts: localhost
  gather_facts: false
  tasks:
    # Generate an OpenSSH user certificate that is valid for 1 hour from now and will be regenerated
    # if it is valid for less than 10 minutes from the time the module is being run
    - name: Certify OpenSSH user key using master key
      community.crypto.openssh_cert:
        type: user
        signing_key: /tmp/id_ed25519_sk
        public_key: /tmp/id_ecdsa_sk.pub
        path: /tmp/id_ecdsa_sk-cert.pub
        valid_from: -1m
        valid_to: +1h
        valid_at: +10m
        ignore_timestamps: true
        principals:
          - myname
        options:
          - clear
          - permit-pty
          - no-touch-required
$ ansible-playbook -c local -i /dev/null -v sign-key.yml

Without this PR:

No config file found; using defaults
[WARNING]: provided hosts list is empty, only localhost is available. Note that the
implicit localhost does not match 'all'

PLAY [Sign OpenSSH user key] **********************************************************

TASK [Certify OpenSSH user key using master key] **************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Unable to read new certificate: Invalid certificate format identifier: b'[email protected]'"}

PLAY RECAP ****************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

The full traceback is:

  File "/tmp/ansible_community.crypto.openssh_cert_payload_kpyxp1cv/ansible_community.crypto.openssh_cert_payload.zip/ansible_collections/community/crypto/plugins/modules/openssh_cert.py", line 469, in _generate
    self.data = OpensshCertificate.load(self.path)
                ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/tmp/ansible_community.crypto.openssh_cert_payload_kpyxp1cv/ansible_community.crypto.openssh_cert_payload.zip/ansible_collections/community/crypto/plugins/module_utils/openssh/certificate.py", line 482, in load
    raise ValueError("Invalid certificate format identifier: %s" % format_identifier)

With this PR applied:

No config file found; using defaults
[WARNING]: provided hosts list is empty, only localhost is available. Note that the
implicit localhost does not match 'all'

PLAY [Sign OpenSSH user key] **********************************************************

TASK [Certify OpenSSH user key using master key] **************************************
changed: [localhost] => {"changed": true, "filename": "/tmp/id_ecdsa_sk-cert.pub", "info": ["Type: [email protected] user certificate", "Public key: ECDSA-SK-CERT SHA256:5+FIpbU8zV3jqDqNLoC8XIS2vfeoGNxARJVVhucoR8c", "Signing CA: ED25519-SK SHA256:rQJvUPDwKZOst1h5MnDrum4bhEU6KnEi5HNb+4qYYm0 (using [email protected])", "Key ID: \"\"", "Serial: 0", "Valid: from 2024-10-29T11:33:01 to 2024-10-29T12:34:01", "Principals: myname", "Critical Options: (none)", "Extensions: no-touch-required permit-pty"], "type": "user"}

PLAY RECAP ****************************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

jnohlgard avatar Oct 29 '24 10:10 jnohlgard

@jnohlgard ping

felixfontein avatar Nov 11 '24 17:11 felixfontein