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
4 changes: 4 additions & 0 deletions api/proto/teleport/legacy/types/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,10 @@ message AD {
string LDAPCert = 5 [(gogoproto.jsontag) = "ldap_cert,omitempty"];
// KDCHostName is the host name for a KDC for x509 Authentication.
string KDCHostName = 6 [(gogoproto.jsontag) = "kdc_host_name,omitempty"];
// LDAPServiceAccountName is the name of service account for performing LDAP queries. Required for x509 Auth / PKINIT.
string LDAPServiceAccountName = 7 [(gogoproto.jsontag) = "ldap_service_account_name,omitempty"];
// LDAPServiceAccountSID is the SID of service account for performing LDAP queries. Required for x509 Auth / PKINIT.
string LDAPServiceAccountSID = 8 [(gogoproto.jsontag) = "ldap_service_account_sid,omitempty"];
}

// DatabaseTLSMode represents the level of TLS verification performed by
Expand Down
4 changes: 3 additions & 1 deletion api/types/derived.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4,067 changes: 2,080 additions & 1,987 deletions api/types/types.pb.go

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Optional:
- `keytab_file` (String) KeytabFile is the path to the Kerberos keytab file.
- `krb5_file` (String) Krb5File is the path to the Kerberos configuration file. Defaults to /etc/krb5.conf.
- `ldap_cert` (String) LDAPCert is a certificate from Windows LDAP/AD, optional; only for x509 Authentication.
- `ldap_service_account_name` (String) LDAPServiceAccountName is the name of service account for performing LDAP queries. Required for x509 Auth / PKINIT.
- `ldap_service_account_sid` (String) LDAPServiceAccountSID is the SID of service account for performing LDAP queries. Required for x509 Auth / PKINIT.
- `spn` (String) SPN is the service principal name for the database.


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ Optional:
- `keytab_file` (String) KeytabFile is the path to the Kerberos keytab file.
- `krb5_file` (String) Krb5File is the path to the Kerberos configuration file. Defaults to /etc/krb5.conf.
- `ldap_cert` (String) LDAPCert is a certificate from Windows LDAP/AD, optional; only for x509 Authentication.
- `ldap_service_account_name` (String) LDAPServiceAccountName is the name of service account for performing LDAP queries. Required for x509 Auth / PKINIT.
- `ldap_service_account_sid` (String) LDAPServiceAccountSID is the SID of service account for performing LDAP queries. Required for x509 Auth / PKINIT.
- `spn` (String) SPN is the service principal name for the database.


Expand Down
88 changes: 88 additions & 0 deletions integrations/terraform/tfschema/types_terraform.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 11 additions & 3 deletions lib/auth/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"encoding/base64"
"encoding/pem"
"fmt"
"log/slog"
"strings"
"time"

Expand Down Expand Up @@ -158,7 +159,7 @@ func (a *Server) generateDatabaseCert(ctx context.Context, req *proto.DatabaseCe
// Pass through ExtKeyUsage (which we need for Smartcard Logon usage)
// and SubjectAltName (which we need for otherName SAN, not supported
// out of the box in crypto/x509) extensions only.
certReq.ExtraExtensions = filterExtensions(csr.Extensions, oidExtKeyUsage, oidSubjectAltName)
certReq.ExtraExtensions = filterExtensions(a.CloseContext(), a.logger, csr.Extensions, oidExtKeyUsage, oidSubjectAltName, oidADUserMapping)
certReq.KeyUsage = x509.KeyUsageDigitalSignature
// CRL is required for Windows smartcard certs.
certReq.CRLDistributionPoints = []string{req.CRLEndpoint}
Expand Down Expand Up @@ -396,14 +397,20 @@ func getSnowflakeJWTParams(accountName, userName string, publicKey []byte) (stri
return subject, issuer
}

func filterExtensions(extensions []pkix.Extension, oids ...asn1.ObjectIdentifier) []pkix.Extension {
func filterExtensions(ctx context.Context, logger *slog.Logger, extensions []pkix.Extension, oids ...asn1.ObjectIdentifier) []pkix.Extension {
filtered := make([]pkix.Extension, 0, len(oids))
for _, e := range extensions {
matched := false
for _, id := range oids {
if e.Id.Equal(id) {
filtered = append(filtered, e)
matched = true
}
}
if matched {
filtered = append(filtered, e)
} else {
logger.WarnContext(ctx, "filtering out unexpected certificate extension; this may indicate Teleport bug", "oid", e.Id.String(), "value", e.Value, "critical", e.Critical)
}
}
return filtered
}
Expand All @@ -412,6 +419,7 @@ func filterExtensions(extensions []pkix.Extension, oids ...asn1.ObjectIdentifier
var (
oidExtKeyUsage = asn1.ObjectIdentifier{2, 5, 29, 37}
oidSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17}
oidADUserMapping = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 25, 2}

oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1}
oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2}
Expand Down
42 changes: 42 additions & 0 deletions lib/auth/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"context"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"log/slog"
"testing"
"time"

Expand Down Expand Up @@ -224,3 +226,43 @@ func mustVerifyCert(t *testing.T, rootPEM, leafPEM []byte, keyUsages ...x509.Ext
_, err = leafCert.Verify(opts)
require.NoError(t, err)
}

func TestFilterExtensions(t *testing.T) {
oidA := asn1.ObjectIdentifier{1, 2, 3, 4}
oidB := asn1.ObjectIdentifier{1, 2, 3, 5}
extA := pkix.Extension{Id: oidA, Value: []byte("a")}
extB := pkix.Extension{Id: oidB, Value: []byte("b")}

tests := []struct {
name string
input []pkix.Extension
allowedOIDs []asn1.ObjectIdentifier
expected []pkix.Extension
}{
{
name: "keeps allowed extension",
input: []pkix.Extension{extA},
allowedOIDs: []asn1.ObjectIdentifier{oidA},
expected: []pkix.Extension{extA},
},
{
name: "filters disallowed extension",
input: []pkix.Extension{extA},
allowedOIDs: []asn1.ObjectIdentifier{oidB},
expected: []pkix.Extension{},
},
{
name: "keeps only allowed extension",
input: []pkix.Extension{extA, extB},
allowedOIDs: []asn1.ObjectIdentifier{oidA},
expected: []pkix.Extension{extA},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := filterExtensions(context.Background(), slog.Default(), tt.input, tt.allowedOIDs...)
require.Equal(t, tt.expected, got)
})
}
}
24 changes: 14 additions & 10 deletions lib/auth/windows/ldap.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,20 @@ import (
type LDAPConfig struct {
// Addr is the LDAP server address in the form host:port.
// Standard port is 636 for LDAPS.
Addr string //nolint:unused // False-positive
Addr string
// Domain is an Active Directory domain name, like "example.com".
Domain string //nolint:unused // False-positive
Domain string
// Username is an LDAP username, like "EXAMPLE\Administrator", where
// "EXAMPLE" is the NetBIOS version of Domain.
Username string //nolint:unused // False-positive
Username string
// SID is the SID for the user specified by Username.
SID string //nolint:unused // False-positive
SID string
// InsecureSkipVerify decides whether we skip verifying with the LDAP server's CA when making the LDAPS connection.
InsecureSkipVerify bool //nolint:unused // False-positive
InsecureSkipVerify bool
// ServerName is the name of the LDAP server for TLS.
ServerName string //nolint:unused // False-positive
ServerName string
// CA is an optional CA cert to be used for verification if InsecureSkipVerify is set to false.
CA *x509.Certificate //nolint:unused // False-positive
CA *x509.Certificate
}

// Check verifies this LDAPConfig
Expand Down Expand Up @@ -100,13 +100,17 @@ const searchPageSize = 1000
// is closed. Callers should check for trace.ConnectionProblem errors
// and provide a new client with [SetClient].
type LDAPClient struct {
// Cfg is the LDAPConfig
Cfg LDAPConfig

mu sync.Mutex
client ldap.Client
}

// NewLDAPClient returns new LDAPClient. Parameter client may be nil.
func NewLDAPClient(client ldap.Client) *LDAPClient {
return &LDAPClient{
client: client,
}
}

// SetClient sets the underlying ldap.Client
func (c *LDAPClient) SetClient(client ldap.Client) {
c.mu.Lock()
Expand Down
6 changes: 6 additions & 0 deletions lib/auth/windows/windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ func GenerateWindowsDesktopCredentials(ctx context.Context, req *GenerateCredent
return nil, nil, trace.Wrap(err)
}
certBlock, _ := pem.Decode(genResp.Cert)
if certBlock == nil {
return nil, nil, trace.BadParameter("failed to decode certificate")
}
certDER = certBlock.Bytes
keyDER = certReq.keyDER
return certDER, keyDER, nil
Expand Down Expand Up @@ -260,6 +263,9 @@ func generateDatabaseCredentials(ctx context.Context, req *GenerateCredentialsRe
return nil, nil, nil, trace.Wrap(err)
}
certBlock, _ := pem.Decode(genResp.Cert)
if certBlock == nil {
return nil, nil, nil, trace.BadParameter("failed to decode certificate")
}
certDER = certBlock.Bytes
keyDER = certReq.keyDER
return certDER, keyDER, genResp.CACerts, nil
Expand Down
14 changes: 8 additions & 6 deletions lib/config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -1955,12 +1955,14 @@ func applyDatabasesConfig(fc *FileConfig, cfg *servicecfg.Config) error {
InstanceID: database.GCP.InstanceID,
},
AD: servicecfg.DatabaseAD{
KeytabFile: database.AD.KeytabFile,
Krb5File: database.AD.Krb5File,
Domain: database.AD.Domain,
SPN: database.AD.SPN,
LDAPCert: database.AD.LDAPCert,
KDCHostName: database.AD.KDCHostName,
KeytabFile: database.AD.KeytabFile,
Krb5File: database.AD.Krb5File,
Domain: database.AD.Domain,
SPN: database.AD.SPN,
LDAPCert: database.AD.LDAPCert,
KDCHostName: database.AD.KDCHostName,
LDAPServiceAccountName: database.AD.LDAPServiceAccountName,
LDAPServiceAccountSID: database.AD.LDAPServiceAccountSID,
},
Azure: servicecfg.DatabaseAzure{
ResourceID: database.Azure.ResourceID,
Expand Down
4 changes: 4 additions & 0 deletions lib/config/fileconf.go
Original file line number Diff line number Diff line change
Expand Up @@ -1931,6 +1931,10 @@ type DatabaseAD struct {
LDAPCert string `yaml:"ldap_cert,omitempty"`
// KDCHostName is the host name for a KDC for x509 Authentication.
KDCHostName string `yaml:"kdc_host_name,omitempty"`
// LDAPServiceAccountName is the name of service account for performing LDAP queries. Required for x509 Auth / PKINIT.
LDAPServiceAccountName string `yaml:"ldap_service_account_name,omitempty"`
// LDAPServiceAccountSID is the SID of service account for performing LDAP queries. Required for x509 Auth / PKINIT.
LDAPServiceAccountSID string `yaml:"ldap_service_account_sid,omitempty"`
}

// DatabaseTLS keeps TLS settings used when connecting to database.
Expand Down
8 changes: 4 additions & 4 deletions lib/service/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ func (process *TeleportProcess) initDatabaseService() (retErr error) {

// Create database resources from databases defined in the static configuration.
var databases types.Databases
for _, db := range process.Config.Databases.Databases {
db, err := db.ToDatabase()
for _, dbSpec := range process.Config.Databases.Databases {
database, err := dbSpec.ToDatabase()
if err != nil {
return trace.Wrap(err)
}
if err := services.ValidateDatabase(db); err != nil {
if err := services.ValidateDatabase(database); err != nil {
return trace.Wrap(err)
}
databases = append(databases, db)
databases = append(databases, database)
}

lockWatcher, err := services.NewLockWatcher(process.ExitContext(), services.LockWatcherConfig{
Expand Down
Loading
Loading