Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lib/auth/windows/windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,11 @@ func getCertRequest(req *GenerateCredentialsRequest) (*certRequest, error) {
// domain, with the assumption that some other windows_desktop_service
// published a CRL there.
crlDN := crlDN(req.ClusterName, req.LDAPConfig, req.CAType)
return &certRequest{csrPEM: csrPEM, crlEndpoint: fmt.Sprintf("ldap:///%s?certificateRevocationList?base?objectClass=cRLDistributionPoint", crlDN), keyDER: keyDER}, nil
return &certRequest{
csrPEM: csrPEM,
crlEndpoint: fmt.Sprintf("ldap:///%s?certificateRevocationList?base?objectClass=cRLDistributionPoint", crlDN),
keyDER: keyDER,
}, nil
}

// AuthInterface is a subset of auth.ClientI
Expand Down
2 changes: 2 additions & 0 deletions lib/config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -1848,6 +1848,8 @@ func applyWindowsDesktopConfig(fc *FileConfig, cfg *servicecfg.Config) error {
CA: cert,
}

cfg.WindowsDesktop.PKIDomain = fc.WindowsDesktop.PKIDomain

var hlrs []servicecfg.HostLabelRule
for _, rule := range fc.WindowsDesktop.HostLabels {
r, err := regexp.Compile(rule.Match)
Expand Down
6 changes: 6 additions & 0 deletions lib/config/fileconf.go
Original file line number Diff line number Diff line change
Expand Up @@ -2374,6 +2374,12 @@ type WindowsDesktopService struct {
ShowDesktopWallpaper bool `yaml:"show_desktop_wallpaper,omitempty"`
// LDAP is the LDAP connection parameters.
LDAP LDAPConfig `yaml:"ldap"`
// PKIDomain optionally configures a separate Active Directory domain
// for PKI operations. If empty, the domain from the LDAP config is used.
// This can be useful for cases where PKI is configured in a root domain
// but Teleport is used to provide access to users and computers in a child
// domain.
PKIDomain string `yaml:"pki_domain"`
// Discovery configures desktop discovery via LDAP.
Discovery LDAPDiscoveryConfig `yaml:"discovery,omitempty"`
// Hosts is a list of static, AD-connected Windows hosts. This gives users
Expand Down
1 change: 1 addition & 0 deletions lib/service/desktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ func (process *TeleportProcess) initWindowsDesktopServiceRegistered(log *logrus.
},
ShowDesktopWallpaper: cfg.WindowsDesktop.ShowDesktopWallpaper,
LDAPConfig: windows.LDAPConfig(cfg.WindowsDesktop.LDAP),
PKIDomain: cfg.WindowsDesktop.PKIDomain,
DiscoveryBaseDN: cfg.WindowsDesktop.Discovery.BaseDN,
DiscoveryLDAPFilters: cfg.WindowsDesktop.Discovery.Filters,
DiscoveryLDAPAttributeLabels: cfg.WindowsDesktop.Discovery.LabelAttributes,
Expand Down
6 changes: 6 additions & 0 deletions lib/service/servicecfg/windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ type WindowsDesktopConfig struct {
ShowDesktopWallpaper bool
// LDAP is the LDAP connection parameters.
LDAP LDAPConfig
// PKIDomain optionally configures a separate Active Directory domain
// for PKI operations. If empty, the domain from the LDAP config is used.
// This can be useful for cases where PKI is configured in a root domain
// but Teleport is used to provide access to users and computers in a child
// domain.
PKIDomain string

// Discovery configures automatic desktop discovery via LDAP.
Discovery LDAPDiscoveryConfig
Expand Down
26 changes: 23 additions & 3 deletions lib/srv/desktop/windows_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ type WindowsServiceConfig struct {
// LDAPConfig contains parameters for connecting to an LDAP server.
// LDAP functionality is disabled if Addr is empty.
windows.LDAPConfig
// PKIDomain optionally configures a separate Active Directory domain
// for PKI operations. If empty, the domain from the LDAP config is used.
// This can be useful for cases where PKI is configured in a root domain
// but Teleport is used to provide access to users and computers in a child
// domain.
PKIDomain string
// DiscoveryBaseDN is the base DN for searching for Windows Desktops.
// Desktop discovery is disabled if this field is empty.
DiscoveryBaseDN string
Expand Down Expand Up @@ -352,15 +358,21 @@ func NewWindowsService(cfg WindowsServiceConfig) (*WindowsService, error) {
auditCache: newSharedDirectoryAuditCache(),
}

caLDAPConfig := s.cfg.LDAPConfig
if s.cfg.PKIDomain != "" {
caLDAPConfig.Domain = s.cfg.PKIDomain
}
s.cfg.Log.Infof("Windows PKI will be performed against %v", s.cfg.PKIDomain)

s.ca = windows.NewCertificateStoreClient(windows.CertificateStoreConfig{
AccessPoint: s.cfg.AccessPoint,
LDAPConfig: s.cfg.LDAPConfig,
LDAPConfig: caLDAPConfig,
Log: s.cfg.Log,
ClusterName: s.clusterName,
LC: s.lc,
})

if s.cfg.LDAPConfig.Addr != "" {
if caLDAPConfig.Addr != "" {
s.ldapConfigured = true
// initialize LDAP - if this fails it will automatically schedule a retry.
// we don't want to return an error in this case, because failure to start
Expand Down Expand Up @@ -1196,14 +1208,22 @@ type generateCredentialsRequest struct {
// Directory. See:
// https://docs.microsoft.com/en-us/windows/security/identity-protection/smart-cards/smart-card-certificate-requirements-and-enumeration
func (s *WindowsService) generateCredentials(ctx context.Context, request generateCredentialsRequest) (certDER, keyDER []byte, err error) {
// If PKI domain has been overridden, make sure we pass that through
// to the cert request, otherwise the CRL in the cert we issue will
// point at the wrong domain.
lc := s.cfg.LDAPConfig
if s.cfg.PKIDomain != "" {
lc.Domain = s.cfg.PKIDomain
}

return windows.GenerateWindowsDesktopCredentials(ctx, &windows.GenerateCredentialsRequest{
CAType: types.UserCA,
Username: request.username,
Domain: request.domain,
TTL: request.ttl,
ClusterName: s.clusterName,
ActiveDirectorySID: request.activeDirectorySID,
LDAPConfig: s.cfg.LDAPConfig,
LDAPConfig: lc,
AuthClient: s.cfg.AuthClient,
CreateUser: request.createUser,
Groups: request.groups,
Expand Down
36 changes: 24 additions & 12 deletions lib/srv/desktop/windows_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,32 +123,45 @@ func TestGenerateCredentials(t *testing.T) {
require.NoError(t, client.Close())
})

w := &WindowsService{
clusterName: clusterName,
cfg: WindowsServiceConfig{
LDAPConfig: windows.LDAPConfig{
Domain: domain,
},
AuthClient: client,
},
}

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

for _, test := range []struct {
name string
activeDirectorySID string
cdp string
configure func(*WindowsServiceConfig)
}{
{
name: "no ad sid",
activeDirectorySID: "",
cdp: `ldap:///CN=test,CN=Teleport,CN=CDP,CN=Public Key Services,CN=Services,CN=Configuration,DC=test,DC=example,DC=com?certificateRevocationList?base?objectClass=cRLDistributionPoint`,
},
{
name: "with ad sid",
activeDirectorySID: testSid,
cdp: `ldap:///CN=test,CN=Teleport,CN=CDP,CN=Public Key Services,CN=Services,CN=Configuration,DC=test,DC=example,DC=com?certificateRevocationList?base?objectClass=cRLDistributionPoint`,
},
{
name: "separate PKI domain",
activeDirectorySID: "",
configure: func(cfg *WindowsServiceConfig) { cfg.PKIDomain = "pki.example.com" },
cdp: `ldap:///CN=test,CN=Teleport,CN=CDP,CN=Public Key Services,CN=Services,CN=Configuration,DC=pki,DC=example,DC=com?certificateRevocationList?base?objectClass=cRLDistributionPoint`,
},
} {
w := &WindowsService{
clusterName: clusterName,
cfg: WindowsServiceConfig{
LDAPConfig: windows.LDAPConfig{
Domain: domain,
},
AuthClient: client,
},
}
if test.configure != nil {
test.configure(&w.cfg)
}

certb, keyb, err := w.generateCredentials(ctx, generateCredentialsRequest{
username: user,
domain: domain,
Expand All @@ -164,8 +177,7 @@ func TestGenerateCredentials(t *testing.T) {
require.NotNil(t, cert)

require.Equal(t, user, cert.Subject.CommonName)
require.Contains(t, cert.CRLDistributionPoints,
`ldap:///CN=test,CN=Teleport,CN=CDP,CN=Public Key Services,CN=Services,CN=Configuration,DC=test,DC=example,DC=com?certificateRevocationList?base?objectClass=cRLDistributionPoint`)
require.ElementsMatch(t, cert.CRLDistributionPoints, []string{test.cdp})

foundKeyUsage := false
foundAltName := false
Expand Down