Skip to content

x/crypto/ssh: add support for hostkeys@openssh.com #37245

Description

@FiloSottile

The hostkeys@openssh.com extension lets a server notify a client of all its host keys to enable a smooth transition if they have UpdateHostKeys enabled. We should offer it server-side.

Activity

  1. added
    NeedsFixThe path to resolution is known, but the work has not been done.
    FeatureRequestIssues asking for a new feature that does not need a proposal.
    on Feb 16, 2020
  2. added this to the Unreleased milestone on Feb 16, 2020
  3. aphistic commented on Sep 13, 2023

    @aphistic

    I'm interested in taking this on, as it would be helpful for our implementations. I've reviewed the extension details in the openssh PROTOCOL file and it's relatively light on specifics, but seems to be enough to implement it.

    One question I have on the x/crypto/ssh side of things: You mention implementation on server-side, but if memory serves all the tests in that package use the client to test the server. Since there won't be a client-side implementation (maybe a config callback could be added that would be called when changes are found?), how would you expect the server side to be tested? Just sending raw messages from a client to the server and checking the expected responses?

  4. drakkan commented on Sep 14, 2023

    @drakkan
    Member

    Hi @aphistic,

    I'm a little short on time right now but this is also on my TODO.
    I think we should also add a client implementation consisting of callbacks to implement in the application using crypto/ssh.

  5. aphistic commented on Sep 14, 2023

    @aphistic

    Hey @drakkan!

    Is this something where you'd want to collaborate on it, or I should put it off? Do you have any thoughts on the implementation of it that I should implement?

    We currently have a fork of x/crypto we're maintaining with support for RFC8332 and RFC8308 (server-sig-algs), but also has a couple of modifications made to allow us to introspect things about the connection (host key alg or kex alg chosen for a connection) and allowed us to implement the host key extension in our service directly.

    Since the recent release of x/crypto now implements the features we originally created the fork for, we're looking to get back to using the upstream version. In order to use that, though, we'd need to have the host keys extension implemented. That's where the desire to work on this comes from.

  6. drakkan commented on Sep 14, 2023

    @drakkan
    Member

    Hi @aphistic,

    yes I would like to collobarate but I'm currently busy with other things, if this is urgent for you feel free to start. I haven't had time to think about the implementation details yet. I just added this issue to my TODO and I marked it as low priority

    Is your crypto fork public? I think it would be a good starting point.

    I recently became a crypto/ssh maintainer and I'm trying to help implement the common features we all need 😄

    I'm currently working on these issues

  7. achal1012 commented on Nov 28, 2023

    @achal1012

    Hello, I would like to contribute and help here if possible, I am also interested in this feature. @aphistic can you share the host key implementation that you have?

  8. drakkan commented on Jan 27, 2024

    @drakkan
    Member

    Hello,

    I propose the following API changes:

    // HostKeysProve sends a hostkeys-prove@openssh.com message to request the
    // server prove ownership of the private half of the key and validates the
    // received signatures.
    func (c *Client) HostKeysProve(publicKeys []PublicKey) error 
    
    // HostKeys00Callback is the function type used to receive the host keys sent by
    // servers using the hostkeys-00@openssh.com global message.
    type HostKeys00Callback func(publicKeys []PublicKey)
    
    type ClientConfig struct {
        ....
        // HostKeys00Callback is called after the authentication if the server sends
        // a hostkeys-00@openssh.com message. The client configuraton can supply
        // this callback to get notified about the received public keys.
        HostKeys00Callback HostKeys00Callback
    

    No server-side API changes are needed because the hostkeys-00@openssh.com request can always be sent after user-authentication has completed. We only need to allow to accept multiple host keys with the same key format in AddHostKey.

    We can also add something like FixedHostKeys to simplify verification for multiple host keys but I think this should be a separate commit

  9. gopherbot commented on Jan 27, 2024

    @gopherbot
    Contributor

    Change https://go.dev/cl/559055 mentions this issue: ssh: add support for hostkeys@openssh.com

  10. drakkan commented on Jan 27, 2024

    @drakkan
    Member

    Help with testing the above CL and thoughts on it are greatly appreciated. Thank you!

  11. drakkan commented on Feb 3, 2024

    @drakkan
    Member

    CL updated with new API

    // HostKeyUpdate represents a public key sent from servers using the
    // hostkeys-00@openssh.com global message.
    type HostKeyUpdate struct {
    	key PublicKey
    }
    
    // Fingerprint returns the key's fingerprint as unpadded base64 encoded sha256
    // hash.
    func (h *HostKeyUpdate) Fingerprint() string {
    	return FingerprintSHA256(h.key)
    }
    
    // KeyType returns the key type.
    func (h *HostKeyUpdate) KeyType() string {
    	return h.key.Type()
    }
    
    // Equal reports whether the public part of the host key matches the one
    // supplied.
    func (h *HostKeyUpdate) Equal(key PublicKey) bool {
    	return bytes.Equal(h.key.Marshal(), key.Marshal())
    }
    
    // PublicKey sends a hostkeys-prove@openssh.com message to request the server
    // prove ownership of the private half of the key and returns the public key if
    // the verification is successful.
    func (h *HostKeyUpdate) PublicKey(client *Client) (PublicKey, error) {
    	if err := client.hostKeysProve([]PublicKey{h.key}); err != nil {
    		return nil, err
    	}
    	return h.key, nil
    }
    
    
    // HostKeysUpdateCallback is the function type used to receive the host keys
    // sent by servers using the hostkeys-00@openssh.com global message.
    type HostKeysUpdateCallback func(keysUpdate []HostKeyUpdate)
    
    type ClientConfig struct {
        ....
        // HostKeysUpdateCallback is called after the authentication if the server
        // sends a hostkeys-00@openssh.com message. The client configuration can
        // supply this callback to get notified about the received public keys.
        HostKeysUpdateCallback HostKeysUpdateCallback
        ....
    

    The purpose of these changes is to allow clients to get public keys only after servers prove ownership.
    Clients can use Fingerprint, KeyType and Equal methods to check if they already know the public key.

    Also updated the check in AddHostKey to avoid adding the same host key multiple times. I think this was the purpose of the previous code. If we add the same host key multiple times OpenSSH returns an error like this

    client_input_hostkeys: received duplicated ssh-rsa host key
    
  12. FiloSottile commented on Feb 6, 2024

    @FiloSottile
    ContributorAuthor
    type HostKeysUpdateCallback func(keysUpdate []HostKeyUpdate)
    

    we should make this

    type HostKeysUpdateCallback func([]*HostKeyUpdate)
    
  13. FiloSottile commented on Feb 6, 2024

    @FiloSottile
    ContributorAuthor

    Also, (*HostKeyUpdate).PublicKey shouldn't take a Client, which instead should be saved in HostKeyUpdate.

  14. 8 remaining items

  15. moved this to Incoming in Proposalson Mar 6, 2024
  16. rsc commented on Mar 8, 2024

    @rsc
    Contributor

    This proposal has been added to the active column of the proposals project
    and will now be reviewed at the weekly proposal review meetings.
    — rsc for the proposal review group

  17. moved this from Incoming to Active in Proposalson Mar 8, 2024
  18. rsc commented on Mar 13, 2024

    @rsc
    Contributor

    Any comments on the API in #37245 (comment)?

    I assume the empty struct is not really empty but has unexported fields.

  19. drakkan commented on Mar 13, 2024

    @drakkan
    Member

    @rsc, I confirm that the struct has unexported fields used to immplement the public methods

    Here are the struct fields in the proposed CL

    type HostKeyUpdate struct {
    	key       PublicKey
    	mux       *mux
    	sessionID []byte
    }
    

    key is used to implement Fingerprint, KeyType and Equal methods. mux and sessionID are used to send the hostkeys-prove@openssh.com to the server to verify the public key before returning it.

    The public key is not exposed directly to prevent users from trusting it without sending the hostkeys-prove@openssh.com request.

  20. moved this from Active to Likely Accept in Proposalson Mar 27, 2024
  21. rsc commented on Mar 27, 2024

    @rsc
    Contributor

    Based on the discussion above, this proposal seems like a likely accept.
    — rsc for the proposal review group

    The proposal details are in #37245 (comment).

  22. moved this from Likely Accept to Accepted in Proposalson Apr 4, 2024
  23. rsc commented on Apr 4, 2024

    @rsc
    Contributor

    No change in consensus, so accepted. 🎉
    This issue now tracks the work of implementing the proposal.
    — rsc for the proposal review group

    The proposal details are in #37245 (comment).

  24. changed the title [-]proposal: x/crypto/ssh: add support for hostkeys@openssh.com[/-] [+]x/crypto/ssh: add support for hostkeys@openssh.com[/+] on Apr 4, 2024
  25. modified the milestones: Proposal, Backlog on Apr 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    FeatureRequestIssues asking for a new feature that does not need a proposal.NeedsFixThe path to resolution is known, but the work has not been done.ProposalProposal-AcceptedProposal-CryptoProposal related to crypto packages or other security issueshelp wanted

    Type

    No type

    Projects

    • Status
      Accepted

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions