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
8 changes: 8 additions & 0 deletions builtin/credential/ldap/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri
return nil, logical.ErrorResponse(err.Error()), nil, nil
}

if cfg.AnonymousGroupSearch {
c, err = ldapClient.DialLDAP(cfg.ConfigEntry)
if err != nil {
return nil, logical.ErrorResponse("ldap operation failed"), nil, nil
}
defer c.Close() // Defer closing of this connection as the deferal above closes the other defined connection
}

ldapGroups, err := ldapClient.GetLdapGroups(cfg.ConfigEntry, c, userDN, username)
if err != nil {
return nil, logical.ErrorResponse(err.Error()), nil, nil
Expand Down
9 changes: 9 additions & 0 deletions sdk/helper/ldaputil/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,5 +489,14 @@ func getTLSConfig(cfg *ConfigEntry, host string) (*tls.Config, error) {
}
tlsConfig.RootCAs = caPool
}
if cfg.ClientTLSCert != "" && cfg.ClientTLSKey != "" {
certificate, err := tls.X509KeyPair([]byte(cfg.ClientTLSCert), []byte(cfg.ClientTLSKey))
if err != nil {
return nil, errwrap.Wrapf("failed to parse client X509 key pair: {{err}}", err)
}
tlsConfig.Certificates = append(tlsConfig.Certificates, certificate)
} else if cfg.ClientTLSCert != "" || cfg.ClientTLSKey != "" {
return nil, fmt.Errorf("both client_tls_cert and client_tls_key must be set")
}
return tlsConfig, nil
}
124 changes: 93 additions & 31 deletions sdk/helper/ldaputil/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ldaputil

import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
Expand All @@ -18,6 +19,14 @@ import (
// Not all fields will be used by every integration.
func ConfigFields() map[string]*framework.FieldSchema {
return map[string]*framework.FieldSchema{
"anonymous_group_search": {
Type: framework.TypeBool,
Default: false,
Description: "Use anonymous binds when performing LDAP group searches (if true the initial credentials will still be used for the initial connection test).",
DisplayAttrs: &framework.DisplayAttributes{
Name: "Anonymous group search",
},
},
"url": {
Type: framework.TypeString,
Default: "ldap://127.0.0.1",
Expand Down Expand Up @@ -105,6 +114,28 @@ Default: cn`,
"certificate": {
Type: framework.TypeString,
Description: "CA certificate to use when verifying LDAP server certificate, must be x509 PEM encoded (optional)",
DisplayAttrs: &framework.DisplayAttributes{
Name: "CA certificate",
EditType: "file",
},
},

"client_tls_cert": {
Type: framework.TypeString,
Description: "Client certificate to provide to the LDAP server, must be x509 PEM encoded (optional)",
DisplayAttrs: &framework.DisplayAttributes{
Name: "Client certificate",
EditType: "file",
},
},

"client_tls_key": {
Type: framework.TypeString,
Description: "Client certificate key to provide to the LDAP server, must be x509 PEM encoded (optional)",
DisplayAttrs: &framework.DisplayAttributes{
Name: "Client key",
EditType: "file",
},
},

"discoverdn": {
Expand Down Expand Up @@ -196,6 +227,10 @@ func NewConfigEntry(existing *ConfigEntry, d *framework.FieldData) (*ConfigEntry
cfg = new(ConfigEntry)
}

if _, ok := d.Raw["anonymous_group_search"]; ok || !hadExisting {
cfg.AnonymousGroupSearch = d.Get("anonymous_group_search").(bool)
}

if _, ok := d.Raw["url"]; ok || !hadExisting {
cfg.Url = strings.ToLower(d.Get("url").(string))
}
Expand Down Expand Up @@ -236,20 +271,31 @@ func NewConfigEntry(existing *ConfigEntry, d *framework.FieldData) (*ConfigEntry
if _, ok := d.Raw["certificate"]; ok || !hadExisting {
certificate := d.Get("certificate").(string)
if certificate != "" {
block, _ := pem.Decode([]byte(certificate))

if block == nil || block.Type != "CERTIFICATE" {
return nil, errors.New("failed to decode PEM block in the certificate")
}
_, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, errwrap.Wrapf("failed to parse certificate: {{err}}", err)
if err := validateCertificate([]byte(certificate)); err != nil {
return nil, errwrap.Wrapf("failed to parse server tls cert: {{err}}", err)
}
}

cfg.Certificate = certificate
}

if _, ok := d.Raw["client_tls_cert"]; ok || !hadExisting {
clientTLSCert := d.Get("client_tls_cert").(string)
cfg.ClientTLSCert = clientTLSCert
}

if _, ok := d.Raw["client_tls_key"]; ok || !hadExisting {
clientTLSKey := d.Get("client_tls_key").(string)
cfg.ClientTLSKey = clientTLSKey
}

if cfg.ClientTLSCert != "" && cfg.ClientTLSKey != "" {
if _, err := tls.X509KeyPair([]byte(cfg.ClientTLSCert), []byte(cfg.ClientTLSKey)); err != nil {
return nil, errwrap.Wrapf("failed to parse client X509 key pair: {{err}}", err)
}
} else if cfg.ClientTLSCert != "" || cfg.ClientTLSKey != "" {
return nil, fmt.Errorf("both client_tls_cert and client_tls_key must be set")
}

if _, ok := d.Raw["insecure_tls"]; ok || !hadExisting {
cfg.InsecureTLS = d.Get("insecure_tls").(bool)
}
Expand Down Expand Up @@ -318,12 +364,15 @@ func NewConfigEntry(existing *ConfigEntry, d *framework.FieldData) (*ConfigEntry
type ConfigEntry struct {
Url string `json:"url"`
UserDN string `json:"userdn"`
AnonymousGroupSearch bool `json:"anonymous_group_search"`
GroupDN string `json:"groupdn"`
GroupFilter string `json:"groupfilter"`
GroupAttr string `json:"groupattr"`
UPNDomain string `json:"upndomain"`
UserAttr string `json:"userattr"`
Certificate string `json:"certificate"`
ClientTLSCert string `json:"client_tls_cert`
ClientTLSKey string `json:"client_tls_key`
InsecureTLS bool `json:"insecure_tls"`
StartTLS bool `json:"starttls"`
BindDN string `json:"binddn"`
Expand Down Expand Up @@ -351,22 +400,23 @@ func (c *ConfigEntry) Map() map[string]interface{} {

func (c *ConfigEntry) PasswordlessMap() map[string]interface{} {
m := map[string]interface{}{
"url": c.Url,
"userdn": c.UserDN,
"groupdn": c.GroupDN,
"groupfilter": c.GroupFilter,
"groupattr": c.GroupAttr,
"upndomain": c.UPNDomain,
"userattr": c.UserAttr,
"certificate": c.Certificate,
"insecure_tls": c.InsecureTLS,
"starttls": c.StartTLS,
"binddn": c.BindDN,
"deny_null_bind": c.DenyNullBind,
"discoverdn": c.DiscoverDN,
"tls_min_version": c.TLSMinVersion,
"tls_max_version": c.TLSMaxVersion,
"use_token_groups": c.UseTokenGroups,
"url": c.Url,
"userdn": c.UserDN,
"groupdn": c.GroupDN,
"groupfilter": c.GroupFilter,
"groupattr": c.GroupAttr,
"upndomain": c.UPNDomain,
"userattr": c.UserAttr,
"certificate": c.Certificate,
"insecure_tls": c.InsecureTLS,
"starttls": c.StartTLS,
"binddn": c.BindDN,
"deny_null_bind": c.DenyNullBind,
"discoverdn": c.DiscoverDN,
"tls_min_version": c.TLSMinVersion,
"tls_max_version": c.TLSMaxVersion,
"use_token_groups": c.UseTokenGroups,
"anonymous_group_search": c.AnonymousGroupSearch,
}
if c.CaseSensitiveNames != nil {
m["case_sensitive_names"] = *c.CaseSensitiveNames
Expand All @@ -377,6 +427,18 @@ func (c *ConfigEntry) PasswordlessMap() map[string]interface{} {
return m
}

func validateCertificate(pemBlock []byte) error {
block, _ := pem.Decode([]byte(pemBlock))
if block == nil || block.Type != "CERTIFICATE" {
return errors.New("failed to decode PEM block in the certificate")
}
_, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return fmt.Errorf("failed to parse certificate %s", err.Error())
}
return nil
}

func (c *ConfigEntry) Validate() error {
if len(c.Url) == 0 {
return errors.New("at least one url must be provided")
Expand All @@ -398,13 +460,13 @@ func (c *ConfigEntry) Validate() error {
return errors.New("'tls_max_version' must be greater than or equal to 'tls_min_version'")
}
if c.Certificate != "" {
block, _ := pem.Decode([]byte(c.Certificate))
if block == nil || block.Type != "CERTIFICATE" {
return errors.New("failed to decode PEM block in the certificate")
if err := validateCertificate([]byte(c.Certificate)); err != nil {
return errwrap.Wrapf("failed to parse server tls cert: {{err}}", err)
}
_, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return fmt.Errorf("failed to parse certificate %s", err.Error())
}
if c.ClientTLSCert != "" && c.ClientTLSKey != "" {
if _, err := tls.X509KeyPair([]byte(c.ClientTLSCert), []byte(c.ClientTLSKey)); err != nil {
return errwrap.Wrapf("failed to parse client X509 key pair: {{err}}", err)
}
}
return nil
Expand Down
3 changes: 3 additions & 0 deletions ui/app/models/auth-config/ldap.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ export default AuthConfig.extend({
'tlsMinVersion',
'tlsMaxVersion',
'certificate',
'clientTlsCert',
'clientTlsKey',
'userattr',
'upndomain',
'anonymousGroupSearch',
],
},
{
Expand Down

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

Loading