Reject age encryption configuration until support is restored (#791)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Cory LaNou
2025-10-13 14:05:48 -05:00
committed by GitHub
parent f236f5099f
commit 6465f6b18b
2 changed files with 80 additions and 0 deletions

View File

@@ -567,6 +567,15 @@ func NewReplicaFromConfig(c *ReplicaConfig, db *litestream.DB) (_ *litestream.Re
return nil, fmt.Errorf("replica path cannot be a url, please use the 'url' field instead: %s", c.Path)
}
// Reject age encryption configuration as it's currently non-functional.
// Age encryption support was removed during the LTX storage layer refactor
// and has not been reimplemented. Accepting this config would silently
// write plaintext data to remote storage instead of encrypted data.
// See: https://github.com/benbjohnson/litestream/issues/790
if len(c.Age.Identities) > 0 || len(c.Age.Recipients) > 0 {
return nil, fmt.Errorf("age encryption is not currently supported, if you need encryption please revert back to Litestream v0.3.x")
}
// Build replica.
r := litestream.NewReplica(db)
if v := c.SyncInterval; v != nil {

View File

@@ -217,6 +217,77 @@ func TestNewGSReplicaFromConfig(t *testing.T) {
}
}
// TestNewReplicaFromConfig_AgeEncryption verifies that age encryption configuration is rejected.
// Age encryption is currently non-functional and would silently write plaintext data.
// See: https://github.com/benbjohnson/litestream/issues/790
func TestNewReplicaFromConfig_AgeEncryption(t *testing.T) {
t.Run("RejectIdentities", func(t *testing.T) {
config := &main.ReplicaConfig{
URL: "s3://foo/bar",
}
config.Age.Identities = []string{"AGE-SECRET-KEY-1EXAMPLE"}
_, err := main.NewReplicaFromConfig(config, nil)
if err == nil {
t.Fatal("expected error when age identities are configured")
}
if !strings.Contains(err.Error(), "age encryption is not currently supported") {
t.Errorf("expected age encryption error, got: %v", err)
}
if !strings.Contains(err.Error(), "revert back to Litestream v0.3.x") {
t.Errorf("expected error to reference v0.3.x, got: %v", err)
}
})
t.Run("RejectRecipients", func(t *testing.T) {
config := &main.ReplicaConfig{
URL: "s3://foo/bar",
}
config.Age.Recipients = []string{"age1example"}
_, err := main.NewReplicaFromConfig(config, nil)
if err == nil {
t.Fatal("expected error when age recipients are configured")
}
if !strings.Contains(err.Error(), "age encryption is not currently supported") {
t.Errorf("expected age encryption error, got: %v", err)
}
if !strings.Contains(err.Error(), "revert back to Litestream v0.3.x") {
t.Errorf("expected error to reference v0.3.x, got: %v", err)
}
})
t.Run("RejectBoth", func(t *testing.T) {
config := &main.ReplicaConfig{
URL: "s3://foo/bar",
}
config.Age.Identities = []string{"AGE-SECRET-KEY-1EXAMPLE"}
config.Age.Recipients = []string{"age1example"}
_, err := main.NewReplicaFromConfig(config, nil)
if err == nil {
t.Fatal("expected error when both age identities and recipients are configured")
}
if !strings.Contains(err.Error(), "age encryption is not currently supported") {
t.Errorf("expected age encryption error, got: %v", err)
}
if !strings.Contains(err.Error(), "revert back to Litestream v0.3.x") {
t.Errorf("expected error to reference v0.3.x, got: %v", err)
}
})
t.Run("AllowEmpty", func(t *testing.T) {
config := &main.ReplicaConfig{
URL: "s3://foo/bar",
}
_, err := main.NewReplicaFromConfig(config, nil)
if err != nil {
t.Fatalf("unexpected error when age configuration is not present: %v", err)
}
})
}
// TestConfig_Validate_SnapshotIntervals tests validation of snapshot intervals
func TestConfig_Validate_SnapshotIntervals(t *testing.T) {
t.Run("ValidInterval", func(t *testing.T) {