Skip to content
Closed
2 changes: 1 addition & 1 deletion docs/pages/reference/cli/teleport.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ we recommend using a [configuration file](../config.mdx) in production.
| `--ca-pin` | none | **string** `sha256:<hash>` | set CA pin to validate the Auth Service. Generated by `tctl status` |
| `--nodename` | value returned by the `hostname` command on the machine | **string** | assigns an alternative name for the node which can be used by clients to log in. |
| `-c, --config` | `/etc/teleport.yaml` | **string** `.yaml` filepath | starts services with config specified in the YAML file, overrides CLI flags if set |
| `--apply-on-startup` | none | **string** `.yaml` filepath | On startup, always apply resources described in the file at the given path. Only supports the following kinds: `token`, `role`, `user`, `cluster-auth-preference`, `cluster-networking-config`. |
| `--apply-on-startup` | none | **string** `.yaml` filepath | On startup, always apply resources described in the file at the given path. Only supports the following kinds: `token`, `role`, `user`, `cluster_auth_preference`, `cluster_networking_config`, `bot`. |
| `--bootstrap` | none | **string** `.yaml` filepath | bootstrap configured YAML resources {/* TODO link how to configure this file */} |
| `--labels` | none | **string** comma-separated list | assigns a set of labels to a node, for example env=dev,app=web. See the explanation of labeling mechanism in the [Labeling Nodes](../../admin-guides/management/admin/labels.mdx) section. |
| `--insecure` | none | none | disable certificate validation on Proxy Service, validation still occurs on Auth Service. |
Expand Down
74 changes: 66 additions & 8 deletions lib/auth/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ import (
"github.com/gravitational/teleport/api/client/proto"
autoupdatev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1"
clusterconfigpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/clusterconfig/v1"
dbobjectimportrulev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobjectimportrule/v1"
healthcheckconfigv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/healthcheckconfig/v1"
machineidv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/clusterconfig"
Expand All @@ -57,7 +55,6 @@ import (
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib"
"github.com/gravitational/teleport/lib/auth/autoupdate/autoupdatev1"
"github.com/gravitational/teleport/lib/auth/dbobjectimportrule/dbobjectimportrulev1"
igcredentials "github.com/gravitational/teleport/lib/auth/integration/credentials"
"github.com/gravitational/teleport/lib/auth/keystore"
"github.com/gravitational/teleport/lib/auth/machineid/machineidv1"
Expand Down Expand Up @@ -804,7 +801,10 @@ func initializeAuthority(ctx context.Context, asrv *Server, caID types.CertAuthI
"key_types", []string{strings.Join(allKeyTypes[:numKeyTypes-1], ", "), allKeyTypes[numKeyTypes-1]},
)
}

ca, err = applyAuthorityConfig(ctx, asrv, ca)
if err != nil {
return nil, nil, trace.Wrap(err)
}
keysInUse := collectKeysInUse(ca.GetActiveKeys(), ca.GetAdditionalTrustedKeys())
return usableKeysResult, keysInUse, nil
}
Expand All @@ -824,6 +824,59 @@ func collectKeysInUse(cas ...types.CAKeySet) (keysInUse [][]byte) {
return keysInUse
}

// applyAuthorityConfig applies the latest keystore config to active keys updating
// the stored CA if any changes occur.
func applyAuthorityConfig(ctx context.Context, asrv *Server, ca types.CertAuthority) (types.CertAuthority, error) {
activeKeys := ca.GetActiveKeys()
var (
changed bool
err error
)

apply := func(curr []byte) ([]byte, error) {
next, err := asrv.keyStore.ApplyMultiRegionConfig(ctx, curr)
if err != nil {
return nil, trace.Wrap(err)
}
if !slices.Equal(curr, next) {
changed = true
}
return next, nil
}

for _, key := range activeKeys.SSH {
key.PrivateKey, err = apply(key.PrivateKey)
if err != nil {
return nil, trace.Wrap(err)
}
}
for _, key := range activeKeys.TLS {
key.Key, err = apply(key.Key)
if err != nil {
return nil, trace.Wrap(err)
}
}
for _, key := range activeKeys.JWT {
key.PrivateKey, err = apply(key.PrivateKey)
if err != nil {
return nil, trace.Wrap(err)
}
}
if !changed {
return ca, nil
}
if err := ca.SetActiveKeys(activeKeys); err != nil {
return nil, trace.Wrap(err)
}
// This is only executed during cluster init while holding a lock to prevent
// other auth servers from updating CAs simulaniously.
ca, err = asrv.UpdateCertAuthority(ctx, ca)
if err != nil {
return nil, trace.Wrap(err)
}
return ca, nil
}

// generateAuthority creates a new self-signed authority of the provided type
// and returns it to the caller. It is the responsibility of callers to persist
// the authority.
Expand Down Expand Up @@ -1577,6 +1630,15 @@ func applyResources(ctx context.Context, service *Services, resources []types.Re
return cmp.Compare(priorityA, priorityB)
})
for _, resource := range resources {
// DO NOT ADD EVERY RESOURCE TO THIS SWITCH
// Apply-on-startup should not be supported for every resource, this is a way to bootstrap a
// minimal cluster (bootstrap IaC user, role, bot, join token, config so you have enough
// trust to join core components without human intervention).
// Resources not required for a minimal cluster should be created via Infrastructure-as-Code
// (tctl, Terraform Provider, Kube Operator)
// You MUST also add a test case for your resource in `TestInit_ApplyOnStartup` because adding
// your resource to this switch in not enough for the --apply-on-startup flag to work, you also
// need to RegisterResourceUnmarshaler() your resource.
switch r := resource.(type) {
case types.ProvisionToken:
err = service.Provisioner.UpsertToken(ctx, r)
Expand All @@ -1594,14 +1656,10 @@ func applyResources(ctx context.Context, service *Services, resources []types.Re
_, err = service.ClusterConfigurationInternal.UpsertAuthPreference(ctx, r)
case types.Resource153UnwrapperT[*machineidv1pb.Bot]:
_, err = machineidv1.UpsertBot(ctx, service, r.UnwrapT(), time.Now(), "system")
case types.Resource153UnwrapperT[*dbobjectimportrulev1pb.DatabaseObjectImportRule]:
_, err = dbobjectimportrulev1.UpsertDatabaseObjectImportRule(ctx, service, r.UnwrapT())
case types.Resource153UnwrapperT[*autoupdatev1pb.AutoUpdateConfig]:
_, err = autoupdatev1.UpsertAutoUpdateConfig(ctx, service, r.UnwrapT())
case types.Resource153UnwrapperT[*autoupdatev1pb.AutoUpdateVersion]:
_, err = autoupdatev1.UpsertAutoUpdateVersion(ctx, service, r.UnwrapT())
case types.Resource153UnwrapperT[*healthcheckconfigv1pb.HealthCheckConfig]:
_, err = service.UpsertHealthCheckConfig(ctx, r.UnwrapT())
default:
return trace.NotImplemented("cannot apply resource of type %T", resource)
}
Expand Down
20 changes: 18 additions & 2 deletions lib/auth/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1795,6 +1795,12 @@ spec:
second_factor: off
type: local
version: v2
`
botYAML = `kind: bot
metadata:
name: my-bot
spec:
roles: ["admin"]
`
)

Expand All @@ -1807,6 +1813,7 @@ func TestInit_ApplyOnStartup(t *testing.T) {
lock := resourceFromYAML(t, lockYAML).(types.Lock)
clusterNetworkingConfig := resourceFromYAML(t, clusterNetworkingConfYAML).(types.ClusterNetworkingConfig)
authPref := resourceFromYAML(t, authPrefYAML).(types.AuthPreference)
bot := resourceFromYAML(t, botYAML)

tests := []struct {
name string
Expand Down Expand Up @@ -1876,9 +1883,18 @@ func TestInit_ApplyOnStartup(t *testing.T) {
assertError: require.NoError,
},
{
name: "Apply HealthCheckConfig",
name: "Apply Role+Bot",
modifyConfig: func(cfg *InitConfig) {
cfg.ApplyOnStartupResources = append(cfg.ApplyOnStartupResources, role)
cfg.ApplyOnStartupResources = append(cfg.ApplyOnStartupResources, bot)
},
assertError: require.NoError,
},
{
name: "Apply Bot+Role",
modifyConfig: func(cfg *InitConfig) {
cfg.ApplyOnStartupResources = append(cfg.ApplyOnStartupResources, newHealthCheckConfig(t))
cfg.ApplyOnStartupResources = append(cfg.ApplyOnStartupResources, bot)
cfg.ApplyOnStartupResources = append(cfg.ApplyOnStartupResources, role)
},
assertError: require.NoError,
},
Expand Down
Loading
Loading