From b6974ffcfd1147b7c1b1b2bc67c836172abda083 Mon Sep 17 00:00:00 2001 From: kolaente Date: Sun, 8 Feb 2026 16:03:19 +0100 Subject: [PATCH] feat: add UNSIGNED-PAYLOAD config option for S3-compatible stores (#2205) Adds `files.s3.disablesigning` config option that sends `UNSIGNED-PAYLOAD` instead of computing SHA256 hashes for S3 request signing which fixes `XAmzContentSHA256Mismatch` errors with S3-compatible providers like Ceph RadosGW and Clever Cloud Cellar Resolves https://github.com/go-vikunja/vikunja/issues/2181 --- config-raw.json | 5 +++++ pkg/config/config.go | 16 +++++++++------- pkg/files/filehandling.go | 4 ++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/config-raw.json b/config-raw.json index 4df3590d4..8e1e7d686 100644 --- a/config-raw.json +++ b/config-raw.json @@ -528,6 +528,11 @@ "key": "usepathstyle", "default_value": "false", "comment": "Whether to use path-style addressing (e.g., https://s3.amazonaws.com/bucket/key) instead of virtual-hosted-style (e.g., https://bucket.s3.amazonaws.com/key). This is commonly needed for self-hosted S3-compatible services. Some providers only support one style or the other." + }, + { + "key": "disablesigning", + "default_value": "false", + "comment": "When enabled, the S3 client will send UNSIGNED-PAYLOAD instead of computing a SHA256 hash for request signing. Some S3-compatible providers (such as Ceph RadosGW, Clever Cloud Cellar) do not correctly verify payload signatures and return XAmzContentSHA256Mismatch errors. Enabling this option works around the issue. Only applies over HTTPS." } ] } diff --git a/pkg/config/config.go b/pkg/config/config.go index 64e931893..b8d56e9ad 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -162,13 +162,14 @@ const ( FilesType Key = `files.type` // S3 Configuration - FilesS3Endpoint Key = `files.s3.endpoint` - FilesS3Bucket Key = `files.s3.bucket` - FilesS3Region Key = `files.s3.region` - FilesS3AccessKey Key = `files.s3.accesskey` - FilesS3SecretKey Key = `files.s3.secretkey` - FilesS3UsePathStyle Key = `files.s3.usepathstyle` - FilesS3TempDir Key = `files.s3.tempdir` + FilesS3Endpoint Key = `files.s3.endpoint` + FilesS3Bucket Key = `files.s3.bucket` + FilesS3Region Key = `files.s3.region` + FilesS3AccessKey Key = `files.s3.accesskey` + FilesS3SecretKey Key = `files.s3.secretkey` + FilesS3UsePathStyle Key = `files.s3.usepathstyle` + FilesS3DisableSigning Key = `files.s3.disablesigning` + FilesS3TempDir Key = `files.s3.tempdir` MigrationTodoistEnable Key = `migration.todoist.enable` MigrationTodoistClientID Key = `migration.todoist.clientid` @@ -446,6 +447,7 @@ func InitDefaultConfig() { FilesS3AccessKey.setDefault("") FilesS3SecretKey.setDefault("") FilesS3UsePathStyle.setDefault(false) + FilesS3DisableSigning.setDefault(false) FilesS3TempDir.setDefault("") // Cors CorsEnable.setDefault(true) diff --git a/pkg/files/filehandling.go b/pkg/files/filehandling.go index e47858f6d..6cd81d568 100644 --- a/pkg/files/filehandling.go +++ b/pkg/files/filehandling.go @@ -33,6 +33,7 @@ import ( "code.vikunja.io/api/pkg/modules/keyvalue" "github.com/aws/aws-sdk-go-v2/aws" + v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4" awsconfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/s3" @@ -97,6 +98,9 @@ func initS3FileHandler() error { client := s3.NewFromConfig(cfg, func(o *s3.Options) { o.BaseEndpoint = aws.String(endpoint) o.UsePathStyle = config.FilesS3UsePathStyle.GetBool() + if config.FilesS3DisableSigning.GetBool() { + o.APIOptions = append(o.APIOptions, v4.SwapComputePayloadSHA256ForUnsignedPayloadMiddleware) + } }) // Initialize S3 filesystem using afero-s3