adiciona opção para ignorar cert

This commit is contained in:
2026-09-16 10:36:11 -03:00
parent 36ad6ab38b
commit 61b3274c6c
4 changed files with 103 additions and 25 deletions
+2
View File
@@ -29,6 +29,7 @@ Useful flags:
- `-key-id KEY`: uses explicit S3 access key credentials. - `-key-id KEY`: uses explicit S3 access key credentials.
- `-application-key SECRET`: uses explicit S3 secret or application key credentials. - `-application-key SECRET`: uses explicit S3 secret or application key credentials.
- `-endpoint-url https://s3.example.com`: uses a custom S3-compatible endpoint. - `-endpoint-url https://s3.example.com`: uses a custom S3-compatible endpoint.
- `-insecure-skip-verify`: skips TLS certificate verification for the S3 server. Use only with trusted endpoints.
- `-http-timeout 30s`: sets a timeout for AWS HTTP requests. - `-http-timeout 30s`: sets a timeout for AWS HTTP requests.
Flags override values loaded from `-config`. Flags override values loaded from `-config`.
@@ -45,6 +46,7 @@ Example config:
"key_id": "REPLACE_WITH_KEY_ID", "key_id": "REPLACE_WITH_KEY_ID",
"application_key": "REPLACE_WITH_APPLICATION_KEY", "application_key": "REPLACE_WITH_APPLICATION_KEY",
"endpoint_url": "https://s3.us-east-1.amazonaws.com", "endpoint_url": "https://s3.us-east-1.amazonaws.com",
"insecure_skip_verify": false,
"interval": "1m", "interval": "1m",
"http_timeout": "30s", "http_timeout": "30s",
"once": false, "once": false,
+44 -25
View File
@@ -3,6 +3,7 @@ package main
import ( import (
"context" "context"
"crypto/tls"
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
@@ -44,7 +45,7 @@ func run() error {
defer stop() defer stop()
awsOptions := []func(*config.LoadOptions) error{ awsOptions := []func(*config.LoadOptions) error{
config.WithHTTPClient(&http.Client{Timeout: cfg.httpTimeout}), config.WithHTTPClient(newHTTPClient(cfg)),
} }
if cfg.region != "" { if cfg.region != "" {
awsOptions = append(awsOptions, config.WithRegion(cfg.region)) awsOptions = append(awsOptions, config.WithRegion(cfg.region))
@@ -84,18 +85,19 @@ func run() error {
} }
type cliConfig struct { type cliConfig struct {
bucket string bucket string
prefix string prefix string
dir string dir string
hook string hook string
region string region string
keyID string keyID string
applicationKey string applicationKey string
endpointURL string endpointURL string
interval time.Duration insecureSkipVerify bool
httpTimeout time.Duration interval time.Duration
once bool httpTimeout time.Duration
prune bool once bool
prune bool
} }
func (c cliConfig) validate() error { func (c cliConfig) validate() error {
@@ -171,6 +173,7 @@ func newFlagSet(name string, cfg *cliConfig, configPath *string) *flag.FlagSet {
fs.StringVar(&cfg.keyID, "key-id", cfg.keyID, "S3 access key ID override") fs.StringVar(&cfg.keyID, "key-id", cfg.keyID, "S3 access key ID override")
fs.StringVar(&cfg.applicationKey, "application-key", cfg.applicationKey, "S3 secret/application key override") fs.StringVar(&cfg.applicationKey, "application-key", cfg.applicationKey, "S3 secret/application key override")
fs.StringVar(&cfg.endpointURL, "endpoint-url", cfg.endpointURL, "S3 endpoint URL override") fs.StringVar(&cfg.endpointURL, "endpoint-url", cfg.endpointURL, "S3 endpoint URL override")
fs.BoolVar(&cfg.insecureSkipVerify, "insecure-skip-verify", cfg.insecureSkipVerify, "skip verification of the S3 server TLS certificate")
fs.DurationVar(&cfg.httpTimeout, "http-timeout", cfg.httpTimeout, "timeout for AWS HTTP requests") fs.DurationVar(&cfg.httpTimeout, "http-timeout", cfg.httpTimeout, "timeout for AWS HTTP requests")
return fs return fs
} }
@@ -213,6 +216,9 @@ func loadConfigFile(path string, cfg *cliConfig) error {
if disk.EndpointURL != nil { if disk.EndpointURL != nil {
cfg.endpointURL = *disk.EndpointURL cfg.endpointURL = *disk.EndpointURL
} }
if disk.InsecureSkipVerify != nil {
cfg.insecureSkipVerify = *disk.InsecureSkipVerify
}
if disk.Interval != nil { if disk.Interval != nil {
duration, err := time.ParseDuration(*disk.Interval) duration, err := time.ParseDuration(*disk.Interval)
if err != nil { if err != nil {
@@ -238,16 +244,29 @@ func loadConfigFile(path string, cfg *cliConfig) error {
} }
type configFile struct { type configFile struct {
Bucket *string `json:"bucket"` Bucket *string `json:"bucket"`
Prefix *string `json:"prefix"` Prefix *string `json:"prefix"`
Dir *string `json:"dir"` Dir *string `json:"dir"`
Hook *string `json:"hook"` Hook *string `json:"hook"`
Region *string `json:"region"` Region *string `json:"region"`
KeyID *string `json:"key_id"` KeyID *string `json:"key_id"`
ApplicationKey *string `json:"application_key"` ApplicationKey *string `json:"application_key"`
EndpointURL *string `json:"endpoint_url"` EndpointURL *string `json:"endpoint_url"`
Interval *string `json:"interval"` InsecureSkipVerify *bool `json:"insecure_skip_verify"`
HTTPTimeout *string `json:"http_timeout"` Interval *string `json:"interval"`
Once *bool `json:"once"` HTTPTimeout *string `json:"http_timeout"`
Prune *bool `json:"prune"` Once *bool `json:"once"`
Prune *bool `json:"prune"`
}
func newHTTPClient(cfg cliConfig) *http.Client {
transport := http.DefaultTransport.(*http.Transport).Clone()
if cfg.insecureSkipVerify {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} //nolint:gosec // Explicit opt-in for self-signed S3 endpoints.
}
return &http.Client{
Transport: transport,
Timeout: cfg.httpTimeout,
}
} }
+56
View File
@@ -1,6 +1,9 @@
package main package main
import ( import (
"io"
"net/http"
"net/http/httptest"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
@@ -17,6 +20,7 @@ func TestParseConfigLoadsJSONConfig(t *testing.T) {
"key_id": "key-id-from-config", "key_id": "key-id-from-config",
"application_key": "application-key-from-config", "application_key": "application-key-from-config",
"endpoint_url": "https://s3.example.com", "endpoint_url": "https://s3.example.com",
"insecure_skip_verify": true,
"interval": "45s", "interval": "45s",
"http_timeout": "10s", "http_timeout": "10s",
"once": true, "once": true,
@@ -52,6 +56,9 @@ func TestParseConfigLoadsJSONConfig(t *testing.T) {
if cfg.endpointURL != "https://s3.example.com" { if cfg.endpointURL != "https://s3.example.com" {
t.Fatalf("endpointURL = %q, want https://s3.example.com", cfg.endpointURL) t.Fatalf("endpointURL = %q, want https://s3.example.com", cfg.endpointURL)
} }
if !cfg.insecureSkipVerify {
t.Fatal("insecureSkipVerify = false, want true")
}
if cfg.interval != 45*time.Second { if cfg.interval != 45*time.Second {
t.Fatalf("interval = %s, want 45s", cfg.interval) t.Fatalf("interval = %s, want 45s", cfg.interval)
} }
@@ -71,6 +78,7 @@ func TestParseConfigAllowsFlagOverrides(t *testing.T) {
"bucket": "config-bucket", "bucket": "config-bucket",
"dir": "/from/config", "dir": "/from/config",
"interval": "1m", "interval": "1m",
"insecure_skip_verify": true,
"prune": true "prune": true
}`) }`)
@@ -82,6 +90,7 @@ func TestParseConfigAllowsFlagOverrides(t *testing.T) {
"-key-id", "flag-key-id", "-key-id", "flag-key-id",
"-application-key", "flag-application-key", "-application-key", "flag-application-key",
"-endpoint-url", "https://s3.flag.example.com", "-endpoint-url", "https://s3.flag.example.com",
"-insecure-skip-verify=false",
"-prune=false", "-prune=false",
}) })
if err != nil { if err != nil {
@@ -106,11 +115,58 @@ func TestParseConfigAllowsFlagOverrides(t *testing.T) {
if cfg.endpointURL != "https://s3.flag.example.com" { if cfg.endpointURL != "https://s3.flag.example.com" {
t.Fatalf("endpointURL = %q, want https://s3.flag.example.com", cfg.endpointURL) t.Fatalf("endpointURL = %q, want https://s3.flag.example.com", cfg.endpointURL)
} }
if cfg.insecureSkipVerify {
t.Fatal("insecureSkipVerify = true, want false")
}
if cfg.prune { if cfg.prune {
t.Fatal("prune = true, want false") t.Fatal("prune = true, want false")
} }
} }
func TestHTTPClientCertificateVerification(t *testing.T) {
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, err := io.WriteString(w, "ok")
if err != nil {
t.Errorf("writing response: %v", err)
}
}))
defer server.Close()
tests := []struct {
name string
insecureSkipVerify bool
wantErr bool
}{
{name: "verify certificate", wantErr: true},
{name: "skip certificate verification", insecureSkipVerify: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := defaultConfig()
cfg.insecureSkipVerify = tt.insecureSkipVerify
client := newHTTPClient(cfg)
response, err := client.Get(server.URL)
if tt.wantErr {
if err == nil {
if closeErr := response.Body.Close(); closeErr != nil {
t.Errorf("closing response body: %v", closeErr)
}
t.Fatal("Get() error = nil, want certificate verification error")
}
return
}
if err != nil {
t.Fatalf("Get() error = %v", err)
}
if err := response.Body.Close(); err != nil {
t.Fatalf("closing response body: %v", err)
}
})
}
}
func TestConfigValidateRequiresCompleteStaticCredentials(t *testing.T) { func TestConfigValidateRequiresCompleteStaticCredentials(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
+1
View File
@@ -7,6 +7,7 @@
"key_id": "REPLACE_WITH_KEY_ID", "key_id": "REPLACE_WITH_KEY_ID",
"application_key": "REPLACE_WITH_APPLICATION_KEY", "application_key": "REPLACE_WITH_APPLICATION_KEY",
"endpoint_url": "https://s3.us-east-1.amazonaws.com", "endpoint_url": "https://s3.us-east-1.amazonaws.com",
"insecure_skip_verify": false,
"interval": "1m", "interval": "1m",
"http_timeout": "30s", "http_timeout": "30s",
"once": false, "once": false,