Skip to content

Fail-closed SSL verification, HttpClient reuse, and robustness fixes - #78

Open
henryx wants to merge 34 commits into
jopenlibs:masterfrom
henryx:master
Open

henryx wants to merge 34 commits into
jopenlibs:masterfrom
henryx:master

Conversation

@henryx

@henryx henryx commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR mainly fixes SslConfig states that silently disabled SSL certificate verification. It also
reuses one HTTP client per VaultConfig, makes environment-variable handling stricter, and fixes
several smaller bugs. This PR fixes CVE-2026-109408

Security: SSL verification now fails closed

Before this change, SslConfig.isVerify() could return false when the caller hadn't asked for
that. Rest then used a context that trusts every certificate, so chain and hostname checks were
both skipped with no error or log. This affected every release that has SslConfig (3.0.0 to
6.2.3).

Case Before After
SslConfig passed to VaultConfig without calling build() verification off, pinned cert ignored VaultConfig.build() builds it; verification on
SslConfig restored by Java deserialization no fields restored; verification off fields restored (defaultReadObject()); verification on unless verify(false) was set
VAULT_SSL_VERIFY=1, t, yes, on or a typo verification off verification on; unknown values log a warning

Details:

  • verify defaults to true, and an explicit verify(Boolean) takes priority even when build()
    hasn't run.
  • readObject keeps verification on for data saved by older releases or by never-built instances,
    since that data can carry a false nobody asked for.
  • EnvironmentLoader declares the serialVersionUID that 6.2.3 data carries, so configs
    serialized by 6.2.3 still deserialize.
  • The explicit opt-outs, verify(false) and VAULT_SSL_VERIFY=false, still work, with or without
    build().

Other changes

  • HTTP client reuse: each VaultConfig keeps one HttpClient and rebuilds it when the SSL
    settings or timeout change. getHttpClient() returns the client in use instead of null. A
    config with a custom client can be serialized; the client itself is not.
  • Environment variables: surrounding whitespace is removed and blank values count as unset.
    VAULT_TOKEN and .vault-token are not read when a token is set in code.
  • Timeouts: 0 means no timeout again, as before 6.0.0. Negative values are rejected by
    build(); negative values from environment variables are ignored with a warning.
  • Retries: only transient errors are retried (I/O errors and HTTP 5xx, 408, 412, 429). An
    interrupt stops retrying.
  • REST: query parameters are URL-encoded once instead of twice. Response bodies are returned
    byte-for-byte, so binary content is no longer corrupted.
  • Null handling: the response classes tolerate missing or null JSON fields.
    VaultConfig.address(null) falls back to VAULT_ADDR. Vault.create(config, null) throws
    IllegalArgumentException. When no KV engine version is set, version 2 is used.
  • Logging: warnings go to java.util.logging instead of stderr.
  • Deprecations: methods documented as deprecated now also carry @Deprecated, so callers get
    a compiler warning: Vault.leases(), mounts() and seal() (and their VaultImpl
    overrides), plus Auth.lookupWrap(), unwrap(), wrap() and rewrap(). Use the Sys
    equivalents, e.g. vault.sys().leases() and vault.sys().wrapping(). Auth.loginByAppID()
    gains a @deprecated Javadoc tag pointing to loginByAppRole().
  • Javadoc examples: Leases, Mounts and Wrapping examples use the sys() calls instead
    of the deprecated ones. The Wrapping examples also used new Vault(config) (an interface) and
    a misspelled lookupWarp(), and one referred to an undefined variable; all are fixed.
  • Build/CI: Maven plugin versions are pinned, Checkstyle runs and fails the build on
    violations, CI test reports are fixed, and the Travis config is removed. The CI matrix drops
    Vault 1.8.12–1.15.6 and adds 2.1.0–2.1.2, so it now tests Vault 1.16.3 to 2.1.2 and latest.

Breaking or behaviour changes

  • VAULT_SSL_VERIFY=0, no or off now keep verification on. Use false to disable it.
  • 4xx responses other than 408, 412 and 429 now fail immediately instead of being retried.
  • Logical.getEngineVersionForSecretPath() returns 2 instead of null when no version is set.
  • VaultConfig.nameSpace() no longer throws; an empty namespace is rejected by build().
    getSecretsEnginePathMap() returns an unmodifiable map.
  • A config serialized by 6.2.3 that was built with VAULT_SSL_VERIFY=false comes back with
    verification on.
  • Vault 1.8 to 1.15 are deprecated: they are no longer tested in CI. Code that only applies to
    them (the App ID integration test and its setup, and the pre-1.13 PKI status-code branches) is
    marked as deprecated.
  • Calls to the methods newly annotated @Deprecated now produce compiler warnings.

Known limitations (documented in the README)

  • A JKS keystore or truststore isn't serialized. A restored JKS-based SslConfig checks the server
    against the JVM's default CAs, so it still verifies but loses the pinned CA.
  • Certificate settings changed after build() only apply once build() is called again.

Tests

  • New SslConfigSerializationTests: round-trips of SslConfig and VaultConfig, data saved by
    never-built instances, and fixtures written by release 6.2.3
    (src/test/resources/serialized-6.2.3/).
  • New tests in VaultConfigTests (environment variables, VAULT_SSL_VERIFY parsing, timeouts)
    plus tests for retries, response bodies and null handling.

Thanks

Special thanks to Echo to help to find this problem

@henryx henryx added next-major-version Issue can be made in the next major version bug Something isn't working labels Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working next-major-version Issue can be made in the next major version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant