Andrey Smirnov 92314e47bf
refactor: use controllers/resources to feed trustd with data
This is mostly same as the way `apid` consumes certificates generated by
`machined` via COSI API connection.

Service `trustd` consumes two resources:

* `secrets.Trustd` which contains `trustd` server TLS certificates and
  it gets refreshed as e.g. node IP changes
* `secrets.OSRoot` which contains Talos API CA and join token

This PR fixes an issue with `trustd` certs not always including all IPs
of the node, as previously `trustd` certs will only capture addresses of
the node at the moment of `trustd` startup.

Another thing is that refactoring allows to dynamically change API CA
and join token. This needs more work, but `trustd` should now pick up
changes without any additional changes.

Fixes #5863

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
2022-08-04 23:45:34 +04:00

140 lines
3.7 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package trustd
import (
"context"
"flag"
"fmt"
"log"
"os/signal"
"syscall"
"time"
"github.com/cosi-project/runtime/api/v1alpha1"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/cosi-project/runtime/pkg/state/protobuf/client"
debug "github.com/talos-systems/go-debug"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"github.com/talos-systems/talos/internal/app/trustd/internal/provider"
"github.com/talos-systems/talos/internal/app/trustd/internal/reg"
"github.com/talos-systems/talos/pkg/grpc/factory"
"github.com/talos-systems/talos/pkg/grpc/middleware/auth/basic"
"github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/machinery/resources/secrets"
"github.com/talos-systems/talos/pkg/startup"
)
func runDebugServer(ctx context.Context) {
const debugAddr = ":9983"
debugLogFunc := func(msg string) {
log.Print(msg)
}
if err := debug.ListenAndServe(ctx, debugAddr, debugLogFunc); err != nil {
log.Fatalf("failed to start debug server: %s", err)
}
}
// Main is the entrypoint into trustd.
func Main() {
if err := trustdMain(); err != nil {
log.Fatal(err)
}
}
//nolint:gocyclo
func trustdMain() error {
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
defer cancel()
log.SetFlags(log.Lshortfile | log.Ldate | log.Lmicroseconds | log.Ltime)
flag.Parse()
go runDebugServer(ctx)
var err error
if err = startup.RandSeed(); err != nil {
return fmt.Errorf("startup: %s", err)
}
runtimeConn, err := grpc.Dial("unix://"+constants.TrustdRuntimeSocketPath, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return fmt.Errorf("failed to dial runtime connection: %w", err)
}
stateClient := v1alpha1.NewStateClient(runtimeConn)
resources := state.WrapCore(client.NewAdapter(stateClient))
tlsConfig, err := provider.NewTLSConfig(resources)
if err != nil {
return fmt.Errorf("failed to create remote certificate provider: %w", err)
}
serverTLSConfig, err := tlsConfig.ServerConfig()
if err != nil {
return fmt.Errorf("failed to create OS-level TLS configuration: %w", err)
}
creds := basic.NewTokenCredentialsDynamic(tokenGetter(resources))
networkListener, err := factory.NewListener(
factory.Port(constants.TrustdPort),
)
if err != nil {
return fmt.Errorf("error creating listener: %w", err)
}
networkServer := factory.NewServer(
&reg.Registrator{Resources: resources},
factory.WithDefaultLog(),
factory.WithUnaryInterceptor(creds.UnaryInterceptor()),
factory.ServerOptions(
grpc.Creds(
credentials.NewTLS(serverTLSConfig),
),
),
)
errGroup, ctx := errgroup.WithContext(ctx)
errGroup.Go(func() error {
return networkServer.Serve(networkListener)
})
errGroup.Go(func() error {
<-ctx.Done()
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer shutdownCancel()
factory.ServerGracefulStop(networkServer, shutdownCtx)
return nil
})
return errGroup.Wait()
}
func tokenGetter(state state.State) basic.TokenGetterFunc {
return func(ctx context.Context) (string, error) {
osRoot, err := safe.StateGet[*secrets.OSRoot](ctx, state, resource.NewMetadata(secrets.NamespaceName, secrets.OSRootType, secrets.OSRootID, resource.VersionUndefined))
if err != nil {
return "", err
}
return osRoot.TypedSpec().Token, nil
}
}