Wouter Verlaek 72d9152a71 [image-builder] Enable TLS in workspace clusters
Fix cyclic import cycle

Update altnames

Update golden testdata, todo

Update grpc opts

Testing

Remove blocking dial

Only add TLS in ws cluster

Conditional TLS

Add comments
2022-12-20 02:45:29 -03:00

245 lines
8.0 KiB
Go

// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.
package cmd
import (
"context"
"net"
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"
"github.com/bombsimon/logrusr/v2"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/kubernetes"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/metrics"
common_grpc "github.com/gitpod-io/gitpod/common-go/grpc"
"github.com/gitpod-io/gitpod/common-go/log"
"github.com/gitpod-io/gitpod/common-go/pprof"
"github.com/gitpod-io/gitpod/content-service/pkg/layer"
imgbldr "github.com/gitpod-io/gitpod/image-builder/api"
"github.com/gitpod-io/gitpod/ws-manager/pkg/manager"
"github.com/gitpod-io/gitpod/ws-manager/pkg/proxy"
volumesnapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
volumesnapshotclientv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned"
)
// serveCmd represents the serve command
var runCmd = &cobra.Command{
Use: "run",
Short: "Starts the workspace monitor",
Run: func(cmd *cobra.Command, args []string) {
cfg := getConfig()
err := cfg.Manager.Validate()
if err != nil {
log.WithError(err).Fatal("invalid configuration")
}
log.Info("wsman configuration is valid")
common_grpc.SetupLogging()
ctrl.SetLogger(logrusr.New(log.Log))
opts := ctrl.Options{
Scheme: scheme,
Namespace: cfg.Manager.Namespace,
//Port: 9443,
HealthProbeBindAddress: ":0",
LeaderElection: false,
LeaderElectionID: "ws-manager-leader.gitpod.io",
}
if cfg.Prometheus.Addr != "" {
opts.MetricsBindAddress = cfg.Prometheus.Addr
err := metrics.Registry.Register(common_grpc.ClientMetrics())
if err != nil {
log.WithError(err).Error("Prometheus metrics incomplete")
}
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), opts)
if err != nil {
log.WithError(err).Fatal("unable to start manager")
}
kubeConfig, err := ctrl.GetConfig()
if err != nil {
log.WithError(err).Fatal("unable to getting Kubernetes client config")
}
clientset, err := kubernetes.NewForConfig(kubeConfig)
if err != nil {
log.WithError(err).Fatal("constructing Kubernetes client")
}
volumesnapshotclientset, err := volumesnapshotclientv1.NewForConfig(kubeConfig)
if err != nil {
log.WithError(err).Fatal("constructing volume snapshot client")
}
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
log.WithError(err).Fatal("unable to set up health check")
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
log.WithError(err).Fatal("unable to set up ready check")
}
cp, err := layer.NewProvider(&cfg.Content.Storage)
if err != nil {
log.WithError(err).Fatal("invalid content provider configuration")
}
err = volumesnapshotv1.AddToScheme(mgr.GetScheme())
if err != nil {
log.WithError(err).Fatal("cannot register Kubernetes volumesnapshotv1 schema - this should never happen")
}
mgmt, err := manager.New(cfg.Manager, mgr.GetClient(), clientset, volumesnapshotclientset, cp)
if err != nil {
log.WithError(err).Fatal("cannot create manager")
}
defer mgmt.Close()
if cfg.Prometheus.Addr != "" {
err = mgmt.RegisterMetrics(metrics.Registry)
if err != nil {
log.WithError(err).Error("Prometheus metrics incomplete")
}
}
if len(cfg.RPCServer.RateLimits) > 0 {
log.WithField("ratelimits", cfg.RPCServer.RateLimits).Info("imposing rate limits on the gRPC interface")
}
ratelimits := common_grpc.NewRatelimitingInterceptor(cfg.RPCServer.RateLimits)
metrics.Registry.MustRegister(ratelimits)
grpcMetrics := grpc_prometheus.NewServerMetrics()
grpcMetrics.EnableHandlingTimeHistogram(
grpc_prometheus.WithHistogramBuckets([]float64{.005, .025, .05, .1, .5, 1, 2.5, 5, 30, 60, 120, 240, 600}),
)
metrics.Registry.MustRegister(grpcMetrics)
grpcOpts := common_grpc.ServerOptionsWithInterceptors(
[]grpc.StreamServerInterceptor{grpcMetrics.StreamServerInterceptor()},
[]grpc.UnaryServerInterceptor{grpcMetrics.UnaryServerInterceptor(), ratelimits.UnaryInterceptor()},
)
if cfg.RPCServer.TLS.CA != "" && cfg.RPCServer.TLS.Certificate != "" && cfg.RPCServer.TLS.PrivateKey != "" {
tlsConfig, err := common_grpc.ClientAuthTLSConfig(
cfg.RPCServer.TLS.CA, cfg.RPCServer.TLS.Certificate, cfg.RPCServer.TLS.PrivateKey,
common_grpc.WithSetClientCAs(true),
common_grpc.WithServerName("ws-manager"),
)
if err != nil {
log.WithError(err).Fatal("cannot load ws-manager certs")
}
grpcOpts = append(grpcOpts, grpc.Creds(credentials.NewTLS(tlsConfig)))
} else {
log.Warn("no TLS configured - gRPC server will be unsecured")
}
grpcServer := grpc.NewServer(grpcOpts...)
defer grpcServer.Stop()
grpc_prometheus.Register(grpcServer)
if cfg.ImageBuilderProxy.TargetAddr != "" {
creds := insecure.NewCredentials()
if cfg.ImageBuilderProxy.TLS.CA != "" && cfg.ImageBuilderProxy.TLS.Certificate != "" && cfg.ImageBuilderProxy.TLS.PrivateKey != "" {
tlsConfig, err := common_grpc.ClientAuthTLSConfig(
cfg.ImageBuilderProxy.TLS.CA, cfg.ImageBuilderProxy.TLS.Certificate, cfg.ImageBuilderProxy.TLS.PrivateKey,
common_grpc.WithSetRootCAs(true),
common_grpc.WithServerName("image-builder-mk3"),
)
if err != nil {
log.WithError(err).Fatal("cannot load image-builder-mk3 TLS certs")
}
log.Info("Loaded TLS for image builder")
creds = credentials.NewTLS(tlsConfig)
}
// Note: never use block here, because image-builder connects to ws-manager,
// and if we blocked here, ws-manager wouldn't come up, hence we couldn't connect to ws-manager.
conn, err := grpc.Dial(cfg.ImageBuilderProxy.TargetAddr, grpc.WithTransportCredentials(creds))
if err != nil {
log.WithError(err).Fatal("failed to connect to image builder")
}
imgbldr.RegisterImageBuilderServer(grpcServer, proxy.ImageBuilder{D: imgbldr.NewImageBuilderClient(conn)})
}
manager.Register(grpcServer, mgmt)
lis, err := net.Listen("tcp", cfg.RPCServer.Addr)
if err != nil {
log.WithError(err).WithField("addr", cfg.RPCServer.Addr).Fatal("cannot start RPC server")
}
//nolint:errcheck
go grpcServer.Serve(lis)
log.WithField("addr", cfg.RPCServer.Addr).Info("started gRPC server")
monitor, err := mgmt.CreateMonitor()
if err != nil {
log.WithError(err).Fatal("cannot start workspace monitor")
}
go func() {
mgr.GetCache().WaitForCacheSync(context.Background())
err = monitor.Start()
if err != nil {
log.WithError(err).Fatal("cannot start workspace monitor")
}
}()
defer monitor.Stop()
log.Info("workspace monitor is up and running")
err = (&manager.PodReconciler{
Monitor: monitor,
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("Pod"),
Scheme: mgr.GetScheme(),
Pods: make(map[types.NamespacedName]corev1.Pod),
}).SetupWithManager(mgr)
if err != nil {
log.WithError(err).Fatal("unable to create controller", "controller", "Pod")
}
if cfg.PProf.Addr != "" {
go pprof.Serve(cfg.PProf.Addr)
}
// run until we're told to stop
log.Info("🦸 wsman is up and running. Stop with SIGINT or CTRL+C")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
log.WithError(err).Fatal("problem starting wsman")
}
log.Info("Received SIGINT - shutting down")
},
}
func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
rootCmd.AddCommand(runCmd)
}
var (
scheme = runtime.NewScheme()
)