Browse Source
Added tls for http clients (#5766)
Added tls for http clients (#5766)
* Added global http client * Added Do func for global http client * Changed the code to use the global http client * Fix http client in volume uploader * Fixed pkg name * Fixed http util funcs * Fixed http client for bench_filer_upload * Fixed http client for stress_filer_upload * Fixed http client for filer_server_handlers_proxy * Fixed http client for command_fs_merge_volumes * Fixed http client for command_fs_merge_volumes and command_volume_fsck * Fixed http client for s3api_server * Added init global client for main funcs * Rename global_client to client * Changed: - fixed NewHttpClient; - added CheckIsHttpsClientEnabled func - updated security.toml in scaffold * Reduce the visibility of some functions in the util/http/client pkg * Added the loadSecurityConfig function * Use util.LoadSecurityConfiguration() in NewHttpClient funcpull/4462/merge
vadimartynov
5 months ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
66 changed files with 646 additions and 198 deletions
-
3unmaintained/change_superblock/change_superblock.go
-
2unmaintained/diff_volume_servers/diff_volume_servers.go
-
3unmaintained/fix_dat/fix_dat.go
-
3unmaintained/load_test/load_test_meta_tail/load_test_meta_tail.go
-
2unmaintained/remove_duplicate_fids/remove_duplicate_fids.go
-
14unmaintained/repeated_vacuum/repeated_vacuum.go
-
6unmaintained/s3/presigned_put/presigned_put.go
-
2unmaintained/see_dat/see_dat.go
-
3unmaintained/see_idx/see_idx.go
-
2unmaintained/see_log_entry/see_log_entry.go
-
2unmaintained/see_meta/see_meta.go
-
2unmaintained/stream_read_volume/stream_read_volume.go
-
16unmaintained/stress_filer_upload/bench_filer_upload/bench_filer_upload.go
-
15unmaintained/stress_filer_upload/stress_filer_upload_actual/stress_filer_upload.go
-
2unmaintained/volume_tailer/volume_tailer.go
-
5weed/command/benchmark.go
-
9weed/command/download.go
-
21weed/command/filer_copy.go
-
8weed/command/scaffold/security.toml
-
5weed/command/update.go
-
5weed/filer/filechunk_manifest.go
-
8weed/filer/filer_notify_append.go
-
4weed/filer/reader_cache.go
-
5weed/filer/stream.go
-
6weed/mount/weedfs_write.go
-
3weed/mq/broker/broker_topic_partition_read_write.go
-
8weed/mq/broker/broker_write.go
-
2weed/mq/client/cmd/weed_pub_kv/publisher_kv.go
-
2weed/mq/client/cmd/weed_pub_record/publisher_record.go
-
2weed/mq/client/cmd/weed_sub_kv/subscriber_kv.go
-
2weed/mq/client/cmd/weed_sub_record/subscriber_record.go
-
5weed/operation/chunked_file.go
-
16weed/operation/needle_parse_test.go
-
24weed/operation/submit.go
-
84weed/operation/upload_content.go
-
4weed/replication/repl_util/replication_util.go
-
11weed/replication/sink/filersink/fetch_write.go
-
5weed/replication/source/filer_source.go
-
4weed/s3api/s3api_acl_helper.go
-
4weed/s3api/s3api_bucket_handlers.go
-
4weed/s3api/s3api_object_handlers.go
-
9weed/s3api/s3api_object_handlers_copy.go
-
11weed/s3api/s3api_server.go
-
7weed/server/common.go
-
17weed/server/filer_server_handlers_proxy.go
-
3weed/server/filer_server_handlers_write.go
-
8weed/server/filer_server_handlers_write_autochunk.go
-
8weed/server/filer_server_handlers_write_cipher.go
-
8weed/server/filer_server_handlers_write_upload.go
-
3weed/server/master_server.go
-
7weed/server/master_server_handlers_admin.go
-
11weed/server/volume_grpc_remote.go
-
11weed/server/volume_server_handlers_read.go
-
7weed/server/webdav_server.go
-
21weed/shell/command_fs_merge_volumes.go
-
3weed/shell/command_s3_clean_uploads.go
-
5weed/shell/command_volume_fsck.go
-
10weed/topology/store_replicate.go
-
201weed/util/http/client/http_client.go
-
16weed/util/http/client/http_client_interface.go
-
14weed/util/http/client/http_client_name.go
-
23weed/util/http/client/http_client_name_string.go
-
18weed/util/http/client/http_client_opt.go
-
27weed/util/http/http_global_client_init.go
-
55weed/util/http/http_global_client_util.go
-
2weed/weed.go
@ -0,0 +1,201 @@ |
|||||
|
package client |
||||
|
|
||||
|
import ( |
||||
|
"crypto/tls" |
||||
|
"crypto/x509" |
||||
|
"fmt" |
||||
|
util "github.com/seaweedfs/seaweedfs/weed/util" |
||||
|
"github.com/spf13/viper" |
||||
|
"io" |
||||
|
"net/http" |
||||
|
"net/url" |
||||
|
"os" |
||||
|
"strings" |
||||
|
"sync" |
||||
|
) |
||||
|
|
||||
|
var ( |
||||
|
loadSecurityConfigOnce sync.Once |
||||
|
) |
||||
|
|
||||
|
type HTTPClient struct { |
||||
|
Client *http.Client |
||||
|
Transport *http.Transport |
||||
|
expectHttpsScheme bool |
||||
|
} |
||||
|
|
||||
|
func (httpClient *HTTPClient) Do(req *http.Request) (*http.Response, error) { |
||||
|
req.URL.Scheme = httpClient.GetHttpScheme() |
||||
|
return httpClient.Client.Do(req) |
||||
|
} |
||||
|
|
||||
|
func (httpClient *HTTPClient) Get(url string) (resp *http.Response, err error) { |
||||
|
url, err = httpClient.NormalizeHttpScheme(url) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return httpClient.Client.Get(url) |
||||
|
} |
||||
|
|
||||
|
func (httpClient *HTTPClient) Post(url, contentType string, body io.Reader) (resp *http.Response, err error) { |
||||
|
url, err = httpClient.NormalizeHttpScheme(url) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return httpClient.Client.Post(url, contentType, body) |
||||
|
} |
||||
|
|
||||
|
func (httpClient *HTTPClient) PostForm(url string, data url.Values) (resp *http.Response, err error) { |
||||
|
url, err = httpClient.NormalizeHttpScheme(url) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return httpClient.Client.PostForm(url, data) |
||||
|
} |
||||
|
|
||||
|
func (httpClient *HTTPClient) Head(url string) (resp *http.Response, err error) { |
||||
|
url, err = httpClient.NormalizeHttpScheme(url) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return httpClient.Client.Head(url) |
||||
|
} |
||||
|
func (httpClient *HTTPClient) CloseIdleConnections() { |
||||
|
httpClient.Client.CloseIdleConnections() |
||||
|
} |
||||
|
|
||||
|
func (httpClient *HTTPClient) GetClientTransport() *http.Transport { |
||||
|
return httpClient.Transport |
||||
|
} |
||||
|
|
||||
|
func (httpClient *HTTPClient) GetHttpScheme() string { |
||||
|
if httpClient.expectHttpsScheme { |
||||
|
return "https" |
||||
|
} |
||||
|
return "http" |
||||
|
} |
||||
|
|
||||
|
func (httpClient *HTTPClient) NormalizeHttpScheme(rawURL string) (string, error) { |
||||
|
expectedScheme := httpClient.GetHttpScheme() |
||||
|
|
||||
|
if !(strings.HasPrefix(rawURL, "http://") || strings.HasPrefix(rawURL, "https://")) { |
||||
|
return expectedScheme + "://" + rawURL, nil |
||||
|
} |
||||
|
|
||||
|
parsedURL, err := url.Parse(rawURL) |
||||
|
if err != nil { |
||||
|
return "", err |
||||
|
} |
||||
|
|
||||
|
if expectedScheme != parsedURL.Scheme { |
||||
|
parsedURL.Scheme = expectedScheme |
||||
|
} |
||||
|
return parsedURL.String(), nil |
||||
|
} |
||||
|
|
||||
|
func NewHttpClient(clientName ClientName, opts ...HttpClientOpt) (*HTTPClient, error) { |
||||
|
httpClient := HTTPClient{} |
||||
|
httpClient.expectHttpsScheme = checkIsHttpsClientEnabled(clientName) |
||||
|
var tlsConfig *tls.Config = nil |
||||
|
|
||||
|
if httpClient.expectHttpsScheme { |
||||
|
clientCertPair, err := getClientCertPair(clientName) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
clientCaCert, clientCaCertName, err := getClientCaCert(clientName) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
if clientCertPair != nil || len(clientCaCert) != 0 { |
||||
|
caCertPool, err := createHTTPClientCertPool(clientCaCert, clientCaCertName) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
tlsConfig = &tls.Config{ |
||||
|
Certificates: []tls.Certificate{}, |
||||
|
RootCAs: caCertPool, |
||||
|
InsecureSkipVerify: false, |
||||
|
} |
||||
|
|
||||
|
if clientCertPair != nil { |
||||
|
tlsConfig.Certificates = append(tlsConfig.Certificates, *clientCertPair) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
httpClient.Transport = &http.Transport{ |
||||
|
MaxIdleConns: 1024, |
||||
|
MaxIdleConnsPerHost: 1024, |
||||
|
TLSClientConfig: tlsConfig, |
||||
|
} |
||||
|
httpClient.Client = &http.Client{ |
||||
|
Transport: httpClient.Transport, |
||||
|
} |
||||
|
|
||||
|
for _, opt := range opts { |
||||
|
opt(&httpClient) |
||||
|
} |
||||
|
return &httpClient, nil |
||||
|
} |
||||
|
|
||||
|
func getStringOptionFromSecurityConfiguration(clientName ClientName, stringOptionName string) string { |
||||
|
util.LoadSecurityConfiguration() |
||||
|
return viper.GetString(fmt.Sprintf("https.%s.%s", clientName.LowerCaseString(), stringOptionName)) |
||||
|
} |
||||
|
|
||||
|
func getBoolOptionFromSecurityConfiguration(clientName ClientName, boolOptionName string) bool { |
||||
|
util.LoadSecurityConfiguration() |
||||
|
return viper.GetBool(fmt.Sprintf("https.%s.%s", clientName.LowerCaseString(), boolOptionName)) |
||||
|
} |
||||
|
|
||||
|
func checkIsHttpsClientEnabled(clientName ClientName) bool { |
||||
|
return getBoolOptionFromSecurityConfiguration(clientName, "enabled") |
||||
|
} |
||||
|
|
||||
|
func getFileContentFromSecurityConfiguration(clientName ClientName, fileType string) ([]byte, string, error) { |
||||
|
if fileName := getStringOptionFromSecurityConfiguration(clientName, fileType); fileName != "" { |
||||
|
fileContent, err := os.ReadFile(fileName) |
||||
|
if err != nil { |
||||
|
return nil, fileName, err |
||||
|
} |
||||
|
return fileContent, fileName, err |
||||
|
} |
||||
|
return nil, "", nil |
||||
|
} |
||||
|
|
||||
|
func getClientCertPair(clientName ClientName) (*tls.Certificate, error) { |
||||
|
certFileName := getStringOptionFromSecurityConfiguration(clientName, "cert") |
||||
|
keyFileName := getStringOptionFromSecurityConfiguration(clientName, "key") |
||||
|
if certFileName == "" && keyFileName == "" { |
||||
|
return nil, nil |
||||
|
} |
||||
|
if certFileName != "" && keyFileName != "" { |
||||
|
clientCert, err := tls.LoadX509KeyPair(certFileName, keyFileName) |
||||
|
if err != nil { |
||||
|
return nil, fmt.Errorf("error loading client certificate and key: %s", err) |
||||
|
} |
||||
|
return &clientCert, nil |
||||
|
} |
||||
|
return nil, fmt.Errorf("error loading key pair: key `%s` and certificate `%s`", keyFileName, certFileName) |
||||
|
} |
||||
|
|
||||
|
func getClientCaCert(clientName ClientName) ([]byte, string, error) { |
||||
|
return getFileContentFromSecurityConfiguration(clientName, "ca") |
||||
|
} |
||||
|
|
||||
|
func createHTTPClientCertPool(certContent []byte, fileName string) (*x509.CertPool, error) { |
||||
|
certPool := x509.NewCertPool() |
||||
|
if len(certContent) == 0 { |
||||
|
return certPool, nil |
||||
|
} |
||||
|
|
||||
|
ok := certPool.AppendCertsFromPEM(certContent) |
||||
|
if !ok { |
||||
|
return nil, fmt.Errorf("error processing certificate in %s", fileName) |
||||
|
} |
||||
|
return certPool, nil |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package client |
||||
|
|
||||
|
import ( |
||||
|
"io" |
||||
|
"net/http" |
||||
|
"net/url" |
||||
|
) |
||||
|
|
||||
|
type HTTPClientInterface interface { |
||||
|
Do(req *http.Request) (*http.Response, error) |
||||
|
Get(url string) (resp *http.Response, err error) |
||||
|
Post(url, contentType string, body io.Reader) (resp *http.Response, err error) |
||||
|
PostForm(url string, data url.Values) (resp *http.Response, err error) |
||||
|
Head(url string) (resp *http.Response, err error) |
||||
|
CloseIdleConnections() |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
package client |
||||
|
|
||||
|
import "strings" |
||||
|
|
||||
|
type ClientName int |
||||
|
|
||||
|
//go:generate stringer -type=ClientName -output=http_client_name_string.go
|
||||
|
const ( |
||||
|
Client ClientName = iota |
||||
|
) |
||||
|
|
||||
|
func (name *ClientName) LowerCaseString() string { |
||||
|
return strings.ToLower(name.String()) |
||||
|
} |
@ -0,0 +1,23 @@ |
|||||
|
// Code generated by "stringer -type=ClientName -output=http_client_name_string.go"; DO NOT EDIT.
|
||||
|
|
||||
|
package client |
||||
|
|
||||
|
import "strconv" |
||||
|
|
||||
|
func _() { |
||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
|
// Re-run the stringer command to generate them again.
|
||||
|
var x [1]struct{} |
||||
|
_ = x[Client-0] |
||||
|
} |
||||
|
|
||||
|
const _ClientName_name = "Client" |
||||
|
|
||||
|
var _ClientName_index = [...]uint8{0, 6} |
||||
|
|
||||
|
func (i ClientName) String() string { |
||||
|
if i < 0 || i >= ClientName(len(_ClientName_index)-1) { |
||||
|
return "ClientName(" + strconv.FormatInt(int64(i), 10) + ")" |
||||
|
} |
||||
|
return _ClientName_name[_ClientName_index[i]:_ClientName_index[i+1]] |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
package client |
||||
|
|
||||
|
import ( |
||||
|
"net" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
type HttpClientOpt = func(clientCfg *HTTPClient) |
||||
|
|
||||
|
func AddDialContext(httpClient *HTTPClient) { |
||||
|
dialContext := (&net.Dialer{ |
||||
|
Timeout: 10 * time.Second, |
||||
|
KeepAlive: 10 * time.Second, |
||||
|
}).DialContext |
||||
|
|
||||
|
httpClient.Transport.DialContext = dialContext |
||||
|
httpClient.Client.Transport = httpClient.Transport |
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
package http |
||||
|
|
||||
|
import ( |
||||
|
"github.com/seaweedfs/seaweedfs/weed/glog" |
||||
|
util_http_client "github.com/seaweedfs/seaweedfs/weed/util/http/client" |
||||
|
) |
||||
|
|
||||
|
var ( |
||||
|
globalHttpClient *util_http_client.HTTPClient |
||||
|
) |
||||
|
|
||||
|
func NewGlobalHttpClient(opt ...util_http_client.HttpClientOpt) (*util_http_client.HTTPClient, error) { |
||||
|
return util_http_client.NewHttpClient(util_http_client.Client, opt...) |
||||
|
} |
||||
|
|
||||
|
func GetGlobalHttpClient() *util_http_client.HTTPClient { |
||||
|
return globalHttpClient |
||||
|
} |
||||
|
|
||||
|
func InitGlobalHttpClient() { |
||||
|
var err error |
||||
|
|
||||
|
globalHttpClient, err = NewGlobalHttpClient() |
||||
|
if err != nil { |
||||
|
glog.Fatalf("error init global http client: %v", err) |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue