feat(storage): add insecure_skip_tls_verify option for S3
Adds an opt-in toggle to skip TLS certificate verification when connecting to the S3 endpoint, for self-hosted S3-compatible backends (e.g. rustfs, MinIO) that use self-signed certificates. Exposed in both the store/API protos and the storage settings UI, mirroring the existing use_path_style toggle. When enabled, the AWS client uses an HTTP transport with InsecureSkipVerify; default behavior is unchanged. This governs backend-initiated S3 calls (uploads, deletes, thumbnails, and image/document streaming). Video/audio playback redirects the browser to a presigned URL, so that path still requires the browser to trust the cert. Closes #6039
This commit is contained in:
parent
1e3ec38fa1
commit
20c19ef82d
10 changed files with 98 additions and 23 deletions
|
|
@ -2,10 +2,13 @@ package s3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/aws"
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
|
awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http"
|
||||||
"github.com/aws/aws-sdk-go-v2/config"
|
"github.com/aws/aws-sdk-go-v2/config"
|
||||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
|
|
@ -20,10 +23,20 @@ type Client struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(ctx context.Context, s3Config *storepb.StorageS3Config) (*Client, error) {
|
func NewClient(ctx context.Context, s3Config *storepb.StorageS3Config) (*Client, error) {
|
||||||
cfg, err := config.LoadDefaultConfig(ctx,
|
loadOptions := []func(*config.LoadOptions) error{
|
||||||
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(s3Config.AccessKeyId, s3Config.AccessKeySecret, "")),
|
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(s3Config.AccessKeyId, s3Config.AccessKeySecret, "")),
|
||||||
config.WithRegion(s3Config.Region),
|
config.WithRegion(s3Config.Region),
|
||||||
)
|
}
|
||||||
|
if s3Config.InsecureSkipTlsVerify {
|
||||||
|
// Skip TLS certificate verification for endpoints using self-signed certificates.
|
||||||
|
// This is opt-in and removes protection against man-in-the-middle attacks.
|
||||||
|
httpClient := awshttp.NewBuildableClient().WithTransportOptions(func(tr *http.Transport) {
|
||||||
|
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} // #nosec G402 -- opt-in for self-signed S3 endpoints
|
||||||
|
})
|
||||||
|
loadOptions = append(loadOptions, config.WithHTTPClient(httpClient))
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := config.LoadDefaultConfig(ctx, loadOptions...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to load s3 config")
|
return nil, errors.Wrap(err, "failed to load s3 config")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,10 @@ message InstanceSetting {
|
||||||
string region = 4;
|
string region = 4;
|
||||||
string bucket = 5;
|
string bucket = 5;
|
||||||
bool use_path_style = 6;
|
bool use_path_style = 6;
|
||||||
|
// insecure_skip_tls_verify disables TLS certificate verification when connecting
|
||||||
|
// to the S3 endpoint. Only enable this for trusted endpoints that use a self-signed
|
||||||
|
// certificate; it removes protection against man-in-the-middle attacks.
|
||||||
|
bool insecure_skip_tls_verify = 7;
|
||||||
}
|
}
|
||||||
// The S3 config.
|
// The S3 config.
|
||||||
S3Config s3_config = 4;
|
S3Config s3_config = 4;
|
||||||
|
|
|
||||||
|
|
@ -1541,8 +1541,12 @@ type InstanceSetting_StorageSetting_S3Config struct {
|
||||||
Region string `protobuf:"bytes,4,opt,name=region,proto3" json:"region,omitempty"`
|
Region string `protobuf:"bytes,4,opt,name=region,proto3" json:"region,omitempty"`
|
||||||
Bucket string `protobuf:"bytes,5,opt,name=bucket,proto3" json:"bucket,omitempty"`
|
Bucket string `protobuf:"bytes,5,opt,name=bucket,proto3" json:"bucket,omitempty"`
|
||||||
UsePathStyle bool `protobuf:"varint,6,opt,name=use_path_style,json=usePathStyle,proto3" json:"use_path_style,omitempty"`
|
UsePathStyle bool `protobuf:"varint,6,opt,name=use_path_style,json=usePathStyle,proto3" json:"use_path_style,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
// insecure_skip_tls_verify disables TLS certificate verification when connecting
|
||||||
sizeCache protoimpl.SizeCache
|
// to the S3 endpoint. Only enable this for trusted endpoints that use a self-signed
|
||||||
|
// certificate; it removes protection against man-in-the-middle attacks.
|
||||||
|
InsecureSkipTlsVerify bool `protobuf:"varint,7,opt,name=insecure_skip_tls_verify,json=insecureSkipTlsVerify,proto3" json:"insecure_skip_tls_verify,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *InstanceSetting_StorageSetting_S3Config) Reset() {
|
func (x *InstanceSetting_StorageSetting_S3Config) Reset() {
|
||||||
|
|
@ -1617,6 +1621,13 @@ func (x *InstanceSetting_StorageSetting_S3Config) GetUsePathStyle() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *InstanceSetting_StorageSetting_S3Config) GetInsecureSkipTlsVerify() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.InsecureSkipTlsVerify
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Email delivery configuration for notifications.
|
// Email delivery configuration for notifications.
|
||||||
type InstanceSetting_NotificationSetting_EmailSetting struct {
|
type InstanceSetting_NotificationSetting_EmailSetting struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
|
@ -1802,7 +1813,7 @@ const file_api_v1_instance_service_proto_rawDesc = "" +
|
||||||
"\x06commit\x18\b \x01(\tR\x06commit\x12\x1f\n" +
|
"\x06commit\x18\b \x01(\tR\x06commit\x12\x1f\n" +
|
||||||
"\vneeds_setup\x18\t \x01(\bR\n" +
|
"\vneeds_setup\x18\t \x01(\bR\n" +
|
||||||
"needsSetup\"\x1b\n" +
|
"needsSetup\"\x1b\n" +
|
||||||
"\x19GetInstanceProfileRequest\"\xcd\x1b\n" +
|
"\x19GetInstanceProfileRequest\"\x86\x1c\n" +
|
||||||
"\x0fInstanceSetting\x12\x17\n" +
|
"\x0fInstanceSetting\x12\x17\n" +
|
||||||
"\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12W\n" +
|
"\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12W\n" +
|
||||||
"\x0fgeneral_setting\x18\x02 \x01(\v2,.memos.api.v1.InstanceSetting.GeneralSettingH\x00R\x0egeneralSetting\x12W\n" +
|
"\x0fgeneral_setting\x18\x02 \x01(\v2,.memos.api.v1.InstanceSetting.GeneralSettingH\x00R\x0egeneralSetting\x12W\n" +
|
||||||
|
|
@ -1824,19 +1835,20 @@ const file_api_v1_instance_service_proto_rawDesc = "" +
|
||||||
"\rCustomProfile\x12\x14\n" +
|
"\rCustomProfile\x12\x14\n" +
|
||||||
"\x05title\x18\x01 \x01(\tR\x05title\x12 \n" +
|
"\x05title\x18\x01 \x01(\tR\x05title\x12 \n" +
|
||||||
"\vdescription\x18\x02 \x01(\tR\vdescription\x12\x19\n" +
|
"\vdescription\x18\x02 \x01(\tR\vdescription\x12\x19\n" +
|
||||||
"\blogo_url\x18\x03 \x01(\tR\alogoUrl\x1a\xc1\x04\n" +
|
"\blogo_url\x18\x03 \x01(\tR\alogoUrl\x1a\xfa\x04\n" +
|
||||||
"\x0eStorageSetting\x12[\n" +
|
"\x0eStorageSetting\x12[\n" +
|
||||||
"\fstorage_type\x18\x01 \x01(\x0e28.memos.api.v1.InstanceSetting.StorageSetting.StorageTypeR\vstorageType\x12+\n" +
|
"\fstorage_type\x18\x01 \x01(\x0e28.memos.api.v1.InstanceSetting.StorageSetting.StorageTypeR\vstorageType\x12+\n" +
|
||||||
"\x11filepath_template\x18\x02 \x01(\tR\x10filepathTemplate\x12/\n" +
|
"\x11filepath_template\x18\x02 \x01(\tR\x10filepathTemplate\x12/\n" +
|
||||||
"\x14upload_size_limit_mb\x18\x03 \x01(\x03R\x11uploadSizeLimitMb\x12R\n" +
|
"\x14upload_size_limit_mb\x18\x03 \x01(\x03R\x11uploadSizeLimitMb\x12R\n" +
|
||||||
"\ts3_config\x18\x04 \x01(\v25.memos.api.v1.InstanceSetting.StorageSetting.S3ConfigR\bs3Config\x1a\xd1\x01\n" +
|
"\ts3_config\x18\x04 \x01(\v25.memos.api.v1.InstanceSetting.StorageSetting.S3ConfigR\bs3Config\x1a\x8a\x02\n" +
|
||||||
"\bS3Config\x12\"\n" +
|
"\bS3Config\x12\"\n" +
|
||||||
"\raccess_key_id\x18\x01 \x01(\tR\vaccessKeyId\x12/\n" +
|
"\raccess_key_id\x18\x01 \x01(\tR\vaccessKeyId\x12/\n" +
|
||||||
"\x11access_key_secret\x18\x02 \x01(\tB\x03\xe0A\x04R\x0faccessKeySecret\x12\x1a\n" +
|
"\x11access_key_secret\x18\x02 \x01(\tB\x03\xe0A\x04R\x0faccessKeySecret\x12\x1a\n" +
|
||||||
"\bendpoint\x18\x03 \x01(\tR\bendpoint\x12\x16\n" +
|
"\bendpoint\x18\x03 \x01(\tR\bendpoint\x12\x16\n" +
|
||||||
"\x06region\x18\x04 \x01(\tR\x06region\x12\x16\n" +
|
"\x06region\x18\x04 \x01(\tR\x06region\x12\x16\n" +
|
||||||
"\x06bucket\x18\x05 \x01(\tR\x06bucket\x12$\n" +
|
"\x06bucket\x18\x05 \x01(\tR\x06bucket\x12$\n" +
|
||||||
"\x0euse_path_style\x18\x06 \x01(\bR\fusePathStyle\"L\n" +
|
"\x0euse_path_style\x18\x06 \x01(\bR\fusePathStyle\x127\n" +
|
||||||
|
"\x18insecure_skip_tls_verify\x18\a \x01(\bR\x15insecureSkipTlsVerify\"L\n" +
|
||||||
"\vStorageType\x12\x1c\n" +
|
"\vStorageType\x12\x1c\n" +
|
||||||
"\x18STORAGE_TYPE_UNSPECIFIED\x10\x00\x12\f\n" +
|
"\x18STORAGE_TYPE_UNSPECIFIED\x10\x00\x12\f\n" +
|
||||||
"\bDATABASE\x10\x01\x12\t\n" +
|
"\bDATABASE\x10\x01\x12\t\n" +
|
||||||
|
|
|
||||||
|
|
@ -3627,6 +3627,12 @@ components:
|
||||||
type: string
|
type: string
|
||||||
usePathStyle:
|
usePathStyle:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
insecureSkipTlsVerify:
|
||||||
|
type: boolean
|
||||||
|
description: |-
|
||||||
|
insecure_skip_tls_verify disables TLS certificate verification when connecting
|
||||||
|
to the S3 endpoint. Only enable this for trusted endpoints that use a self-signed
|
||||||
|
certificate; it removes protection against man-in-the-middle attacks.
|
||||||
description: |-
|
description: |-
|
||||||
S3 configuration for cloud storage backend.
|
S3 configuration for cloud storage backend.
|
||||||
Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/
|
Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/
|
||||||
|
|
|
||||||
|
|
@ -673,8 +673,12 @@ type StorageS3Config struct {
|
||||||
Region string `protobuf:"bytes,4,opt,name=region,proto3" json:"region,omitempty"`
|
Region string `protobuf:"bytes,4,opt,name=region,proto3" json:"region,omitempty"`
|
||||||
Bucket string `protobuf:"bytes,5,opt,name=bucket,proto3" json:"bucket,omitempty"`
|
Bucket string `protobuf:"bytes,5,opt,name=bucket,proto3" json:"bucket,omitempty"`
|
||||||
UsePathStyle bool `protobuf:"varint,6,opt,name=use_path_style,json=usePathStyle,proto3" json:"use_path_style,omitempty"`
|
UsePathStyle bool `protobuf:"varint,6,opt,name=use_path_style,json=usePathStyle,proto3" json:"use_path_style,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
// insecure_skip_tls_verify disables TLS certificate verification when connecting
|
||||||
sizeCache protoimpl.SizeCache
|
// to the S3 endpoint. Only enable this for trusted endpoints that use a self-signed
|
||||||
|
// certificate; it removes protection against man-in-the-middle attacks.
|
||||||
|
InsecureSkipTlsVerify bool `protobuf:"varint,7,opt,name=insecure_skip_tls_verify,json=insecureSkipTlsVerify,proto3" json:"insecure_skip_tls_verify,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *StorageS3Config) Reset() {
|
func (x *StorageS3Config) Reset() {
|
||||||
|
|
@ -749,6 +753,13 @@ func (x *StorageS3Config) GetUsePathStyle() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *StorageS3Config) GetInsecureSkipTlsVerify() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.InsecureSkipTlsVerify
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type InstanceMemoRelatedSetting struct {
|
type InstanceMemoRelatedSetting struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
// content_length_limit is the limit of content length. Unit is byte.
|
// content_length_limit is the limit of content length. Unit is byte.
|
||||||
|
|
@ -1338,14 +1349,15 @@ const file_store_instance_setting_proto_rawDesc = "" +
|
||||||
"\x18STORAGE_TYPE_UNSPECIFIED\x10\x00\x12\f\n" +
|
"\x18STORAGE_TYPE_UNSPECIFIED\x10\x00\x12\f\n" +
|
||||||
"\bDATABASE\x10\x01\x12\t\n" +
|
"\bDATABASE\x10\x01\x12\t\n" +
|
||||||
"\x05LOCAL\x10\x02\x12\x06\n" +
|
"\x05LOCAL\x10\x02\x12\x06\n" +
|
||||||
"\x02S3\x10\x03\"\xd3\x01\n" +
|
"\x02S3\x10\x03\"\x8c\x02\n" +
|
||||||
"\x0fStorageS3Config\x12\"\n" +
|
"\x0fStorageS3Config\x12\"\n" +
|
||||||
"\raccess_key_id\x18\x01 \x01(\tR\vaccessKeyId\x12*\n" +
|
"\raccess_key_id\x18\x01 \x01(\tR\vaccessKeyId\x12*\n" +
|
||||||
"\x11access_key_secret\x18\x02 \x01(\tR\x0faccessKeySecret\x12\x1a\n" +
|
"\x11access_key_secret\x18\x02 \x01(\tR\x0faccessKeySecret\x12\x1a\n" +
|
||||||
"\bendpoint\x18\x03 \x01(\tR\bendpoint\x12\x16\n" +
|
"\bendpoint\x18\x03 \x01(\tR\bendpoint\x12\x16\n" +
|
||||||
"\x06region\x18\x04 \x01(\tR\x06region\x12\x16\n" +
|
"\x06region\x18\x04 \x01(\tR\x06region\x12\x16\n" +
|
||||||
"\x06bucket\x18\x05 \x01(\tR\x06bucket\x12$\n" +
|
"\x06bucket\x18\x05 \x01(\tR\x06bucket\x12$\n" +
|
||||||
"\x0euse_path_style\x18\x06 \x01(\bR\fusePathStyle\"\xc5\x01\n" +
|
"\x0euse_path_style\x18\x06 \x01(\bR\fusePathStyle\x127\n" +
|
||||||
|
"\x18insecure_skip_tls_verify\x18\a \x01(\bR\x15insecureSkipTlsVerify\"\xc5\x01\n" +
|
||||||
"\x1aInstanceMemoRelatedSetting\x120\n" +
|
"\x1aInstanceMemoRelatedSetting\x120\n" +
|
||||||
"\x14content_length_limit\x18\x03 \x01(\x05R\x12contentLengthLimit\x127\n" +
|
"\x14content_length_limit\x18\x03 \x01(\x05R\x12contentLengthLimit\x127\n" +
|
||||||
"\x18enable_double_click_edit\x18\x04 \x01(\bR\x15enableDoubleClickEdit\x12\x1c\n" +
|
"\x18enable_double_click_edit\x18\x04 \x01(\bR\x15enableDoubleClickEdit\x12\x1c\n" +
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,10 @@ message StorageS3Config {
|
||||||
string region = 4;
|
string region = 4;
|
||||||
string bucket = 5;
|
string bucket = 5;
|
||||||
bool use_path_style = 6;
|
bool use_path_style = 6;
|
||||||
|
// insecure_skip_tls_verify disables TLS certificate verification when connecting
|
||||||
|
// to the S3 endpoint. Only enable this for trusted endpoints that use a self-signed
|
||||||
|
// certificate; it removes protection against man-in-the-middle attacks.
|
||||||
|
bool insecure_skip_tls_verify = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message InstanceMemoRelatedSetting {
|
message InstanceMemoRelatedSetting {
|
||||||
|
|
|
||||||
|
|
@ -444,10 +444,11 @@ func convertInstanceStorageSettingFromStore(settingpb *storepb.InstanceStorageSe
|
||||||
setting.S3Config = &v1pb.InstanceSetting_StorageSetting_S3Config{
|
setting.S3Config = &v1pb.InstanceSetting_StorageSetting_S3Config{
|
||||||
AccessKeyId: settingpb.S3Config.AccessKeyId,
|
AccessKeyId: settingpb.S3Config.AccessKeyId,
|
||||||
// AccessKeySecret is write-only: never returned in responses.
|
// AccessKeySecret is write-only: never returned in responses.
|
||||||
Endpoint: settingpb.S3Config.Endpoint,
|
Endpoint: settingpb.S3Config.Endpoint,
|
||||||
Region: settingpb.S3Config.Region,
|
Region: settingpb.S3Config.Region,
|
||||||
Bucket: settingpb.S3Config.Bucket,
|
Bucket: settingpb.S3Config.Bucket,
|
||||||
UsePathStyle: settingpb.S3Config.UsePathStyle,
|
UsePathStyle: settingpb.S3Config.UsePathStyle,
|
||||||
|
InsecureSkipTlsVerify: settingpb.S3Config.InsecureSkipTlsVerify,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return setting
|
return setting
|
||||||
|
|
@ -464,12 +465,13 @@ func convertInstanceStorageSettingToStore(setting *v1pb.InstanceSetting_StorageS
|
||||||
}
|
}
|
||||||
if setting.S3Config != nil {
|
if setting.S3Config != nil {
|
||||||
settingpb.S3Config = &storepb.StorageS3Config{
|
settingpb.S3Config = &storepb.StorageS3Config{
|
||||||
AccessKeyId: setting.S3Config.AccessKeyId,
|
AccessKeyId: setting.S3Config.AccessKeyId,
|
||||||
AccessKeySecret: setting.S3Config.AccessKeySecret,
|
AccessKeySecret: setting.S3Config.AccessKeySecret,
|
||||||
Endpoint: setting.S3Config.Endpoint,
|
Endpoint: setting.S3Config.Endpoint,
|
||||||
Region: setting.S3Config.Region,
|
Region: setting.S3Config.Region,
|
||||||
Bucket: setting.S3Config.Bucket,
|
Bucket: setting.S3Config.Bucket,
|
||||||
UsePathStyle: setting.S3Config.UsePathStyle,
|
UsePathStyle: setting.S3Config.UsePathStyle,
|
||||||
|
InsecureSkipTlsVerify: setting.S3Config.InsecureSkipTlsVerify,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return settingpb
|
return settingpb
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,7 @@ const StorageSection = () => {
|
||||||
region: existing?.region ?? "",
|
region: existing?.region ?? "",
|
||||||
bucket: existing?.bucket ?? "",
|
bucket: existing?.bucket ?? "",
|
||||||
usePathStyle: existing?.usePathStyle ?? false,
|
usePathStyle: existing?.usePathStyle ?? false,
|
||||||
|
insecureSkipTlsVerify: existing?.insecureSkipTlsVerify ?? false,
|
||||||
[field]: value,
|
[field]: value,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
@ -332,6 +333,16 @@ const StorageSection = () => {
|
||||||
onCheckedChange={(checked) => handleS3FieldChange("usePathStyle", checked)}
|
onCheckedChange={(checked) => handleS3FieldChange("usePathStyle", checked)}
|
||||||
/>
|
/>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
|
|
||||||
|
<SettingRow
|
||||||
|
label={t("setting.storage.insecure-skip-tls-verify")}
|
||||||
|
description={t("setting.storage.insecure-skip-tls-verify-description")}
|
||||||
|
>
|
||||||
|
<Switch
|
||||||
|
checked={instanceStorageSetting.s3Config?.insecureSkipTlsVerify ?? false}
|
||||||
|
onCheckedChange={(checked) => handleS3FieldChange("insecureSkipTlsVerify", checked)}
|
||||||
|
/>
|
||||||
|
</SettingRow>
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -680,6 +680,8 @@
|
||||||
"endpoint-description": "Service endpoint, such as an AWS S3, Cloudflare R2, MinIO, or other compatible URL.",
|
"endpoint-description": "Service endpoint, such as an AWS S3, Cloudflare R2, MinIO, or other compatible URL.",
|
||||||
"filepath-template": "Filepath template",
|
"filepath-template": "Filepath template",
|
||||||
"filepath-template-description": "Used by local and S3 storage. Supports {timestamp}, {uuid}, and {filename}. Default: assets/{timestamp}_{uuid}_{filename}.",
|
"filepath-template-description": "Used by local and S3 storage. Supports {timestamp}, {uuid}, and {filename}. Default: assets/{timestamp}_{uuid}_{filename}.",
|
||||||
|
"insecure-skip-tls-verify": "Skip TLS verification",
|
||||||
|
"insecure-skip-tls-verify-description": "Disable TLS certificate verification for the S3 endpoint. Enable only for trusted endpoints using a self-signed certificate; this removes protection against man-in-the-middle attacks.",
|
||||||
"label": "Storage",
|
"label": "Storage",
|
||||||
"local-description": "Store new attachments on the server file system. This is the default for new instances.",
|
"local-description": "Store new attachments on the server file system. This is the default for new instances.",
|
||||||
"local-note-backup": "Persist and back up the attachment directory, especially in Docker or container deployments.",
|
"local-note-backup": "Persist and back up the attachment directory, especially in Docker or container deployments.",
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue