Files
teleport/lib/teleterm/teleterm.go
T

153 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2021 Gravitational, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package teleterm
import (
"context"
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"
"github.com/gravitational/trace"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"github.com/gravitational/teleport/lib/teleterm/apiserver"
"github.com/gravitational/teleport/lib/teleterm/clusters"
"github.com/gravitational/teleport/lib/teleterm/daemon"
)
// Serve starts daemon service
func Serve(ctx context.Context, cfg Config) error {
if err := cfg.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err)
}
grpcCredentials, err := createGRPCCredentials(cfg.Addr, cfg.CertsDir)
if err != nil {
return trace.Wrap(err)
}
storage, err := clusters.NewStorage(clusters.Config{
Dir: cfg.HomeDir,
InsecureSkipVerify: cfg.InsecureSkipVerify,
})
if err != nil {
return trace.Wrap(err)
}
daemonService, err := daemon.New(daemon.Config{
Storage: storage,
CreateTshdEventsClientCredsFunc: grpcCredentials.tshdEvents,
PrehogAddr: cfg.PrehogAddr,
})
if err != nil {
return trace.Wrap(err)
}
apiServer, err := apiserver.New(apiserver.Config{
HostAddr: cfg.Addr,
Daemon: daemonService,
TshdServerCreds: grpcCredentials.tshd,
ListeningC: cfg.ListeningC,
})
if err != nil {
return trace.Wrap(err)
}
serverAPIWait := make(chan error)
go func() {
err := apiServer.Serve()
serverAPIWait <- err
}()
// Wait for shutdown signals
go func() {
shutdownSignals := []os.Signal{os.Interrupt, syscall.SIGTERM}
c := make(chan os.Signal, len(shutdownSignals))
signal.Notify(c, shutdownSignals...)
select {
case <-ctx.Done():
log.Info("Context closed, stopping service.")
case sig := <-c:
log.Infof("Captured %s, stopping service.", sig)
}
daemonService.Stop()
apiServer.Stop()
}()
errAPI := <-serverAPIWait
if errAPI != nil {
return trace.Wrap(errAPI, "shutting down due to API Server error")
}
return nil
}
type grpcCredentials struct {
tshd grpc.ServerOption
tshdEvents daemon.CreateTshdEventsClientCredsFunc
}
func createGRPCCredentials(tshdServerAddress string, certsDir string) (*grpcCredentials, error) {
shouldUseMTLS := strings.HasPrefix(tshdServerAddress, "tcp://")
if !shouldUseMTLS {
return &grpcCredentials{
tshd: grpc.Creds(nil),
tshdEvents: func() (grpc.DialOption, error) {
return grpc.WithTransportCredentials(insecure.NewCredentials()), nil
},
}, nil
}
rendererCertPath := filepath.Join(certsDir, rendererCertFileName)
tshdCertPath := filepath.Join(certsDir, tshdCertFileName)
tshdKeyPair, err := generateAndSaveCert(tshdCertPath)
if err != nil {
return nil, trace.Wrap(err)
}
// rendererCertPath will be read on an incoming client connection so we can assume that at this
// point the renderer process has saved its public key under that path.
tshdCreds, err := createServerCredentials(tshdKeyPair, rendererCertPath)
if err != nil {
return nil, trace.Wrap(err)
}
// To create client creds, we need to read the server cert. However, at this point we'd need to
// wait for the Electron app to save the cert under rendererCertPath.
//
// Instead of waiting for it, we're going to capture the logic in a function that's going to be
// called after the Electron app calls UpdateTshdEventsServerAddress of the Terminal service.
// Since this calls the gRPC server hosted by tsh, we can assume that by this point the Electron
// app has saved the cert to disk – without the cert, it wouldn't be able to call the tsh server.
createTshdEventsClientCredsFunc := func() (grpc.DialOption, error) {
creds, err := createClientCredentials(tshdKeyPair, rendererCertPath)
return creds, trace.Wrap(err, "could not create tshd events client credentials")
}
return &grpcCredentials{
tshd: tshdCreds,
tshdEvents: createTshdEventsClientCredsFunc,
}, nil
}