Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backend tls #225

Merged
merged 15 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
480 changes: 319 additions & 161 deletions api/gateway/config/v1/gateway.pb.go

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions api/gateway/config/v1/gateway.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ message Gateway {
repeated string hosts = 3 [deprecated = true];
repeated Endpoint endpoints = 4;
repeated Middleware middlewares = 5;
map<string, TLS> tls_store = 6;
}

message TLS {
bool insecure = 1;
string cacert = 2;
string cert = 3;
string key = 4;
string server_name = 5;
}

message PriorityConfig {
Expand Down Expand Up @@ -47,6 +56,9 @@ message Backend {
string target = 1;
optional int64 weight = 2;
HealthCheck health_check = 3;
bool tls = 4;
string tls_config_name = 5;
map<string, string> metadata = 6;
}

enum Protocol {
Expand Down
7 changes: 6 additions & 1 deletion client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,16 @@ func (c *client) RoundTrip(req *http.Request) (resp *http.Response, err error) {

addr := n.Address()
reqOpt.Backends = append(reqOpt.Backends, addr)
backendNode := n.(*node)
req.URL.Host = addr
req.URL.Scheme = "http"
if backendNode.tls {
req.URL.Scheme = "https"
req.Host = addr
}
req.RequestURI = ""
startAt := time.Now()
resp, err = n.(*node).client.Do(req)
resp, err = backendNode.client.Do(req)
reqOpt.UpstreamResponseTime = append(reqOpt.UpstreamResponseTime, time.Since(startAt).Seconds())
if err != nil {
done(ctx, selector.DoneInfo{Err: err})
Expand Down
1 change: 0 additions & 1 deletion client/client_test.go

This file was deleted.

68 changes: 55 additions & 13 deletions client/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package client

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"strconv"
"strings"
Expand All @@ -15,8 +17,13 @@ import (
"github.com/go-kratos/kratos/v2/selector/p2c"
)

type BuildContext struct {
TLSConfigs map[string]*tls.Config
TLSClientStore *HTTPSClientStore
}

// Factory is returns service client.
type Factory func(*config.Endpoint) (Client, error)
type Factory func(*BuildContext, *config.Endpoint) (Client, error)

type Option func(*options)
type options struct {
Expand All @@ -29,6 +36,39 @@ func WithPickerBuilder(in selector.Builder) Option {
}
}

func EmptyBuildContext() *BuildContext {
return &BuildContext{}
}

func NewBuildContext(cfg *config.Gateway) *BuildContext {
tlsConfigs := make(map[string]*tls.Config, len(cfg.TlsStore))
for k, v := range cfg.TlsStore {
cfg := &tls.Config{
InsecureSkipVerify: v.Insecure,
ServerName: v.ServerName,
}
cert, err := tls.X509KeyPair([]byte(v.Cert), []byte(v.Key))
if err != nil {
LOG.Warnf("failed to load tls cert: %q: %v", k, err)
continue
}
cfg.Certificates = []tls.Certificate{cert}
if v.Cacert != "" {
roots := x509.NewCertPool()
if ok := roots.AppendCertsFromPEM([]byte(v.Cacert)); !ok {
LOG.Warnf("failed to load tls cacert: %q", k)
continue
}
cfg.RootCAs = roots
}
tlsConfigs[k] = cfg
}
return &BuildContext{
TLSConfigs: tlsConfigs,
TLSClientStore: NewHTTPSClientStore(tlsConfigs),
}
}

// NewFactory new a client factory.
func NewFactory(r registry.Discovery, opts ...Option) Factory {
o := &options{
Expand All @@ -37,14 +77,15 @@ func NewFactory(r registry.Discovery, opts ...Option) Factory {
for _, opt := range opts {
opt(o)
}
return func(endpoint *config.Endpoint) (Client, error) {
return func(builderCtx *BuildContext, endpoint *config.Endpoint) (Client, error) {
picker := o.pickerBuilder.Build()
ctx, cancel := context.WithCancel(context.Background())
applier := &nodeApplier{
cancel: cancel,
endpoint: endpoint,
registry: r,
picker: picker,
cancel: cancel,
endpoint: endpoint,
registry: r,
picker: picker,
buildContext: builderCtx,
}
if err := applier.apply(ctx); err != nil {
return nil, err
Expand All @@ -55,11 +96,12 @@ func NewFactory(r registry.Discovery, opts ...Option) Factory {
}

type nodeApplier struct {
canceled int64
cancel context.CancelFunc
endpoint *config.Endpoint
registry registry.Discovery
picker selector.Selector
canceled int64
buildContext *BuildContext
cancel context.CancelFunc
endpoint *config.Endpoint
registry registry.Discovery
picker selector.Selector
}

func (na *nodeApplier) apply(ctx context.Context) error {
Expand All @@ -72,7 +114,7 @@ func (na *nodeApplier) apply(ctx context.Context) error {
switch target.Scheme {
case "direct":
weighted := backend.Weight // weight is only valid for direct scheme
node := newNode(backend.Target, na.endpoint.Protocol, weighted, map[string]string{}, "", "")
node := newNode(na.buildContext, backend.Target, na.endpoint.Protocol, weighted, backend.Metadata, "", "", WithTLS(backend.Tls), WithTLSConfigName(backend.TlsConfigName))
nodes = append(nodes, node)
na.picker.Apply(nodes)
case "discovery":
Expand Down Expand Up @@ -116,7 +158,7 @@ func (na *nodeApplier) Callback(services []*registry.ServiceInstance) error {
log.Errorf("failed to parse endpoint: %v/%s: %v", ser.Endpoints, scheme, err)
continue
}
node := newNode(addr, na.endpoint.Protocol, nodeWeight(ser), ser.Metadata, ser.Version, ser.Name)
node := newNode(na.buildContext, addr, na.endpoint.Protocol, nodeWeight(ser), ser.Metadata, ser.Version, ser.Name, WithTLS(false))
nodes = append(nodes, node)
}
na.picker.Apply(nodes)
Expand Down
95 changes: 89 additions & 6 deletions client/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import (

var _ selector.Node = &node{}
var _globalClient = defaultClient()
var _globalH2Client = defaultH2Client()
var _globalH2CClient = defaultH2CClient()
var _globalHTTPSClient = createHTTPSClient(nil)
var _dialTimeout = 200 * time.Millisecond
var followRedirect = false

Expand Down Expand Up @@ -77,7 +78,7 @@ func defaultClient() *http.Client {
}
}

func defaultH2Client() *http.Client {
func defaultH2CClient() *http.Client {
return &http.Client{
CheckRedirect: defaultCheckRedirect,
Transport: &http2.Transport{
Expand All @@ -93,7 +94,78 @@ func defaultH2Client() *http.Client {
}
}

func newNode(addr string, protocol config.Protocol, weight *int64, md map[string]string, version string, name string) *node {
func createHTTPSClient(tlsConfig *tls.Config) *http.Client {
tr := &http.Transport{
TLSClientConfig: tlsConfig,
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: _dialTimeout,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 10000,
MaxIdleConnsPerHost: 1000,
MaxConnsPerHost: 1000,
DisableCompression: true,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
_ = http2.ConfigureTransport(tr)
return &http.Client{
CheckRedirect: defaultCheckRedirect,
Transport: tr,
}
}

type HTTPSClientStore struct {
clientConfigs map[string]*tls.Config
clients map[string]*http.Client
}

func NewHTTPSClientStore(clientConfigs map[string]*tls.Config) *HTTPSClientStore {
return &HTTPSClientStore{
clientConfigs: clientConfigs,
clients: make(map[string]*http.Client),
}
}

func (s *HTTPSClientStore) GetClient(name string) *http.Client {
if name == "" {
return _globalClient
}
client, ok := s.clients[name]
if ok {
return client
}
tlsConfig, ok := s.clientConfigs[name]
if !ok {
LOG.Warnf("tls config not found for %s, using default instead", name)
return _globalHTTPSClient
}
client = createHTTPSClient(tlsConfig)
s.clients[name] = client
return client
}

type NodeOptions struct {
TLS bool
TLSConfigName string
}
type NewNodeOption func(*NodeOptions)

func WithTLS(in bool) NewNodeOption {
return func(o *NodeOptions) {
o.TLS = in
}
}

func WithTLSConfigName(in string) NewNodeOption {
return func(o *NodeOptions) {
o.TLSConfigName = in
}
}

func newNode(ctx *BuildContext, addr string, protocol config.Protocol, weight *int64, md map[string]string, version string, name string, opts ...NewNodeOption) *node {
node := &node{
protocol: protocol,
address: addr,
Expand All @@ -102,10 +174,20 @@ func newNode(addr string, protocol config.Protocol, weight *int64, md map[string
version: version,
name: name,
}
node.client = _globalClient
if protocol == config.Protocol_GRPC {
node.client = _globalH2Client
} else {
node.client = _globalClient
node.client = _globalH2CClient
}
opt := &NodeOptions{}
for _, o := range opts {
o(opt)
}
if opt.TLS {
node.tls = true
node.client = _globalHTTPSClient
if opt.TLSConfigName != "" {
node.client = ctx.TLSClientStore.GetClient(opt.TLSConfigName)
}
}
return node
}
Expand All @@ -119,6 +201,7 @@ type node struct {

client *http.Client
protocol config.Protocol
tls bool
}

func (n *node) Scheme() string {
Expand Down
9 changes: 6 additions & 3 deletions cmd/gateway/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ func main() {
if err != nil {
log.Fatalf("failed to new proxy: %v", err)
}
circuitbreaker.Init(clientFactory)

ctx := context.Background()
var ctrlLoader *configLoader.CtrlConfigLoader
Expand All @@ -123,7 +122,9 @@ func main() {
log.Fatalf("failed to load config: %v", err)
}

if err := p.Update(bc); err != nil {
buildContext := client.NewBuildContext(bc)
circuitbreaker.Init(buildContext, clientFactory)
if err := p.Update(buildContext, bc); err != nil {
log.Fatalf("failed to update service config: %v", err)
}
reloader := func() error {
Expand All @@ -132,7 +133,9 @@ func main() {
log.Errorf("failed to load config: %v", err)
return err
}
if err := p.Update(bc); err != nil {
buildContext := client.NewBuildContext(bc)
circuitbreaker.SetBuildContext(buildContext)
if err := p.Update(buildContext, bc); err != nil {
log.Errorf("failed to update service config: %v", err)
return err
}
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
go.uber.org/atomic v1.11.0
go.uber.org/automaxprocs v1.4.0
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
golang.org/x/net v0.1.0
golang.org/x/net v0.21.0
google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd
google.golang.org/protobuf v1.28.0
sigs.k8s.io/yaml v1.3.0
Expand Down Expand Up @@ -62,8 +62,8 @@ require (
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.4.1 // indirect
go.opentelemetry.io/proto/otlp v0.12.0 // indirect
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.4.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/grpc v1.46.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -462,8 +462,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down Expand Up @@ -540,8 +540,8 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand All @@ -550,8 +550,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down
Loading
Loading