Skip to content

Re-implement Elasticsearch keystore encoding in Go for hot-reloadable secure settings#8987

Closed
pebrc wants to merge 5 commits intoelastic:mainfrom
pebrc:es-go-keystore
Closed

Re-implement Elasticsearch keystore encoding in Go for hot-reloadable secure settings#8987
pebrc wants to merge 5 commits intoelastic:mainfrom
pebrc:es-go-keystore

Conversation

@pebrc
Copy link
Collaborator

@pebrc pebrc commented Jan 6, 2026

Summary

Fixes #8922

This PR implements the Elasticsearch keystore file format in Go within the ECK operator, enabling hot-reloadable secure settings for Elasticsearch 9.3+ without requiring pod restarts or the complexity of a Kubernetes job-based approach.

Motivation

The approach to reload the Elasticsearch keystore without restarts as discussed in #8958 uses a fairly complex Kubernetes job mechanism. This PR takes a different approach: re-implementing the keystore encoding in Go so the operator can directly generate keystore files that Elasticsearch can read.

Changes

Core Keystore Implementation (pkg/controller/elasticsearch/keystore/)

  • version.go: Version selection logic mapping ES versions to keystore format versions (v4-v7) with appropriate cryptographic parameters
  • codec.go: Lucene codec header/footer implementation with CRC32 checksums
  • crypto.go: PBKDF2-HMAC-SHA512 key derivation and AES-256-GCM encryption with AAD
  • entries.go: Entry serialization using Java's modified UTF-8 format (DataOutputStream compatible)
  • keystore.go: Main API for creating keystore files
  • reconciler.go: ES-specific keystore reconciliation with settings hash caching

Integration

  • Version gating: Only enabled for ES 9.3+ (requires keystore digest in reload API response)
  • Opt-out annotation: eck.k8s.elastic.co/disable-reloadable-keystore to fallback to init container approach
  • Symlink mounting: Uses init container linking facility for auto-updating Secret mounts
  • Hot-reload: Calls _nodes/reload_secure_settings API and verifies convergence via keystore digest
  • Pre-created keystore: Always creates a keystore (with bootstrap seed) on ES 9.3+, so adding secure settings later only requires hot-reload, not pod restart

Key Technical Details

The Go implementation matches Elasticsearch's KeyStoreWrapper.java:

  • Lucene codec header with magic number 0x3FD76C17
  • Lucene codec footer with CRC32 checksum (IEEE polynomial)
  • PBKDF2 with 210,000 iterations (v6+) for key derivation
  • AES-256-GCM encryption with salt as AAD
  • Java DataOutputStream binary format (big endian, 2-byte UTF string length prefix)

Testing

  • Still missing comprehensive unit tests + e2e test
  • Verified keystore is readable by Elasticsearch 9.3+

Known Limitations / Future Work

  • Additional testing with various secure settings configurations + FIPS compliance enabled
  • Review of LLM generated code
  • e2e tests
  • Documentation updates
  • Consider squashing fixup commits before merge

Related

Note: created using Cursor + claude-4.5-opus

@prodsecmachine
Copy link
Collaborator

prodsecmachine commented Jan 6, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@botelastic botelastic bot added the triage label Jan 6, 2026
@github-actions
Copy link

github-actions bot commented Jan 7, 2026

🔍 Preview links for changed docs

@pebrc
Copy link
Collaborator Author

pebrc commented Jan 7, 2026

buildkite test this -f t=TestReloadableKeystore,p=gke,s=9.3.0-SNAPSHOT

pebrc added 2 commits January 7, 2026 22:18
This commit introduces a Go-based implementation of the Elasticsearch keystore
format, eliminating the need for Kubernetes Jobs to manage keystores. The new
approach enables hot-reloading of secure settings without pod restarts on
Elasticsearch 9.3+.

Key features:
- Pure Go implementation of Elasticsearch keystore format (V7)
- Lucene codec-compatible file structure with CRC32 checksums
- AES-GCM encryption with PBKDF2-HMAC-SHA512 key derivation
- Automatic hot-reload via ES reload_secure_settings API
- Settings hash optimization to avoid unnecessary keystore regeneration
- Status-based caching to skip redundant reload API calls
- Symlink-based Secret mounting for automatic updates

The keystore is automatically created on ES 9.3+ clusters (even without user
secure settings) to ensure the hot-reload infrastructure is always in place.
Users can opt-out via the eck.k8s.elastic.co/disable-reloadable-keystore
annotation.

Closes: elastic/cloud-on-k8s#XXXX
Adds comprehensive e2e tests for the Go-based reloadable keystore feature:

- TestReloadableKeystore: Tests the full lifecycle including:
  - Cluster startup without secure settings (keystore pre-created)
  - Adding secure settings (hot-reload, no pod restart)
  - Updating secure settings (hot-reload, no pod restart)
  - Verifies keystore annotations (settings hash, digest)

- TestReloadableKeystoreDisabled: Tests the opt-out mechanism via
  the eck.k8s.elastic.co/disable-reloadable-keystore annotation,
  verifying fallback to init container-based keystore creation.
@pebrc pebrc added the >enhancement Enhancement of existing functionality label Jan 8, 2026
@botelastic botelastic bot removed the triage label Jan 8, 2026
@pebrc
Copy link
Collaborator Author

pebrc commented Jan 16, 2026

Closing this in favour of a future implementation based on the file-settings infrastructure in Elasticsearch.

@pebrc pebrc closed this Jan 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

>enhancement Enhancement of existing functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reload the keystore for Elasticsearch 9.3 without node restarts

2 participants