ldap: Add user DN attributes list config param (#19758)

This change uses the updated ldap library in minio/pkg (bumped
up to v3). A new config parameter is added for LDAP configuration to
specify extra user attributes to load from the LDAP server and to store
them as additional claims for the user.

A test is added in sts_handlers.go that shows how to access the LDAP
attributes as a claim.

This is in preparation for adding SSH pubkey authentication to MinIO's SFTP
integration.
This commit is contained in:
Aditya Manthramurthy
2024-05-24 16:05:23 -07:00
committed by GitHub
parent a591e06ae5
commit 5f78691fcf
179 changed files with 524 additions and 362 deletions

View File

@@ -33,7 +33,7 @@ import (
minio "github.com/minio/minio-go/v7"
cr "github.com/minio/minio-go/v7/pkg/credentials"
"github.com/minio/minio-go/v7/pkg/set"
ldap "github.com/minio/pkg/v2/ldap"
ldap "github.com/minio/pkg/v3/ldap"
"golang.org/x/exp/slices"
)
@@ -656,6 +656,7 @@ func (s *TestSuiteIAM) SetUpLDAP(c *check, serverAddr string) {
"lookup_bind_password=admin",
"user_dn_search_base_dn=dc=min,dc=io",
"user_dn_search_filter=(uid=%s)",
"user_dn_attributes=sshPublicKey",
"group_search_base_dn=ou=swengg,dc=min,dc=io",
"group_search_filter=(&(objectclass=groupofnames)(member=%d))",
}
@@ -721,6 +722,7 @@ func TestIAMWithLDAPServerSuite(t *testing.T) {
suite.TestLDAPSTSServiceAccounts(c)
suite.TestLDAPSTSServiceAccountsWithUsername(c)
suite.TestLDAPSTSServiceAccountsWithGroups(c)
suite.TestLDAPAttributesLookup(c)
suite.TearDownSuite(c)
},
)
@@ -1870,6 +1872,91 @@ func (s *TestSuiteIAM) TestLDAPSTSServiceAccountsWithGroups(c *check) {
c.mustNotCreateSvcAccount(ctx, globalActiveCred.AccessKey, userAdmClient)
}
func (s *TestSuiteIAM) TestLDAPAttributesLookup(c *check) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
groupDN := "cn=projectb,ou=groups,ou=swengg,dc=min,dc=io"
_, err := s.adm.AttachPolicyLDAP(ctx, madmin.PolicyAssociationReq{
Policies: []string{"readwrite"},
Group: groupDN,
})
if err != nil {
c.Fatalf("Unable to set policy: %v", err)
}
cases := []struct {
username string
dn string
expectedSSHKeyType string
}{
{
username: "dillon",
dn: "uid=dillon,ou=people,ou=swengg,dc=min,dc=io",
expectedSSHKeyType: "ssh-ed25519",
},
{
username: "liza",
dn: "uid=liza,ou=people,ou=swengg,dc=min,dc=io",
expectedSSHKeyType: "ssh-rsa",
},
}
conn, err := globalIAMSys.LDAPConfig.LDAP.Connect()
if err != nil {
c.Fatalf("LDAP connect failed: %v", err)
}
defer conn.Close()
for i, testCase := range cases {
ldapID := cr.LDAPIdentity{
Client: s.TestSuiteCommon.client,
STSEndpoint: s.endPoint,
LDAPUsername: testCase.username,
LDAPPassword: testCase.username,
}
value, err := ldapID.Retrieve()
if err != nil {
c.Fatalf("Expected to generate STS creds, got err: %#v", err)
}
// Retrieve the STS account's credential object.
u, ok := globalIAMSys.GetUser(ctx, value.AccessKeyID)
if !ok {
c.Fatalf("Expected to find user %s", value.AccessKeyID)
}
if u.Credentials.AccessKey != value.AccessKeyID {
c.Fatalf("Expected access key %s, got %s", value.AccessKeyID, u.Credentials.AccessKey)
}
// Retrieve the credential's claims.
secret, err := getTokenSigningKey()
if err != nil {
c.Fatalf("Error getting token signing key: %v", err)
}
claims, err := getClaimsFromTokenWithSecret(value.SessionToken, secret)
if err != nil {
c.Fatalf("Error getting claims from token: %v", err)
}
// Validate claims. Check if the sshPublicKey claim is present.
dnClaim := claims[ldapUser].(string)
if dnClaim != testCase.dn {
c.Fatalf("Test %d: unexpected dn claim: %s", i+1, dnClaim)
}
sshPublicKeyClaim := claims[ldapAttribPrefix+"sshPublicKey"].([]interface{})[0].(string)
if sshPublicKeyClaim == "" {
c.Fatalf("Test %d: expected sshPublicKey claim to be present", i+1)
}
parts := strings.Split(sshPublicKeyClaim, " ")
if parts[0] != testCase.expectedSSHKeyType {
c.Fatalf("Test %d: unexpected sshPublicKey type: %s", i+1, parts[0])
}
}
}
func (s *TestSuiteIAM) TestOpenIDSTS(c *check) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()