blob: b307d060ddee814a82958e6c5a163eeb60b52b45 [file] [log] [blame]
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +02001package main
2
3import (
4 "context"
5 "crypto/ed25519"
6 "crypto/tls"
7 "crypto/x509"
8 "fmt"
9 "net"
10
11 "golang.org/x/net/proxy"
12 "google.golang.org/grpc"
13
14 "source.monogon.dev/metropolis/node"
15 "source.monogon.dev/metropolis/node/core/rpc"
16 "source.monogon.dev/metropolis/node/core/rpc/resolver"
17)
18
19// dialCluster dials the cluster control address. The owner certificate, and
20// proxy address parameters are optional and can be left nil, and empty,
21// respectively. At least one cluster endpoint must be provided. A missing
22// owner certificate will result in a connection that is authenticated with
23// ephemeral credentials, restricting the available API surface. proxyAddr
24// must point at a SOCKS5 endpoint.
25func dialCluster(ctx context.Context, opkey ed25519.PrivateKey, ocert *x509.Certificate, proxyAddr string, clusterEndpoints []string) (*grpc.ClientConn, error) {
26 var dialOpts []grpc.DialOption
27
28 if opkey == nil {
29 return nil, fmt.Errorf("an owner's private key must be provided")
30 }
31 if len(clusterEndpoints) == 0 {
32 return nil, fmt.Errorf("at least one cluster endpoint must be provided")
33 }
34
35 if proxyAddr != "" {
36 socksDialer, err := proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct)
37 if err != nil {
38 return nil, fmt.Errorf("failed to build a SOCKS dialer: %v", err)
39 }
40 grpcd := func(_ context.Context, addr string) (net.Conn, error) {
41 return socksDialer.Dial("tcp", addr)
42 }
43 dialOpts = append(dialOpts, grpc.WithContextDialer(grpcd))
44 }
45
46 if ocert == nil {
47 creds, err := rpc.NewEphemeralCredentials(opkey, nil)
48 if err != nil {
49 return nil, fmt.Errorf("while building ephemeral credentials: %v", err)
50 }
51 dialOpts = append(dialOpts, grpc.WithTransportCredentials(creds))
52 } else {
53 tlsc := tls.Certificate{
54 Certificate: [][]byte{ocert.Raw},
55 PrivateKey: opkey,
56 }
57 creds := rpc.NewAuthenticatedCredentials(tlsc, nil)
58 dialOpts = append(dialOpts, grpc.WithTransportCredentials(creds))
59 }
60
61 r := resolver.New(ctx)
62 for _, ep := range clusterEndpoints {
63 r.AddEndpoint(resolver.NodeByHostPort(ep, uint16(node.CuratorServicePort)))
64 }
65 dialOpts = append(dialOpts, grpc.WithResolvers(r))
66
67 c, err := grpc.Dial(resolver.MetropolisControlAddress, dialOpts...)
68 if err != nil {
69 return nil, fmt.Errorf("could not dial: %v", err)
70 }
71 return c, nil
72}