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