Skip to content

Commit 6526611

Browse files
authored
Improve troubleshooting for LDAP authentication errors (#48947)
This introduces two small changes: 1. Use an aggregate error to make sure the original error is included along with our attempt at providing a better error message. This change should help distinguish between bad authn/invalid cert and valid authentication but insufficient user permissions. 2. Make the CRL Distribution Point in Windows certs optional. This metadata is required in the certs we issue for users to RDP, but it doesn't need to be present in the certs we use to authenticate our service account. The problem with including it when it is not needed is it causes windows to perform a revocation check and log a failure, which can lead to wasted time when troubleshooting.
1 parent 879426a commit 6526611

File tree

5 files changed

+42
-19
lines changed

5 files changed

+42
-19
lines changed

lib/auth/desktop.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,13 @@ func (a *Server) GenerateWindowsDesktopCert(ctx context.Context, req *proto.Wind
7171
NotAfter: a.clock.Now().UTC().Add(req.TTL.Get()),
7272
ExtraExtensions: csr.Extensions,
7373
KeyUsage: x509.KeyUsageDigitalSignature,
74-
// CRL is required for Windows smartcard certs.
75-
CRLDistributionPoints: []string{req.CRLEndpoint},
74+
}
75+
76+
// CRL Distribution Points (CDP) are required for Windows smartcard certs
77+
// for users wanting to RDP. They are not required for the service account
78+
// cert that Teleport itself uses to authenticate for LDAP.
79+
if req.CRLEndpoint != "" {
80+
certReq.CRLDistributionPoints = []string{req.CRLEndpoint}
7681
}
7782

7883
limitExceeded, err := a.desktopsLimitExceeded(ctx)

lib/auth/windows/ldap.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,9 @@ func convertLDAPError(err error) error {
184184
return trace.ConnectionProblem(err, "network error")
185185
case ldap.LDAPResultOperationsError:
186186
if strings.Contains(err.Error(), "successful bind must be completed") {
187-
return trace.AccessDenied(
188-
"the LDAP server did not accept Teleport's client certificate, " +
189-
"has the Teleport CA been imported correctly?")
187+
return trace.NewAggregate(trace.AccessDenied(
188+
"the LDAP server did not accept Teleport's client certificate, "+
189+
"has the Teleport CA been imported correctly?"), err)
190190
}
191191
case ldap.LDAPResultEntryAlreadyExists:
192192
return trace.AlreadyExists("LDAP object already exists: %v", err)

lib/auth/windows/windows.go

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -128,19 +128,26 @@ func getCertRequest(req *GenerateCredentialsRequest) (*certRequest, error) {
128128
return nil, trace.Wrap(err)
129129
}
130130
csrPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes})
131-
// Note: this CRL DN may or may not be the same DN published in updateCRL.
132-
//
133-
// There can be multiple AD domains connected to Teleport. Each
134-
// windows_desktop_service is connected to a single AD domain and publishes
135-
// CRLs in it. Each service can also handle RDP connections for a different
136-
// domain, with the assumption that some other windows_desktop_service
137-
// published a CRL there.
138-
crlDN := crlDN(req.ClusterName, req.LDAPConfig, req.CAType)
139-
return &certRequest{
140-
csrPEM: csrPEM,
141-
crlEndpoint: fmt.Sprintf("ldap:///%s?certificateRevocationList?base?objectClass=cRLDistributionPoint", crlDN),
142-
keyDER: keyDER,
143-
}, nil
131+
cr := &certRequest{
132+
csrPEM: csrPEM,
133+
keyDER: keyDER,
134+
}
135+
136+
if !req.OmitCDP {
137+
// Note: this CRL DN may or may not be the same DN published in updateCRL.
138+
//
139+
// There can be multiple AD domains connected to Teleport. Each
140+
// windows_desktop_service is connected to a single AD domain and publishes
141+
// CRLs in it. Each service can also handle RDP connections for a different
142+
// domain, with the assumption that some other windows_desktop_service
143+
// published a CRL there.
144+
crlDN := crlDN(req.ClusterName, req.LDAPConfig, req.CAType)
145+
146+
// TODO(zmb3) consider making Teleport itself the CDP (via HTTP) instead of LDAP
147+
cr.crlEndpoint = fmt.Sprintf("ldap:///%s?certificateRevocationList?base?objectClass=cRLDistributionPoint", crlDN)
148+
}
149+
150+
return cr, nil
144151
}
145152

146153
// AuthInterface is a subset of auth.ClientI
@@ -181,6 +188,11 @@ type GenerateCredentialsRequest struct {
181188
CreateUser bool
182189
// Groups are groups that user should be member of
183190
Groups []string
191+
192+
// OmitCDP can be used to prevent Teleport from issuing certs with a
193+
// CRL Distribution Point (CDP). CDPs are required in user certificates
194+
// for RDP, but they can be omitted for certs that are used for LDAP binds.
195+
OmitCDP bool
184196
}
185197

186198
// GenerateWindowsDesktopCredentials generates a private key / certificate pair for the given

lib/srv/desktop/windows_server.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ func (s *WindowsService) tlsConfigForLDAP() (*tls.Config, error) {
443443
domain: s.cfg.Domain,
444444
ttl: windowsDesktopServiceCertTTL,
445445
activeDirectorySID: s.cfg.SID,
446+
omitCDP: true,
446447
})
447448
if err != nil {
448449
return nil, trace.Wrap(err)
@@ -1226,7 +1227,8 @@ type generateCredentialsRequest struct {
12261227
// createUser specifies if Windows user should be created if missing
12271228
createUser bool
12281229
// groups are groups that user should be member of
1229-
groups []string
1230+
groups []string
1231+
omitCDP bool
12301232
}
12311233

12321234
// generateCredentials generates a private key / certificate pair for the given
@@ -1254,6 +1256,7 @@ func (s *WindowsService) generateCredentials(ctx context.Context, request genera
12541256
AuthClient: s.cfg.AuthClient,
12551257
CreateUser: request.createUser,
12561258
Groups: request.groups,
1259+
OmitCDP: request.omitCDP,
12571260
})
12581261
}
12591262

tool/tctl/common/auth_command.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ type AuthCommand struct {
7979
windowsDomain string
8080
windowsPKIDomain string
8181
windowsSID string
82+
omitCDP bool
8283
signOverwrite bool
8384
password string
8485
caType string
@@ -150,6 +151,7 @@ func (a *AuthCommand) Initialize(app *kingpin.Application, config *servicecfg.Co
150151
a.authSign.Flag("windows-domain", `Active Directory domain for which this cert is valid. Only used when --format is set to "windows"`).StringVar(&a.windowsDomain)
151152
a.authSign.Flag("windows-pki-domain", `Active Directory domain where CRLs will be located. Only used when --format is set to "windows"`).StringVar(&a.windowsPKIDomain)
152153
a.authSign.Flag("windows-sid", `Optional Security Identifier to embed in the certificate. Only used when --format is set to "windows"`).StringVar(&a.windowsSID)
154+
a.authSign.Flag("omit-cdp", `Omit CRL Distribution Points from the cert. Only used when --format is set to "windows"`).BoolVar(&a.omitCDP)
153155

154156
a.authRotate = auth.Command("rotate", "Rotate certificate authorities in the cluster.")
155157
a.authRotate.Flag("grace-period", "Grace period keeps previous certificate authorities signatures valid, if set to 0 will force users to re-login and nodes to re-register.").
@@ -363,6 +365,7 @@ func (a *AuthCommand) generateWindowsCert(ctx context.Context, clusterAPI certif
363365
TTL: a.genTTL,
364366
ClusterName: cn.GetClusterName(),
365367
LDAPConfig: windows.LDAPConfig{Domain: domain},
368+
OmitCDP: a.omitCDP,
366369
AuthClient: clusterAPI,
367370
})
368371
if err != nil {

0 commit comments

Comments
 (0)