FEATURE (s3): Allow to skip TLS verification

This commit is contained in:
Rostislav Dugin
2025-12-11 19:50:59 +03:00
parent fda3bf9b98
commit df78e296b3
6 changed files with 79 additions and 4 deletions

View File

@@ -3,6 +3,7 @@ package s3_storage
import (
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"io"
@@ -40,6 +41,7 @@ type S3Storage struct {
S3Prefix string `json:"s3Prefix" gorm:"type:text;column:s3_prefix"`
S3UseVirtualHostedStyle bool `json:"s3UseVirtualHostedStyle" gorm:"default:false;column:s3_use_virtual_hosted_style"`
SkipTLSVerify bool `json:"skipTLSVerify" gorm:"default:false;column:skip_tls_verify"`
}
func (s *S3Storage) TableName() string {
@@ -331,6 +333,7 @@ func (s *S3Storage) Update(incoming *S3Storage) {
s.S3Region = incoming.S3Region
s.S3Endpoint = incoming.S3Endpoint
s.S3UseVirtualHostedStyle = incoming.S3UseVirtualHostedStyle
s.SkipTLSVerify = incoming.SkipTLSVerify
if incoming.S3AccessKey != "" {
s.S3AccessKey = incoming.S3AccessKey
@@ -442,6 +445,9 @@ func (s *S3Storage) getClientParams(
TLSHandshakeTimeout: s3TLSHandshakeTimeout,
ResponseHeaderTimeout: s3ResponseTimeout,
IdleConnTimeout: s3IdleConnTimeout,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: s.SkipTLSVerify,
},
}
return endpoint, useSSL, accessKey, secretKey, bucketLookup, transport, nil

View File

@@ -0,0 +1,11 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE s3_storages
ADD COLUMN skip_tls_verify BOOLEAN NOT NULL DEFAULT FALSE;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE s3_storages
DROP COLUMN skip_tls_verify;
-- +goose StatementEnd

View File

@@ -6,4 +6,5 @@ export interface S3Storage {
s3Endpoint?: string;
s3Prefix?: string;
s3UseVirtualHostedStyle?: boolean;
skipTLSVerify?: boolean;
}

View File

@@ -39,6 +39,7 @@ export function EditStorageComponent({
const [isTestingConnection, setIsTestingConnection] = useState(false);
const [isTestConnectionSuccess, setIsTestConnectionSuccess] = useState(false);
const [connectionError, setConnectionError] = useState<string | undefined>();
const save = async () => {
if (!storage) return;
@@ -60,6 +61,7 @@ export function EditStorageComponent({
if (!storage) return;
setIsTestingConnection(true);
setConnectionError(undefined);
try {
await storageApi.testStorageConnectionDirect(storage);
@@ -69,7 +71,9 @@ export function EditStorageComponent({
description: 'Storage connection tested successfully',
});
} catch (e) {
alert((e as Error).message);
const errorMessage = (e as Error).message;
setConnectionError(errorMessage);
alert(errorMessage);
}
setIsTestingConnection(false);
@@ -290,7 +294,9 @@ export function EditStorageComponent({
setUnsaved={() => {
setIsUnsaved(true);
setIsTestConnectionSuccess(false);
setConnectionError(undefined);
}}
connectionError={connectionError}
/>
)}

View File

@@ -1,6 +1,6 @@
import { DownOutlined, InfoCircleOutlined, UpOutlined } from '@ant-design/icons';
import { Checkbox, Input, Tooltip } from 'antd';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import type { Storage } from '../../../../../entity/storages';
@@ -8,13 +8,27 @@ interface Props {
storage: Storage;
setStorage: (storage: Storage) => void;
setUnsaved: () => void;
connectionError?: string;
}
export function EditS3StorageComponent({ storage, setStorage, setUnsaved }: Props) {
export function EditS3StorageComponent({
storage,
setStorage,
setUnsaved,
connectionError,
}: Props) {
const hasAdvancedValues =
!!storage?.s3Storage?.s3Prefix || !!storage?.s3Storage?.s3UseVirtualHostedStyle;
!!storage?.s3Storage?.s3Prefix ||
!!storage?.s3Storage?.s3UseVirtualHostedStyle ||
!!storage?.s3Storage?.skipTLSVerify;
const [showAdvanced, setShowAdvanced] = useState(hasAdvancedValues);
useEffect(() => {
if (connectionError?.includes('failed to verify certificate')) {
setShowAdvanced(true);
}
}, [connectionError]);
return (
<>
<div className="mb-2 flex items-center">
@@ -226,6 +240,36 @@ export function EditS3StorageComponent({ storage, setStorage, setUnsaved }: Prop
</Tooltip>
</div>
</div>
<div className="mb-1 flex w-full flex-col items-start sm:flex-row sm:items-center">
<div className="mb-1 min-w-[110px] sm:mb-0">Skip TLS verify</div>
<div className="flex items-center">
<Checkbox
checked={storage?.s3Storage?.skipTLSVerify || false}
onChange={(e) => {
if (!storage?.s3Storage) return;
setStorage({
...storage,
s3Storage: {
...storage.s3Storage,
skipTLSVerify: e.target.checked,
},
});
setUnsaved();
}}
>
Skip TLS
</Checkbox>
<Tooltip
className="cursor-pointer"
title="Skip TLS certificate verification. Enable this if your S3-compatible storage uses a self-signed certificate. Warning: this reduces security."
>
<InfoCircleOutlined className="ml-2" style={{ color: 'gray' }} />
</Tooltip>
</div>
</div>
</>
)}

View File

@@ -45,6 +45,13 @@ export function ShowS3StorageComponent({ storage }: Props) {
Enabled
</div>
)}
{storage?.s3Storage?.skipTLSVerify && (
<div className="mb-1 flex items-center">
<div className="min-w-[110px]">Skip TLS</div>
Enabled
</div>
)}
</>
);
}