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