mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-12 02:15:01 +00:00
Deny watchers for cert_authority_override on OSS Auth (#66644)
* Make the cache resilient CA override NotImplemented * Don't apply overrides on OSS builds * Use modulestest.EnterpriseModules() * Revert "Make the cache resilient CA override NotImplemented" This reverts commitd07c9db8e0. * Deny watchers for cert_authority_override in OSS Auth * Tweak watcher debug message * Re-create watcher on events other than OpPut or OpDelete * Fix verifyOpInitKindsForCAEvents return when `e.Resource==nil` * Mention older control plane in comments * Add SubKind to Windows CA override watchers * Restore ForWindows watcher * Revert "Add SubKind to Windows CA override watchers" This reverts commit458f93f5fd.
This commit is contained in:
@@ -11609,7 +11609,7 @@ func TestWatchAllCacheKinds(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := t.Context()
|
||||
srv := newTestTLSServer(t)
|
||||
srv := newTestTLSServer(t, withModules(modulestest.EnterpriseModules()))
|
||||
|
||||
for role, watchKinds := range map[types.SystemRole][]types.WatchKind{
|
||||
types.RoleProxy: cache.ForProxy(cache.Config{}).Watches,
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ func (a *Server) GenerateWindowsDesktopCert(ctx context.Context, req *proto.Wind
|
||||
}
|
||||
|
||||
// Calculate CA override.
|
||||
subCAResolver, err := subca.NewCAOverrideResolver(a.Cache, a.subCAEnabled)
|
||||
subCAResolver, err := subca.NewCAOverrideResolver(a.Cache, a.modules.IsEnterpriseBuild(), a.subCAEnabled)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func TestDesktopAccessDisabled(t *testing.T) {
|
||||
func TestDesktopAccessCAOverrides(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tlsServer := newTestTLSServer(t)
|
||||
tlsServer := newTestTLSServer(t, withModules(modulestest.EnterpriseModules()))
|
||||
authServer := tlsServer.Auth()
|
||||
authServer.SetSubCAEnabled(true)
|
||||
|
||||
|
||||
+32
-8
@@ -155,6 +155,7 @@ import (
|
||||
"github.com/gravitational/teleport/lib/join/joinv1"
|
||||
"github.com/gravitational/teleport/lib/join/legacyjoin"
|
||||
"github.com/gravitational/teleport/lib/limiter"
|
||||
"github.com/gravitational/teleport/lib/modules"
|
||||
"github.com/gravitational/teleport/lib/observability/metrics"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/services/local"
|
||||
@@ -556,12 +557,13 @@ func (g *GRPCServer) WatchEvents(watch *authpb.Watch, stream authpb.AuthService_
|
||||
stream,
|
||||
scopedAuth.scopedContext.User.GetName(),
|
||||
scopedAuth,
|
||||
g.AuthServer.modules,
|
||||
))
|
||||
}
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
return trace.Wrap(WatchEvents(watch, stream, auth.User.GetName(), auth))
|
||||
return trace.Wrap(WatchEvents(watch, stream, auth.User.GetName(), auth, g.AuthServer.modules))
|
||||
}
|
||||
|
||||
// WatchEvent is a stream interface for sending events.
|
||||
@@ -574,14 +576,18 @@ type Watcher interface {
|
||||
NewStream(ctx context.Context, watch types.Watch) (stream.Stream[types.Event], error)
|
||||
}
|
||||
|
||||
// WatchEvents watches for events and streams them to the provided stream.
|
||||
func WatchEvents(watch *authpb.Watch, stream WatchEvent, componentName string, auth Watcher) error {
|
||||
servicesWatch := types.Watch{
|
||||
Name: componentName,
|
||||
Kinds: watch.Kinds,
|
||||
AllowPartialSuccess: watch.AllowPartialSuccess,
|
||||
}
|
||||
var enterpriseOnlyWatchKinds = []string{
|
||||
types.KindCertAuthorityOverride,
|
||||
}
|
||||
|
||||
// WatchEvents watches for events and streams them to the provided stream.
|
||||
func WatchEvents(
|
||||
watch *authpb.Watch,
|
||||
stream WatchEvent,
|
||||
componentName string,
|
||||
auth Watcher,
|
||||
mods modules.Modules,
|
||||
) error {
|
||||
// KindNamespace is being removed but v17 agents will still try to include
|
||||
// it in their cache and they will occasionally do a GetNamespace, so we
|
||||
// pretend to support it as a resource kind here; it's sound to do so
|
||||
@@ -596,10 +602,28 @@ func WatchEvents(watch *authpb.Watch, stream WatchEvent, componentName string, a
|
||||
removedNamespaceWatch = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Deny watchers for Enteprise-only Kinds if we are running OSS.
|
||||
// This simplifies remote event streams that would otherwise try to hit
|
||||
// unimplemented endpoints (Enterprise-only services tend to only bind in
|
||||
// Enterprise builds).
|
||||
if mods.IsOSSBuild() && goslices.Contains(enterpriseOnlyWatchKinds, k.Kind) {
|
||||
if watch.AllowPartialSuccess {
|
||||
continue
|
||||
}
|
||||
return trace.BadParameter("watch for kind %s only available in Teleport Enterprise", k.Kind)
|
||||
}
|
||||
|
||||
filteredKinds = append(filteredKinds, k)
|
||||
}
|
||||
watch.Kinds = filteredKinds
|
||||
|
||||
servicesWatch := types.Watch{
|
||||
Name: componentName,
|
||||
Kinds: watch.Kinds,
|
||||
AllowPartialSuccess: watch.AllowPartialSuccess,
|
||||
}
|
||||
|
||||
events, err := auth.NewStream(stream.Context(), servicesWatch)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
// Teleport
|
||||
// Copyright (C) 2026 Gravitational, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/lib/auth/authtest"
|
||||
"github.com/gravitational/teleport/lib/modules/modulestest"
|
||||
)
|
||||
|
||||
func TestWatchEvents_enterpriseOnlyKinds(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ossTestServer := newTestTLSServer(t)
|
||||
entTestServer := newTestTLSServer(t, withModules(modulestest.EnterpriseModules()))
|
||||
|
||||
ossClient, err := ossTestServer.NewClient(authtest.TestAdmin())
|
||||
require.NoError(t, err)
|
||||
entClient, err := entTestServer.NewClient(authtest.TestAdmin())
|
||||
require.NoError(t, err)
|
||||
|
||||
// kindOther is a watch kind we expect to always work.
|
||||
const kindOther = types.KindCertAuthority
|
||||
|
||||
const wantErr = "only available in Teleport Enterprise"
|
||||
|
||||
confirmKinds := func(t *testing.T, watcher types.Watcher, wantKinds ...string) {
|
||||
t.Helper()
|
||||
|
||||
select {
|
||||
case <-t.Context().Done():
|
||||
t.Fatal("t.Context() done before first event")
|
||||
case <-watcher.Done():
|
||||
t.Fatalf("watcher done before first event, err=%v", watcher.Error())
|
||||
case e := <-watcher.Events():
|
||||
require.Equal(t, types.OpInit, e.Type, "Unexpected event type")
|
||||
|
||||
s, ok := e.Resource.(types.WatchStatus)
|
||||
require.True(t, ok, "e.Resource has unexpected type: %T", e.Resource)
|
||||
|
||||
watchKinds := s.GetKinds()
|
||||
gotKinds := make([]string, len(watchKinds))
|
||||
for i, k := range watchKinds {
|
||||
gotKinds[i] = k.Kind
|
||||
}
|
||||
if diff := cmp.Diff(wantKinds, gotKinds); diff != "" {
|
||||
t.Errorf("WatchStatus.GetKinds() mismatch (-want +got)\n%s", diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
kind string
|
||||
}{
|
||||
{kind: types.KindCertAuthorityOverride},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.kind, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OSS fails", func(t *testing.T) {
|
||||
watcher, err := ossClient.NewWatcher(t.Context(), types.Watch{
|
||||
Name: "oss",
|
||||
Kinds: []types.WatchKind{
|
||||
{Kind: test.kind},
|
||||
},
|
||||
})
|
||||
// The stream is expected to start and fail before the first event.
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { watcher.Close() })
|
||||
|
||||
// Wait for stream to close and assert failure.
|
||||
select {
|
||||
case <-watcher.Done():
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("Timed out waiting for stream to close")
|
||||
}
|
||||
assert.ErrorContains(t, watcher.Error(), wantErr, "Watcher error mismatch")
|
||||
})
|
||||
|
||||
t.Run("OSS with allow partial succeeds", func(t *testing.T) {
|
||||
watcher, err := ossClient.NewWatcher(t.Context(), types.Watch{
|
||||
Name: "oss-allow-partial",
|
||||
Kinds: []types.WatchKind{
|
||||
{Kind: kindOther},
|
||||
{Kind: test.kind},
|
||||
},
|
||||
AllowPartialSuccess: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { watcher.Close() })
|
||||
|
||||
confirmKinds(t, watcher, kindOther)
|
||||
assert.NoError(t, watcher.Error(), "Watcher has unexpected error")
|
||||
})
|
||||
|
||||
t.Run("Ent succeeds", func(t *testing.T) {
|
||||
watcher, err := entClient.NewWatcher(t.Context(), types.Watch{
|
||||
Name: "ent-allow-partial",
|
||||
Kinds: []types.WatchKind{
|
||||
{Kind: kindOther},
|
||||
{Kind: test.kind},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { watcher.Close() })
|
||||
|
||||
confirmKinds(t, watcher, kindOther, test.kind)
|
||||
assert.NoError(t, watcher.Error(), "Watcher has unexpected error")
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1541,6 +1541,7 @@ func (s *WindowsService) watchCAEvents(
|
||||
// Filters not supported for KindCertAuthorityOverride.
|
||||
},
|
||||
},
|
||||
AllowPartialSuccess: true, // CAOverride watch allowed to fail.
|
||||
})
|
||||
if err != nil {
|
||||
logger.WarnContext(ctx,
|
||||
@@ -1602,6 +1603,16 @@ func runCAWatcherLoop(
|
||||
// * https://github.com/gravitational/teleport/blob/1f0ca9e4ae66a47f39d10c40f35e55d5ac5e15ac/lib/services/watcher.go#L336-L338
|
||||
switch {
|
||||
case e.Type == types.OpInit && isFirstEvent:
|
||||
confirmedCAOverrides, err := verifyOpInitKindsForCAEvents(e)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
if !confirmedCAOverrides {
|
||||
eLog.DebugContext(ctx, "WatchStatus did not confirm CA overrides. Overrides are not watched. This is expected if the agent is connected to a non-Enterprise control plane or a control plane that does not support CA overrides.")
|
||||
// Important: "Teleport OSS" here means, specifically, the Auth Server.
|
||||
// An OSS Desktop Service can be paired with an Ent Auth, so checking
|
||||
// the current binary build won't work.
|
||||
}
|
||||
isFirstEvent = false
|
||||
continue // OK, expected.
|
||||
|
||||
@@ -1612,8 +1623,12 @@ func runCAWatcherLoop(
|
||||
)
|
||||
return nil
|
||||
|
||||
case e.Type != types.OpPut:
|
||||
case e.Type == types.OpDelete:
|
||||
continue // OK, we only care about mutating events.
|
||||
|
||||
case e.Type != types.OpPut:
|
||||
eLog.DebugContext(ctx, "Received unexpected event type. Attempting to re-create the watcher.")
|
||||
return nil
|
||||
}
|
||||
|
||||
if e.Resource.GetKind() == types.KindCertAuthorityOverride {
|
||||
@@ -1647,6 +1662,37 @@ func runCAWatcherLoop(
|
||||
}
|
||||
}
|
||||
|
||||
// verifyOpInitKindsForCAEvents verifies the OpInit WatchStatus payload.
|
||||
// - KindCertAuthority is mandatory.
|
||||
// - KindCertAuthorityOverride is optional.
|
||||
func verifyOpInitKindsForCAEvents(e types.Event) (confirmedCAOverrides bool, _ error) {
|
||||
if e.Resource == nil {
|
||||
// Consider all kinds confirmed. See types.Watch.AllowPartialSuccess.
|
||||
// https://github.com/gravitational/teleport/blob/77e56f05a4172b04b386d8b56f4842dd4b3b870d/api/types/events.go#L105-L106
|
||||
return true, nil
|
||||
}
|
||||
|
||||
status, ok := e.Resource.(types.WatchStatus)
|
||||
if !ok {
|
||||
return false, trace.BadParameter("event OpInit has unexpected Resource: %T (expected WatchStatus)", e.Resource)
|
||||
}
|
||||
|
||||
var hasKindCA, hasKindCAOverride bool
|
||||
for _, kind := range status.GetKinds() {
|
||||
switch kind.Kind {
|
||||
case types.KindCertAuthority:
|
||||
hasKindCA = true
|
||||
case types.KindCertAuthorityOverride:
|
||||
hasKindCAOverride = true
|
||||
}
|
||||
}
|
||||
if !hasKindCA {
|
||||
return false, trace.BadParameter("watch status failed to confirm kind %s, aborting watcher", types.KindCertAuthority)
|
||||
}
|
||||
|
||||
return hasKindCAOverride, nil
|
||||
}
|
||||
|
||||
// getKDCAddress gets the KDC address that should be used for NLA in
|
||||
// this priority order:
|
||||
// 1. Explicitly specified kdc_address
|
||||
|
||||
@@ -51,14 +51,15 @@ type CAOverrideResolver struct {
|
||||
// caGetter a CA override source, likely a cached services.SubCAServiceGetter
|
||||
// implementation.
|
||||
//
|
||||
// Production callers should use [Enabled] as the featureEnabled value.
|
||||
func NewCAOverrideResolver(caGetter CAOverrideGetter, featureEnabled bool) (*CAOverrideResolver, error) {
|
||||
// Production callers should use `modules.Modules.IsEnterpriseBuild()` and
|
||||
// [Enabled] as the boolean values, respectively.
|
||||
func NewCAOverrideResolver(caGetter CAOverrideGetter, isEnterpriseBuild, featureEnabled bool) (*CAOverrideResolver, error) {
|
||||
if caGetter == nil {
|
||||
return nil, trace.BadParameter("nil caGetter")
|
||||
}
|
||||
return &CAOverrideResolver{
|
||||
caGetter: caGetter,
|
||||
featureEnabled: featureEnabled,
|
||||
featureEnabled: isEnterpriseBuild && featureEnabled,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,9 @@ func TestCAOverrideResolver_CalculateOverride(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("ok: empty CA override", func(t *testing.T) {
|
||||
r, err := subca.NewCAOverrideResolver(subCA, true)
|
||||
const isEnterpriseBuild = true
|
||||
const featureEnabled = true
|
||||
r, err := subca.NewCAOverrideResolver(subCA, isEnterpriseBuild, featureEnabled)
|
||||
require.NoError(t, err, "NewCAOverrideResolver errored")
|
||||
got, err := r.CalculateOverride(t.Context(), caID, subca.Certificate{PEM: caCert1PEM})
|
||||
require.NoError(t, err, "CalculateCAOverride errored")
|
||||
@@ -201,12 +203,13 @@ func TestCAOverrideResolver_CalculateOverride(t *testing.T) {
|
||||
caOtherCertPEM := []byte("Pretend this is a PEM. Contents not parsed.")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
featureDisabled bool // Inverse because most tests want enabled.
|
||||
id types.CertAuthorityOverrideID
|
||||
caCert subca.Certificate
|
||||
wantErr string
|
||||
want *subca.CalculateOverrideResult
|
||||
name string
|
||||
notEntepriseBuild bool // Inverse because most tests want enterprise.
|
||||
featureDisabled bool // Inverse because most tests want enabled.
|
||||
id types.CertAuthorityOverrideID
|
||||
caCert subca.Certificate
|
||||
wantErr string
|
||||
want *subca.CalculateOverrideResult
|
||||
}{
|
||||
{
|
||||
name: "ok: no CA override resource exists",
|
||||
@@ -243,6 +246,16 @@ func TestCAOverrideResolver_CalculateOverride(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ok: build is not Enterprise",
|
||||
notEntepriseBuild: true,
|
||||
id: caID,
|
||||
caCert: subca.Certificate{PEM: caCert1PEM},
|
||||
want: &subca.CalculateOverrideResult{
|
||||
// If enabled then o1 would apply, per test above.
|
||||
CACertificate: subca.Certificate{PEM: caCert1PEM},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ok: feature disabled",
|
||||
featureDisabled: true,
|
||||
@@ -316,7 +329,7 @@ func TestCAOverrideResolver_CalculateOverride(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r, err := subca.NewCAOverrideResolver(subCA, !test.featureDisabled)
|
||||
r, err := subca.NewCAOverrideResolver(subCA, !test.notEntepriseBuild, !test.featureDisabled)
|
||||
require.NoError(t, err, "NewCAOverrideResolver errored")
|
||||
|
||||
got, err := r.CalculateOverride(t.Context(), test.id, test.caCert)
|
||||
|
||||
@@ -155,8 +155,10 @@ func (c *CertificateStoreClient) updateCAOverrideCRLs(ctx context.Context, tc *t
|
||||
ClusterName: clusterName,
|
||||
CAType: string(caType),
|
||||
})
|
||||
if trace.IsNotFound(err) {
|
||||
return nil // OK, overrides are optional.
|
||||
if trace.IsNotFound(err) || trace.IsNotImplemented(err) {
|
||||
// - NotFound: Overrides are optional.
|
||||
// - NotImplemented: Running against OSS Teleport or an older control plane.
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return trace.Wrap(err, "read CA overrides")
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@@ -115,16 +116,12 @@ func TestCertificateStoreClient_Update(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
ap := &fakeAccessPoint{
|
||||
cas: []types.CertAuthority{ca},
|
||||
}
|
||||
ldap := newFakeLDAP()
|
||||
csc := NewCertificateStoreClient(CertificateStoreConfig{
|
||||
AccessPoint: &fakeAccessPoint{
|
||||
cas: []types.CertAuthority{
|
||||
ca,
|
||||
},
|
||||
caOverrides: []*subcav1.CertAuthorityOverride{
|
||||
caOverride,
|
||||
},
|
||||
},
|
||||
AccessPoint: ap,
|
||||
Domain: domain,
|
||||
Logger: logtest.NewLogger(),
|
||||
ClusterName: clusterName,
|
||||
@@ -134,20 +131,12 @@ func TestCertificateStoreClient_Update(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
tc := &tls.Config{} // Unused, LDAP is faked.
|
||||
require.NoError(t,
|
||||
csc.Update(t.Context(), tc),
|
||||
"Update errored",
|
||||
)
|
||||
|
||||
// Prepare wanted state.
|
||||
// Prepare wanted state (CAs only).
|
||||
containerDN, err := crlContainerDN(domain, types.WindowsCA)
|
||||
require.NoError(t, err)
|
||||
caCRLDN, err := CRLDN(caCert.Subject.CommonName, caCert.SubjectKeyId, domain, types.WindowsCA)
|
||||
require.NoError(t, err)
|
||||
overrideCRLDN, err := CRLDN(overrideCert.Subject.CommonName, overrideCert.SubjectKeyId, domain, types.WindowsCA)
|
||||
require.NoError(t, err)
|
||||
want := map[string]*ldapEntry{
|
||||
wantCAs := map[string]*ldapEntry{
|
||||
containerDN: {
|
||||
ObjectClass: "container",
|
||||
},
|
||||
@@ -157,38 +146,68 @@ func TestCertificateStoreClient_Update(t *testing.T) {
|
||||
"certificateRevocationList": {caCRL},
|
||||
},
|
||||
},
|
||||
overrideCRLDN: {
|
||||
ObjectClass: "cRLDistributionPoint",
|
||||
Attrs: map[string][]string{
|
||||
"certificateRevocationList": {overrideCRL},
|
||||
},
|
||||
}
|
||||
|
||||
// Prepare wanted state (CAs + CA overrides).
|
||||
overrideCRLDN, err := CRLDN(overrideCert.Subject.CommonName, overrideCert.SubjectKeyId, domain, types.WindowsCA)
|
||||
require.NoError(t, err)
|
||||
wantOverrides := maps.Clone(wantCAs)
|
||||
wantOverrides[overrideCRLDN] = &ldapEntry{
|
||||
ObjectClass: "cRLDistributionPoint",
|
||||
Attrs: map[string][]string{
|
||||
"certificateRevocationList": {overrideCRL},
|
||||
},
|
||||
}
|
||||
|
||||
// Assert LDAP state.
|
||||
if diff := cmp.Diff(want, ldap.entries); diff != "" {
|
||||
t.Fatalf("LDAP entries mismatch (-want +got)\n%s", diff)
|
||||
}
|
||||
runUpdate := func(t *testing.T, wantEntries map[string]*ldapEntry) {
|
||||
t.Helper()
|
||||
|
||||
// Exercise entry update flow.
|
||||
t.Run("update", func(t *testing.T) {
|
||||
// Reset state after running.
|
||||
defer func() { clear(ldap.entries) }()
|
||||
|
||||
tc := &tls.Config{} // Unused, LDAP is faked.
|
||||
require.NoError(t,
|
||||
csc.Update(t.Context(), tc),
|
||||
"Update errored",
|
||||
)
|
||||
|
||||
// Assert LDAP state.
|
||||
if diff := cmp.Diff(want, ldap.entries); diff != "" {
|
||||
if diff := cmp.Diff(wantEntries, ldap.entries); diff != "" {
|
||||
t.Fatalf("LDAP entries mismatch (-want +got)\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("CAs only", func(t *testing.T) {
|
||||
runUpdate(t, wantCAs)
|
||||
})
|
||||
|
||||
// Prepare wanted state (CAs + overrides).
|
||||
ap.caOverrides = []*subcav1.CertAuthorityOverride{caOverride}
|
||||
t.Run("CAs and CA overrides", func(t *testing.T) {
|
||||
runUpdate(t, wantOverrides)
|
||||
})
|
||||
|
||||
// Exercise entry update flow.
|
||||
t.Run("update", func(t *testing.T) {
|
||||
// Same state.
|
||||
runUpdate(t, wantOverrides)
|
||||
})
|
||||
|
||||
// Exercise CA overrides NotImplemented (OSS).
|
||||
t.Run("CA overrides not found", func(t *testing.T) {
|
||||
ap.caOverridesNotImplemented = true
|
||||
t.Cleanup(func() { ap.caOverridesNotImplemented = false })
|
||||
|
||||
// CA overrides not synced.
|
||||
runUpdate(t, wantCAs)
|
||||
})
|
||||
}
|
||||
|
||||
type fakeAccessPoint struct {
|
||||
CRLGenerator
|
||||
|
||||
cas []types.CertAuthority
|
||||
caOverrides []*subcav1.CertAuthorityOverride
|
||||
cas []types.CertAuthority
|
||||
caOverrides []*subcav1.CertAuthorityOverride
|
||||
caOverridesNotImplemented bool
|
||||
}
|
||||
|
||||
func (f *fakeAccessPoint) GetCertAuthorities(ctx context.Context, caType types.CertAuthType, loadKeys bool) ([]types.CertAuthority, error) {
|
||||
@@ -206,6 +225,10 @@ func (f *fakeAccessPoint) GetCertAuthorities(ctx context.Context, caType types.C
|
||||
}
|
||||
|
||||
func (f *fakeAccessPoint) GetCertAuthorityOverride(ctx context.Context, id types.CertAuthorityOverrideID) (*subcav1.CertAuthorityOverride, error) {
|
||||
if f.caOverridesNotImplemented {
|
||||
return nil, trace.NotImplemented("not implemented")
|
||||
}
|
||||
|
||||
for _, caOverride := range f.caOverrides {
|
||||
if caOverride.Metadata.Name == id.ClusterName && caOverride.SubKind == id.CAType {
|
||||
return caOverride, nil
|
||||
|
||||
Reference in New Issue
Block a user