blob: a8f933c537414e2348b13a3338d8095dc63fddb4 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Mateusz Zalega18a67b02022-08-02 13:37:50 +02004package core
5
6import (
7 "context"
8 "crypto/ed25519"
9 "crypto/tls"
10 "crypto/x509"
11 "fmt"
12 "io"
13 "net"
14
15 "golang.org/x/net/proxy"
16 "google.golang.org/grpc"
17
18 "source.monogon.dev/metropolis/node"
19 "source.monogon.dev/metropolis/node/core/rpc"
20 "source.monogon.dev/metropolis/node/core/rpc/resolver"
21 "source.monogon.dev/metropolis/proto/api"
22)
23
Serge Bazanski925ec3d2024-02-05 14:38:20 +010024func DialOpts(ctx context.Context, c *ConnectOptions) ([]grpc.DialOption, error) {
25 var opts []grpc.DialOption
26 if c.ProxyServer != "" {
27 socksDialer, err := proxy.SOCKS5("tcp", c.ProxyServer, nil, proxy.Direct)
Mateusz Zalega18a67b02022-08-02 13:37:50 +020028 if err != nil {
Tim Windelschmidt58786032024-05-21 13:47:41 +020029 return nil, fmt.Errorf("failed to build a SOCKS dialer: %w", err)
Mateusz Zalega18a67b02022-08-02 13:37:50 +020030 }
31 grpcd := func(_ context.Context, addr string) (net.Conn, error) {
32 return socksDialer.Dial("tcp", addr)
33 }
Serge Bazanski925ec3d2024-02-05 14:38:20 +010034 opts = append(opts, grpc.WithContextDialer(grpcd))
Mateusz Zalega18a67b02022-08-02 13:37:50 +020035 }
36
37 var resolverOpts []resolver.ResolverOption
Serge Bazanski925ec3d2024-02-05 14:38:20 +010038 if c.ResolverLogger != nil {
39 resolverOpts = append(resolverOpts, resolver.WithLogger(c.ResolverLogger))
Mateusz Zalega18a67b02022-08-02 13:37:50 +020040 }
Serge Bazanski925ec3d2024-02-05 14:38:20 +010041
Mateusz Zalega18a67b02022-08-02 13:37:50 +020042 r := resolver.New(ctx, resolverOpts...)
43
Serge Bazanski925ec3d2024-02-05 14:38:20 +010044 if len(c.Endpoints) == 0 {
45 return nil, fmt.Errorf("no cluster endpoints specified")
46 }
47 for _, eps := range c.Endpoints {
Mateusz Zalega18a67b02022-08-02 13:37:50 +020048 ep := resolver.NodeByHostPort(eps, uint16(node.CuratorServicePort))
49 r.AddEndpoint(ep)
50 }
Serge Bazanski925ec3d2024-02-05 14:38:20 +010051 opts = append(opts, grpc.WithResolvers(r))
Mateusz Zalega18a67b02022-08-02 13:37:50 +020052
Serge Bazanski925ec3d2024-02-05 14:38:20 +010053 return opts, nil
Mateusz Zalega18a67b02022-08-02 13:37:50 +020054}
55
Tim Windelschmidt9bd9bd42025-02-14 17:08:52 +010056func NewNodeClient(ctx context.Context, opkey ed25519.PrivateKey, ocert, ca *x509.Certificate, proxyAddr, nodeId, nodeAddr string) (*grpc.ClientConn, error) {
Serge Bazanskib91938f2023-03-29 14:31:22 +020057 var dialOpts []grpc.DialOption
58
59 if opkey == nil {
60 return nil, fmt.Errorf("an owner's private key must be provided")
61 }
62 if proxyAddr != "" {
63 socksDialer, err := proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct)
64 if err != nil {
Tim Windelschmidt58786032024-05-21 13:47:41 +020065 return nil, fmt.Errorf("failed to build a SOCKS dialer: %w", err)
Serge Bazanskib91938f2023-03-29 14:31:22 +020066 }
67 grpcd := func(_ context.Context, addr string) (net.Conn, error) {
68 return socksDialer.Dial("tcp", addr)
69 }
70 dialOpts = append(dialOpts, grpc.WithContextDialer(grpcd))
71 }
72 tlsc := tls.Certificate{
73 Certificate: [][]byte{ocert.Raw},
74 PrivateKey: opkey,
75 }
76 creds := rpc.NewAuthenticatedCredentials(tlsc, rpc.WantRemoteCluster(ca), rpc.WantRemoteNode(nodeId))
77 dialOpts = append(dialOpts, grpc.WithTransportCredentials(creds))
78
Tim Windelschmidtec2906a2024-03-27 01:36:43 +010079 endpoint := net.JoinHostPort(nodeAddr, node.NodeManagementPort.PortString())
Tim Windelschmidt9bd9bd42025-02-14 17:08:52 +010080 return grpc.NewClient(endpoint, dialOpts...)
Serge Bazanskib91938f2023-03-29 14:31:22 +020081}
82
Mateusz Zalega18a67b02022-08-02 13:37:50 +020083// GetNodes retrieves node records, filtered by the supplied node filter
84// expression fexp.
85func GetNodes(ctx context.Context, mgmt api.ManagementClient, fexp string) ([]*api.Node, error) {
86 resN, err := mgmt.GetNodes(ctx, &api.GetNodesRequest{
87 Filter: fexp,
88 })
89 if err != nil {
90 return nil, err
91 }
92
93 var nodes []*api.Node
94 for {
95 node, err := resN.Recv()
96 if err == io.EOF {
97 break
98 }
99 if err != nil {
100 return nil, err
101 }
102 nodes = append(nodes, node)
103 }
104 return nodes, nil
105}