diff --git a/internal/app/apid/pkg/provider/tls.go b/internal/app/apid/pkg/provider/tls.go index ddd598be5e..9e88e127f9 100644 --- a/internal/app/apid/pkg/provider/tls.go +++ b/internal/app/apid/pkg/provider/tls.go @@ -15,7 +15,6 @@ import ( "github.com/talos-systems/talos/pkg/grpc/gen" "github.com/talos-systems/talos/pkg/machinery/config" - "github.com/talos-systems/talos/pkg/machinery/constants" ) // TLSConfig provides client & server TLS configs for apid. @@ -46,7 +45,6 @@ func NewTLSConfig(config config.Provider, endpoints []string) (*TLSConfig, error generator, err := gen.NewRemoteGenerator( config.Machine().Security().Token(), endpoints, - constants.TrustdPort, ) if err != nil { return nil, fmt.Errorf("failed to create remote certificate genertor: %w", err) diff --git a/pkg/grpc/gen/remote.go b/pkg/grpc/gen/remote.go index 673261674d..16fefdd55c 100644 --- a/pkg/grpc/gen/remote.go +++ b/pkg/grpc/gen/remote.go @@ -8,16 +8,24 @@ import ( "context" "fmt" "log" + "strings" "time" - "github.com/hashicorp/go-multierror" "github.com/talos-systems/crypto/x509" "google.golang.org/grpc" "github.com/talos-systems/talos/pkg/grpc/middleware/auth/basic" securityapi "github.com/talos-systems/talos/pkg/machinery/api/security" + "github.com/talos-systems/talos/pkg/machinery/client/resolver" + "github.com/talos-systems/talos/pkg/machinery/constants" ) +var trustdResolverScheme string + +func init() { + trustdResolverScheme = resolver.RegisterRoundRobinResolver(constants.TrustdPort) +} + // RemoteGenerator represents the OS identity generator. type RemoteGenerator struct { client securityapi.SecurityServiceClient @@ -26,46 +34,33 @@ type RemoteGenerator struct { } // NewRemoteGenerator initializes a RemoteGenerator with a preconfigured grpc.ClientConn. -func NewRemoteGenerator(token string, endpoints []string, port int) (g *RemoteGenerator, err error) { +func NewRemoteGenerator(token string, endpoints []string) (g *RemoteGenerator, err error) { if len(endpoints) == 0 { return nil, fmt.Errorf("at least one root of trust endpoint is required") } creds := basic.NewTokenCredentials(token) - // Loop through trustd endpoints and attempt to download PKI - var ( - conn *grpc.ClientConn - multiError *multierror.Error - ) - - for i := 0; i < len(endpoints); i++ { - conn, err = basic.NewConnection(endpoints[i], port, creds) - if err != nil { - multiError = multierror.Append(multiError, err) - // Unable to connect, bail and attempt to contact next endpoint - continue - } - - client := securityapi.NewSecurityServiceClient(conn) + conn, err := basic.NewConnection(fmt.Sprintf("%s:///%s", trustdResolverScheme, strings.Join(endpoints, ",")), creds) + if err != nil { + return nil, err + } - g := &RemoteGenerator{ - client: client, - conn: conn, - done: make(chan struct{}), - } + client := securityapi.NewSecurityServiceClient(conn) - return g, nil + g = &RemoteGenerator{ + client: client, + conn: conn, + done: make(chan struct{}), } - // We were unable to connect to any trustd endpoint - // Return error from last attempt. - return nil, multiError.ErrorOrNil() + return g, nil } // Certificate implements the securityapi.SecurityClient interface. func (g *RemoteGenerator) Certificate(in *securityapi.CertificateRequest) (resp *securityapi.CertificateResponse, err error) { - ctx := context.Background() + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() resp, err = g.client.Certificate(ctx, in) if err != nil { diff --git a/pkg/grpc/middleware/auth/basic/basic.go b/pkg/grpc/middleware/auth/basic/basic.go index b009d9896f..1a64c03c3f 100644 --- a/pkg/grpc/middleware/auth/basic/basic.go +++ b/pkg/grpc/middleware/auth/basic/basic.go @@ -6,9 +6,7 @@ package basic import ( "crypto/tls" - "fmt" - "github.com/talos-systems/net" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) @@ -22,7 +20,7 @@ type Credentials interface { // NewConnection initializes a grpc.ClientConn configured for basic // authentication. -func NewConnection(address string, port int, creds credentials.PerRPCCredentials) (conn *grpc.ClientConn, err error) { +func NewConnection(address string, creds credentials.PerRPCCredentials) (conn *grpc.ClientConn, err error) { grpcOpts := []grpc.DialOption{} grpcOpts = append( @@ -34,7 +32,7 @@ func NewConnection(address string, port int, creds credentials.PerRPCCredentials grpc.WithPerRPCCredentials(creds), ) - conn, err = grpc.Dial(fmt.Sprintf("%s:%d", net.FormatAddress(address), port), grpcOpts...) + conn, err = grpc.Dial(address, grpcOpts...) if err != nil { return } diff --git a/pkg/machinery/client/resolver.go b/pkg/machinery/client/resolver.go index 56e0718c15..0a3680fd60 100644 --- a/pkg/machinery/client/resolver.go +++ b/pkg/machinery/client/resolver.go @@ -5,85 +5,12 @@ package client import ( - "fmt" - "math/rand" - "strings" - - "github.com/talos-systems/net" - "google.golang.org/grpc/resolver" - + "github.com/talos-systems/talos/pkg/machinery/client/resolver" "github.com/talos-systems/talos/pkg/machinery/constants" ) -func init() { - resolver.Register(&talosListResolverBuilder{}) -} - -const talosListResolverScheme = "taloslist" - -type talosListResolverBuilder struct{} - -// Build implements resolver.Builder. -func (b *talosListResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) { - r := &talosListResolver{ - target: target, - cc: cc, - } - - if err := r.start(); err != nil { - return nil, err - } - - return r, nil -} - -// Build implements resolver.Builder. -func (b *talosListResolverBuilder) Scheme() string { - return talosListResolverScheme -} - -type talosListResolver struct { - target resolver.Target - cc resolver.ClientConn -} - -func (r *talosListResolver) start() error { - var addrs []resolver.Address // nolint: prealloc - - for _, a := range strings.Split(r.target.Endpoint, ",") { - addrs = append(addrs, resolver.Address{ - ServerName: a, - Addr: fmt.Sprintf("%s:%d", net.FormatAddress(a), constants.ApidPort), - }) - } - - // shuffle the list in case client does just one request - rand.Shuffle(len(addrs), func(i, j int) { - addrs[i], addrs[j] = addrs[j], addrs[i] - }) - - serviceConfigJSON := `{ - "loadBalancingConfig": [{ - "round_robin": {} - }] - }` +var talosListResolverScheme string - parsedServiceConfig := r.cc.ParseServiceConfig(serviceConfigJSON) - - if parsedServiceConfig.Err != nil { - return parsedServiceConfig.Err - } - - r.cc.UpdateState(resolver.State{ - Addresses: addrs, - ServiceConfig: parsedServiceConfig, - }) - - return nil +func init() { + talosListResolverScheme = resolver.RegisterRoundRobinResolver(constants.ApidPort) } - -// ResolveNow implements resolver.Resolver. -func (r *talosListResolver) ResolveNow(o resolver.ResolveNowOptions) {} - -// ResolveNow implements resolver.Resolver. -func (r *talosListResolver) Close() {} diff --git a/pkg/machinery/client/resolver/resolver.go b/pkg/machinery/client/resolver/resolver.go new file mode 100644 index 0000000000..091664cdec --- /dev/null +++ b/pkg/machinery/client/resolver/resolver.go @@ -0,0 +1,6 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Package resolver implements gRPC resolvers. +package resolver diff --git a/pkg/machinery/client/resolver/roundrobin.go b/pkg/machinery/client/resolver/roundrobin.go new file mode 100644 index 0000000000..2545eef243 --- /dev/null +++ b/pkg/machinery/client/resolver/roundrobin.go @@ -0,0 +1,100 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package resolver + +import ( + "fmt" + "math/rand" + "strings" + + "github.com/talos-systems/net" + "google.golang.org/grpc/resolver" +) + +// RegisterRoundRobinResolver registers round-robin gRPC resolver for specified port and returns scheme to use in grpc.Dial. +func RegisterRoundRobinResolver(port int) (scheme string) { + scheme = fmt.Sprintf(roundRobinResolverScheme, port) + + resolver.Register(&roundRobinResolverBuilder{ + port: port, + scheme: scheme, + }) + + return +} + +const roundRobinResolverScheme = "taloslist-%d" + +type roundRobinResolverBuilder struct { + port int + scheme string +} + +// Build implements resolver.Builder. +func (b *roundRobinResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) { + r := &roundRobinResolver{ + target: target, + cc: cc, + port: b.port, + } + + if err := r.start(); err != nil { + return nil, err + } + + return r, nil +} + +// Build implements resolver.Builder. +func (b *roundRobinResolverBuilder) Scheme() string { + return b.scheme +} + +type roundRobinResolver struct { + target resolver.Target + cc resolver.ClientConn + port int +} + +func (r *roundRobinResolver) start() error { + var addrs []resolver.Address // nolint: prealloc + + for _, a := range strings.Split(r.target.Endpoint, ",") { + addrs = append(addrs, resolver.Address{ + ServerName: a, + Addr: fmt.Sprintf("%s:%d", net.FormatAddress(a), r.port), + }) + } + + // shuffle the list in case client does just one request + rand.Shuffle(len(addrs), func(i, j int) { + addrs[i], addrs[j] = addrs[j], addrs[i] + }) + + serviceConfigJSON := `{ + "loadBalancingConfig": [{ + "round_robin": {} + }] + }` + + parsedServiceConfig := r.cc.ParseServiceConfig(serviceConfigJSON) + + if parsedServiceConfig.Err != nil { + return parsedServiceConfig.Err + } + + r.cc.UpdateState(resolver.State{ + Addresses: addrs, + ServiceConfig: parsedServiceConfig, + }) + + return nil +} + +// ResolveNow implements resolver.Resolver. +func (r *roundRobinResolver) ResolveNow(o resolver.ResolveNowOptions) {} + +// ResolveNow implements resolver.Resolver. +func (r *roundRobinResolver) Close() {}