Skip to content

Support modern SSH key/signature algorithms (rsa-sha2, Ed25519) for agent connections #559

Description

@sombraSoft

Edit (correction): an earlier version of this issue claimed the current plugin fails to
connect to stock modern-OpenSSH agents. Hands-on testing disproves that for the latest release
rsa-sha2 already works end-to-end on 4.3.17.1. The body below is corrected and the scope is
narrowed accordingly. Details + evidence in the "Verification" section.

Summary

Historically the plugin's SSH agent launcher negotiated only ssh-rsa (RSA + SHA-1), which breaks
against OpenSSH 8.8+ (where ssh-rsa is disabled by default). That gap is already closed for
rsa-sha2 in current releases
— the bundled trilead-api negotiates rsa-sha2-256/512 for both
host-key verification and client public-key auth. The remaining, genuinely-missing capability is
Ed25519 (and modern key types) for the plugin's auto-generated client key, plus making the key
type configurable. This issue proposes adding that, and would value maintainer input on scope before
a PR.

What already works (verified, latest release)

rsa-sha2 requires no new code on a current plugin. The SSH client (trilead-api, currently
build-217-jenkins-371 on develop; build-217-jenkins-247 in release 4.3.17.1) registers
rsa-sha2-256/rsa-sha2-512 ahead of ssh-rsa and AuthenticationManager selects from that list,
so the existing RSA-2048 key authenticates with a SHA-2 signature.

The capability first shipped in 4.3.5 (trilead-api 1.0.13). Releases 4.3.0–4.3.4 (trilead-api
1.0.3) are ssh-rsa-only.

What's actually missing

  1. Ed25519 client keys. ssh/SshKeysHelper.generate() hardcodes JSch KeyPair.RSA, 2048 and an
    "ssh-rsa " public-key prefix. There is no way to provision an ssh-ed25519 (or ECDSA) auto-key.
    The bundled mwiede JSch can already generate these; trilead can authenticate Ed25519 (it bundles
    ED25519KeyAlgorithm, with eddsa present transitively via trilead-api).
  2. No key-type configurability. SshConfiguration only exposes customPrivateKeyCredentialsId;
    there's no selector for the generated key type.
  3. (To confirm) host-key guest-attribute naming. ComputeEngineComputerLauncher.verifyServerHostKey()
    looks up the GCE guest attribute hostkeys/<name> by serverHostKeyAlgorithm.toLowerCase(). With
    rsa-sha2-* negotiated, the lookup for that name won't match the ssh-rsa-named attribute. This
    path is currently fail-open (returns true on miss), so it does not break connections, but it
    silently skips host-key verification — worth tightening alongside this work.

Verification

Isolated reproduction of the plugin's SSH bring-up (connectToSsh()Connection.connect() then
authenticateWithPublicKey(user, key, "")), using the plugin's exact bundled trilead jars and an
RSA-2048 client key, against a real sshd:

Plugin (trilead) Server Host-key Client auth Result
4.3.0 / jenkins-16 (ssh-rsa only) stock Debian 12 (OpenSSH 9.2), no override ssh-ed25519 rejected (signs ssh-rsa) FAIL
4.3.0 / jenkins-16 Debian 12 + PubkeyAcceptedAlgorithms +ssh-rsa ssh-ed25519 ssh-rsa accepted PASS
4.3.17.1 / jenkins-247 (latest) stock Debian 12 (OpenSSH 9.2), no override ssh-ed25519 rsa-sha2 PASS
4.3.17.1 / jenkins-247 stock Debian 13 (OpenSSH 10.0), no override ssh-ed25519 rsa-sha2 PASS
4.3.17.1 / jenkins-247 ssh-rsa force-disabled both legs, RSA-only host key rsa-sha2-256 rsa-sha2 PASS

Takeaways: on the latest release, rsa-sha2 host-key + client-auth already work against stock Debian
12/13 with ssh-rsa disabled; the only legacy-ssh-rsa failures are on pre-4.3.5 plugins, on the
client-auth leg.

Proposed scope

Add Ed25519 (and optionally ECDSA) as a selectable type for the auto-generated client key, default
unchanged (RSA):

  • Thread a key-type param through GoogleKeyPair.generate() / SshKeysHelper.generate() (use JSch
    KeyPairEd25519; emit the correct ssh-ed25519 public-key prefix).
  • Add a key-type selector to SshConfiguration (config.jelly + help-*.html + doCheck), default
    RSA
    . Migration follows the preemptibleprovisioningType precedent (readResolve() +
    JCasC-compat setter); old serialized configs keep producing RSA.
  • Preserve the SECURITY-2045 invariant (private key stays hudson.util.Secret).
  • Tighten verifyServerHostKey so rsa-sha2-* maps to the ssh-rsa guest-attribute name (stop the
    silent fail-open for RSA host keys).

A separate, optional hardening: a regression IT that provisions a stock Ubuntu 22.04 / Debian 12 agent
(no sshd_config overrides) and asserts the handshake succeeds — to lock in the rsa-sha2 behavior
that already works.

Backward compatibility

Default behavior unchanged: RSA auto-key, existing negotiation. The new key-type field is opt-in and
defaults to RSA, with serialized-config migration.

Testing plan

  • Unit: extend SshKeysHelper/GoogleKeyPairTest (keep the SECURITY-2045 plaintext-never-persisted
    assertion); update InstanceConfigurationTest#testConfigRoundtrip and ConfigAsCodeTest round-trip
    • fixture YAML for the new field.
  • Integration: a *IT.java provisioning a real GCE VM that authenticates with an Ed25519 auto-key;
    plus the stock-image rsa-sha2 regression IT above.

Open questions for maintainers

  1. Is "Ed25519 client key + key-type selector" the scope you'd want, with RSA staying the default?
  2. Worth adding the stock-image rsa-sha2 regression IT even though no production code changes for it?
  3. Should verifyServerHostKey's rsa-sha2-*ssh-rsa naming fix land in this issue or separately?
  4. Given Do not bundle eddsa library #465's history, comfortable relying on eddsa via trilead-api for Ed25519?

Happy to implement whichever scope you prefer — would appreciate a steer before I open the PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions