diff --git a/mino/minogrpc/controller/actions.go b/mino/minogrpc/controller/actions.go index 8c3fb0fe5..d607790aa 100644 --- a/mino/minogrpc/controller/actions.go +++ b/mino/minogrpc/controller/actions.go @@ -108,15 +108,20 @@ func (a tokenAction) Execute(req node.Context) error { token := m.GenerateToken(exp) - chain := m.GetCertificateChain() + var certHash string + if m.ServeTLS() { + chain := m.GetCertificateChain() - digest, err := m.GetCertificateStore().Hash(chain) - if err != nil { - return xerrors.Errorf("couldn't hash certificate: %v", err) + digest, err := m.GetCertificateStore().Hash(chain) + if err != nil { + return xerrors.Errorf("couldn't hash certificate: %v", err) + } + + certHash = fmt.Sprintf(" --cert-hash %s", base64.StdEncoding.EncodeToString(digest)) } - fmt.Fprintf(req.Out, "--token %s --cert-hash %s\n", - token, base64.StdEncoding.EncodeToString(digest)) + fmt.Fprintf(req.Out, "--token %s%s\n", + token, certHash) return nil } diff --git a/mino/minogrpc/controller/actions_test.go b/mino/minogrpc/controller/actions_test.go index 4c7d49756..7d7e2cb96 100644 --- a/mino/minogrpc/controller/actions_test.go +++ b/mino/minogrpc/controller/actions_test.go @@ -252,6 +252,10 @@ type fakeJoinable struct { err error } +func (j fakeJoinable) ServeTLS() bool { + return true +} + func (j fakeJoinable) GetCertificateChain() certs.CertChain { cert, _ := j.certs.Load(fake.NewAddress(0)) diff --git a/mino/minogrpc/controller/mod.go b/mino/minogrpc/controller/mod.go index e6900f347..5952c4341 100644 --- a/mino/minogrpc/controller/mod.go +++ b/mino/minogrpc/controller/mod.go @@ -136,7 +136,7 @@ func (m miniController) SetCommands(builder node.Builder) { cli.StringFlag{ Name: "cert-hash", Usage: "certificate hash of the distant server", - Required: true, + Required: false, }, ) sub.SetAction(builder.MakeAction(joinAction{})) diff --git a/mino/minogrpc/mod.go b/mino/minogrpc/mod.go index feacbf446..c85b8388d 100644 --- a/mino/minogrpc/mod.go +++ b/mino/minogrpc/mod.go @@ -65,6 +65,9 @@ var listener = net.Listen type Joinable interface { mino.Mino + // ServeTLS returns true if this node is running with TLS for gRPC. + ServeTLS() bool + // GetCertificateChain returns the certificate chain of the instance. GetCertificateChain() certs.CertChain diff --git a/mino/minogrpc/server.go b/mino/minogrpc/server.go index 12904fced..91e41061c 100644 --- a/mino/minogrpc/server.go +++ b/mino/minogrpc/server.go @@ -434,6 +434,7 @@ type overlay struct { router router.Router connMgr session.ConnectionManager addrFactory mino.AddressFactory + serveTLS bool // secret and public are the key pair that has generated the server // certificate. @@ -443,7 +444,6 @@ type overlay struct { // Keep a text marshalled value for the overlay address so that it's not // calculated for each request. myAddrStr string - useTLS bool } func newOverlay(tmpl *minoTemplate) (*overlay, error) { @@ -509,6 +509,11 @@ func newOverlay(tmpl *minoTemplate) (*overlay, error) { return o, nil } +// ServeTLS returns true if the gRPC server uses TLS +func (o *overlay) ServeTLS() bool { + return o.serveTLS +} + // GetCertificateChain returns the certificate of the overlay with its private key // set. This function will panic if the overlay has the "noTLS" flag sets. func (o *overlay) GetCertificateChain() certs.CertChain { @@ -533,19 +538,41 @@ func (o *overlay) GetCertificateStore() certs.Storage { return o.certs } -// Join sends a join request to a distant node with token generated beforehands -// by the later. +// Join sends a join request to a distant node with a token generated by the +// remote node. +// The certHash is used to make sure that no man-in-the-middle intercepts the +// communication. +// If the certHash is empty, it supposes that a transparent proxy is handling +// the TLS connection and that we can trust the CAs in place. func (o *overlay) Join(addr *url.URL, token string, certHash []byte) error { - target := session.NewAddress(addr.Host + addr.Path) + host := addr.Host + if addr.Port() == "" { + switch addr.Scheme { + case "http": + host += ":80" + case "https": + host += ":443" + default: + return xerrors.Errorf("address doesn't contain a port and uses "+ + "an unknown scheme: %s", addr.Scheme) + } + } + target := session.NewAddress(host + addr.Path) - chain := o.GetCertificateChain() + chain := &ptypes.CertificateChain{ + Address: []byte(o.myAddrStr), + } - // Fetch the certificate of the node we want to join. The hash is used to - // ensure that we get the right certificate. - err := o.certs.Fetch(target, certHash) - if err != nil { - return xerrors.Errorf("couldn't fetch distant certificate: %v", err) + if o.serveTLS { + chain.Value = o.GetCertificateChain() + + // Fetch the certificate of the node we want to join. The hash is used to + // ensure that we get the right certificate. + err := o.certs.Fetch(target, certHash) + if err != nil { + return xerrors.Errorf("couldn't fetch distant certificate: %v", err) + } } conn, err := o.connMgr.Acquire(target) @@ -554,15 +581,11 @@ func (o *overlay) Join(addr *url.URL, token string, certHash []byte) error { } defer o.connMgr.Release(target) - client := ptypes.NewOverlayClient(conn) req := &ptypes.JoinRequest{ Token: token, - Chain: &ptypes.CertificateChain{ - Address: []byte(o.myAddrStr), - Value: chain, - }, + Chain: chain, } ctx, cancel := context.WithCancel(context.Background()) @@ -573,11 +596,13 @@ func (o *overlay) Join(addr *url.URL, token string, certHash []byte) error { return xerrors.Errorf("couldn't call join: %v", err) } - // Update the certificate store with the response from the node we just - // joined. That will allow the node to communicate with the network. - for _, raw := range resp.Peers { - from := o.addrFactory.FromText(raw.GetAddress()) - o.certs.Store(from, raw.GetValue()) + if o.serveTLS { + // Update the certificate store with the response from the node we just + // joined. That will allow the node to communicate with the network. + for _, raw := range resp.Peers { + from := o.addrFactory.FromText(raw.GetAddress()) + o.certs.Store(from, raw.GetValue()) + } } return nil @@ -662,6 +687,14 @@ func (mgr *connManager) Acquire(to mino.Address) (grpc.ClientConnInterface, erro } opts = append(opts, grpc.WithTransportCredentials(ta)) + } else { + // If the remote is accessible via port 443, we suppose it is TLS terminated and signed by a + // CA available on the system. + if strings.HasSuffix(addr, ":443") { + opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))) + } else { + opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) + } } conn, err = grpc.DialContext(