diff options
3 files changed, 549 insertions, 0 deletions
diff --git a/sys-auth/docker_auth/docker_auth-1.3-r1.ebuild b/sys-auth/docker_auth/docker_auth-1.3-r1.ebuild new file mode 100644 index 000000000000..ebeaf7fa15ef --- /dev/null +++ b/sys-auth/docker_auth/docker_auth-1.3-r1.ebuild @@ -0,0 +1,88 @@ +# Copyright 1999-2017 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +EAPI=6 +EGO_PN="github.com/cesanta/docker_auth" + +EGO_VENDOR=( + "github.com/dchest/uniuri 8902c56451e9b58ff940bbe5fec35d5f9c04584a" + "github.com/deckarep/golang-set fc8930a5e645572ee00bf66358ed3414f3c13b90" + "github.com/docker/distribution 0700fa570d7bcc1b3e46ee127c4489fd25f4daa3" + "github.com/docker/libtrust aabc10ec26b754e797f9028f4589c5b7bd90dc20" + "github.com/facebookgo/httpdown a3b1354551a26449fbe05f5d855937f6e7acbd71" + "github.com/facebookgo/clock 600d898af40aa09a7a93ecb9265d87b0504b6f03" + "github.com/facebookgo/stats 1b76add642e42c6ffba7211ad7b3939ce654526e" + "github.com/go-ldap/ldap 13cedcf58a1ea124045dea529a66c849d3444c8e" + "github.com/cesanta/glog 22eb27a0ae192b290b25537b8e876556fc25129c" + "github.com/schwarmco/go-cartesian-product c2c0aca869a6cbf51e017ce148b949d9dee09bc3" + "github.com/syndtr/goleveldb 3c5717caf1475fd25964109a0fc640bd150fce43" + "github.com/golang/snappy 553a641470496b2327abcac10b36396bd98e45c9" + "gopkg.in/asn1-ber.v1 4e86f4367175e39f69d9358a5f17b4dda270378d github.com/go-asn1-ber/asn1-ber" + "gopkg.in/fsnotify.v1 629574ca2a5df945712d3079857300b5e4da0236 github.com/fsnotify/fsnotify" + "gopkg.in/mgo.v2 3f83fa5005286a7fe593b055f0d7771a7dce4655 github.com/go-mgo/mgo" + "gopkg.in/yaml.v2 a3f3340b5840cee44f372bddb5880fcbc419b46a github.com/go-yaml/yaml" + "golang.org/x/crypto e1a4589e7d3ea14a3352255d04b6f1a418845e5e github.com/golang/crypto" + "golang.org/x/sys 493114f68206f85e7e333beccfabc11e98cba8dd github.com/golang/sys" + "golang.org/x/net 859d1a86bb617c0c20d154590c3c5d3fcb670b07 github.com/golang/net" + "google.golang.org/api 39c3dd417c5a443607650f18e829ad308da08dd2 github.com/google/google-api-go-client" + "google.golang.org/grpc 35170916ff58e89ae03f52e778228e18207e0e02 github.com/grpc/grpc-go" + "github.com/golang/protobuf 11b8df160996e00fd4b55cbaafb3d84ec6d50fa8" + "golang.org/x/oauth2 13449ad91cb26cb47661c1b080790392170385fd github.com/golang/oauth2" + "cloud.google.com/go bbf380d59635bf267fc8a8df03d6d261c448ee3c github.com/GoogleCloudPlatform/gcloud-golang" + "golang.org/x/text ab5ac5f9a8deb4855a60fab02bc61a4ec770bd49 github.com/golang/text" + "github.com/googleapis/gax-go 8c160ca1523d8eea3932fbaa494c8964b7724aa8" + "google.golang.org/genproto 595979c8a7bf586b2d293fb42246bf91a0b893d9 github.com/google/go-genproto" + ) + +inherit user golang-build golang-vcs-snapshot +EGIT_COMMIT="2cd3699dab4e44a239db136a52734dab25897bee" +SHORT_COMMIT=${EGIT_COMMIT:0:7} +SRC_URI="https://${EGO_PN}/archive/${PV}.tar.gz -> ${P}.tar.gz + ${EGO_VENDOR_URI}" +KEYWORDS="~amd64" + +DESCRIPTION="Docker Registry 2 authentication server" +HOMEPAGE="https://github.com/cesanta/docker_auth" + +LICENSE="Apache-2.0" +SLOT="0" +IUSE="" + +RESTRICT="test" + +pkg_setup() { + enewgroup ${PN} + enewuser ${PN} -1 -1 /dev/null ${PN} +} + +src_prepare() { + default + pushd src/${EGO_PN} + eapply "${FILESDIR}/${PN}-ldap-group-support-1.patch" + eapply "${FILESDIR}/${PN}-ldap-cacert.patch" + cp "${FILESDIR}/version.go" auth_server/version.go || die + sed -i -e "s/{version}/${PV}/" -e "s/{build_id}/${SHORT_COMMIT}/" auth_server/version.go || die + popd || die +} + +src_compile() { + pushd src/${EGO_PN}/auth_server || die + GOPATH="${WORKDIR}/${P}" go build -o "bin/auth_server" || die + popd || die +} + +src_install() { + pushd src/${EGO_PN} || die + dodoc README.md docs/Backend_MongoDB.md + insinto /usr/share/${PF} + doins -r examples + insinto /etc/docker_auth/ + newins examples/reference.yml config.yml.example + dobin auth_server/bin/auth_server + popd || die + newinitd "${FILESDIR}"/${PN}.initd ${PN} + newconfd "${FILESDIR}"/${PN}.confd ${PN} + insinto /etc/logrotate.d + newins "${FILESDIR}"/${PN}.logrotated ${PN} + keepdir /var/log/docker_auth +} diff --git a/sys-auth/docker_auth/files/docker_auth-ldap-cacert.patch b/sys-auth/docker_auth/files/docker_auth-ldap-cacert.patch new file mode 100644 index 000000000000..e43e9e6ca889 --- /dev/null +++ b/sys-auth/docker_auth/files/docker_auth-ldap-cacert.patch @@ -0,0 +1,67 @@ +From 5505de31a91aea88e0cf623ec8edfd928b5432a7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Manuel=20R=C3=BCger?= <mrueg@gentoo.org> +Date: Mon, 18 Sep 2017 14:02:38 +0200 +Subject: [PATCH] Set custom CA certificate for ldap cert verification + +Code taken from: https://github.com/hashicorp/go-rootcerts/blob/master/rootcerts.go +Original author: Paul Hinze <phinze@phinze.com> +--- + auth_server/authn/ldap_auth.go | 17 ++++++++++++++++- + examples/reference.yml | 2 ++ + 2 files changed, 18 insertions(+), 1 deletion(-) + +diff --git a/auth_server/authn/ldap_auth.go b/auth_server/authn/ldap_auth.go +index 3bdf7c3..a3425ed 100644 +--- a/auth_server/authn/ldap_auth.go ++++ b/auth_server/authn/ldap_auth.go +@@ -19,6 +19,7 @@ package authn + import ( + "bytes" + "crypto/tls" ++ "crypto/x509" + "fmt" + "io/ioutil" + "strings" +@@ -31,6 +32,7 @@ type LDAPAuthConfig struct { + Addr string `yaml:"addr,omitempty"` + TLS string `yaml:"tls,omitempty"` + InsecureTLSSkipVerify bool `yaml:"insecure_tls_skip_verify,omitempty"` ++ CACertificate string `yaml:"ca_certificate,omitempty"` + Base string `yaml:"base,omitempty"` + Filter string `yaml:"filter,omitempty"` + BindDN string `yaml:"bind_dn,omitempty"` +@@ -140,7 +142,20 @@ func (la *LDAPAuth) ldapConnection() (*ldap.Conn, error) { + tlsConfig := &tls.Config{InsecureSkipVerify: true} + if !la.config.InsecureTLSSkipVerify { + addr := strings.Split(la.config.Addr, ":") +- tlsConfig = &tls.Config{InsecureSkipVerify: false, ServerName: addr[0]} ++ if la.config.CACertificate != "" { ++ pool := x509.NewCertPool() ++ pem, err := ioutil.ReadFile(la.config.CACertificate) ++ if err != nil { ++ return nil, fmt.Errorf("Error loading CA File: %s", err) ++ } ++ ok := pool.AppendCertsFromPEM(pem) ++ if !ok { ++ return nil, fmt.Errorf("Error loading CA File: Couldn't parse PEM in: %s", la.config.CACertificate) ++ } ++ tlsConfig = &tls.Config{InsecureSkipVerify: false, ServerName: addr[0], RootCAs: pool} ++ } else { ++ tlsConfig = &tls.Config{InsecureSkipVerify: false, ServerName: addr[0]} ++ } + } + + if la.config.TLS == "" || la.config.TLS == "none" || la.config.TLS == "starttls" { +diff --git a/examples/reference.yml b/examples/reference.yml +index 3090978..769cc91 100644 +--- a/examples/reference.yml ++++ b/examples/reference.yml +@@ -131,6 +131,8 @@ ldap_auth: + tls: always + # set to true to allow insecure tls + insecure_tls_skip_verify: false ++ # set this to specify the ca certificate path ++ ca_cert: + # In case bind DN and password is required for querying user information, + # specify them here. Plain text password is read from the file. + bind_dn: diff --git a/sys-auth/docker_auth/files/docker_auth-ldap-group-support-1.patch b/sys-auth/docker_auth/files/docker_auth-ldap-group-support-1.patch new file mode 100644 index 000000000000..f9e98f410c8a --- /dev/null +++ b/sys-auth/docker_auth/files/docker_auth-ldap-group-support-1.patch @@ -0,0 +1,394 @@ +From 4a33badac6b74617dfe3797a716a6907cf018b27 Mon Sep 17 00:00:00 2001 +From: Kevin <kcd83@users.noreply.github.com> +Date: Mon, 27 Feb 2017 19:09:52 +1300 +Subject: [PATCH 1/4] Initial proof of concept mapping memberOf CN to the label + groups #63 + +--- + auth_server/authn/ldap_auth.go | 73 ++++++++++++++++++++++++++++++++++-------- + 1 file changed, 60 insertions(+), 13 deletions(-) + +diff --git a/auth_server/authn/ldap_auth.go b/auth_server/authn/ldap_auth.go +index f8fc08f..42f5ad0 100644 +--- a/auth_server/authn/ldap_auth.go ++++ b/auth_server/authn/ldap_auth.go +@@ -17,7 +17,6 @@ + package authn + + import ( +- "bytes" + "crypto/tls" + "fmt" + "io/ioutil" +@@ -71,10 +70,20 @@ func (la *LDAPAuth) Authenticate(account string, password PasswordString) (bool, + account = la.escapeAccountInput(account) + + filter := la.getFilter(account) +- accountEntryDN, uSearchErr := la.ldapSearch(l, &la.config.Base, &filter, &[]string{}) ++ ++ // dnAndGroupAttr := []string{"DN"} // example of no groups mapping attribute ++ groupAttribute := "memberOf" ++ dnAndGroupAttr := []string{"DN", groupAttribute} ++ ++ entryAttrMap, uSearchErr := la.ldapSearch(l, &la.config.Base, &filter, &dnAndGroupAttr) + if uSearchErr != nil { + return false, nil, uSearchErr + } ++ if len(entryAttrMap) < 1 || entryAttrMap["DN"] == nil || len(entryAttrMap["DN"]) != 1 { ++ return false, nil, NoMatch // User does not exist ++ } ++ ++ accountEntryDN := entryAttrMap["DN"][0] + if accountEntryDN == "" { + return false, nil, NoMatch // User does not exist + } +@@ -93,6 +102,20 @@ func (la *LDAPAuth) Authenticate(account string, password PasswordString) (bool, + return false, nil, bindErr + } + ++ // Extract group names from the attribute values ++ if entryAttrMap[groupAttribute] != nil { ++ rawGroups := entryAttrMap[groupAttribute] ++ labels := make(map[string][]string) ++ var groups []string ++ for _, value := range rawGroups { ++ cn := la.getCNFromDN(value) ++ groups = append(groups, cn) ++ } ++ labels["groups"] = groups ++ ++ return true, labels, nil ++ } ++ + return true, nil, nil + } + +@@ -170,9 +193,9 @@ func (la *LDAPAuth) getFilter(account string) string { + + //ldap search and return required attributes' value from searched entries + //default return entry's DN value if you leave attrs array empty +-func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, attrs *[]string) (string, error) { ++func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, attrs *[]string) (map[string][]string, error) { + if l == nil { +- return "", fmt.Errorf("No ldap connection!") ++ return nil, fmt.Errorf("No ldap connection!") + } + glog.V(2).Infof("Searching...basedDN:%s, filter:%s", *baseDN, *filter) + searchRequest := ldap.NewSearchRequest( +@@ -183,30 +206,54 @@ func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, att + nil) + sr, err := l.Search(searchRequest) + if err != nil { +- return "", err ++ return nil, err + } + + if len(sr.Entries) == 0 { +- return "", nil // User does not exist ++ return nil, nil // User does not exist + } else if len(sr.Entries) > 1 { +- return "", fmt.Errorf("Too many entries returned.") ++ return nil, fmt.Errorf("Too many entries returned.") + } + +- var buffer bytes.Buffer ++ result := make(map[string][]string) + for _, entry := range sr.Entries { ++ + if len(*attrs) == 0 { + glog.V(2).Infof("Entry DN = %s", entry.DN) +- buffer.WriteString(entry.DN) ++ result["DN"] = []string{entry.DN} + } else { + for _, attr := range *attrs { +- values := strings.Join(entry.GetAttributeValues(attr), " ") +- glog.V(2).Infof("Entry %s = %s", attr, values) +- buffer.WriteString(values) ++ var values []string ++ if attr == "DN" { ++ // DN is excluded from attributes ++ values = []string{entry.DN} ++ } else { ++ values = entry.GetAttributeValues(attr) ++ } ++ valuesString := strings.Join(values, "\n") ++ glog.V(2).Infof("Entry %s = %s", attr, valuesString) ++ result[attr] = values ++ } ++ } ++ } ++ ++ return result, nil ++} ++ ++func (la *LDAPAuth) getCNFromDN(dn string) string { ++ parsedDN, err := ldap.ParseDN(dn) ++ if err != nil || len(parsedDN.RDNs) > 0 { ++ for _, rdn := range parsedDN.RDNs { ++ for _, rdnAttr := range rdn.Attributes { ++ if rdnAttr.Type == "CN" { ++ return rdnAttr.Value ++ } + } + } + } + +- return buffer.String(), nil ++ // else try using raw DN ++ return dn + } + + func (la *LDAPAuth) Stop() { + +From ddde2fa779e746d7e74cd972a4c6795c72f17ee6 Mon Sep 17 00:00:00 2001 +From: Kevin <kcd83@users.noreply.github.com> +Date: Tue, 28 Feb 2017 18:09:55 +1300 +Subject: [PATCH 2/4] Apply attribute mapping from configuration + +--- + auth_server/authn/ldap_auth.go | 125 ++++++++++++++++++++++++----------------- + 1 file changed, 74 insertions(+), 51 deletions(-) + +diff --git a/auth_server/authn/ldap_auth.go b/auth_server/authn/ldap_auth.go +index 42f5ad0..6f733a2 100644 +--- a/auth_server/authn/ldap_auth.go ++++ b/auth_server/authn/ldap_auth.go +@@ -26,16 +26,22 @@ import ( + "github.com/golang/glog" + ) + ++type LabelMap struct { ++ Attribute string `yaml:"attribute,omitempty"` ++ ParseCN bool `yaml:"parse_cn,omitempty"` ++} ++ + type LDAPAuthConfig struct { +- Addr string `yaml:"addr,omitempty"` +- TLS string `yaml:"tls,omitempty"` +- InsecureTLSSkipVerify bool `yaml:"insecure_tls_skip_verify,omitempty"` +- Base string `yaml:"base,omitempty"` +- Filter string `yaml:"filter,omitempty"` +- BindDN string `yaml:"bind_dn,omitempty"` +- BindPasswordFile string `yaml:"bind_password_file,omitempty"` +- GroupBaseDN string `yaml:"group_base_dn,omitempty"` +- GroupFilter string `yaml:"group_filter,omitempty"` ++ Addr string `yaml:"addr,omitempty"` ++ TLS string `yaml:"tls,omitempty"` ++ InsecureTLSSkipVerify bool `yaml:"insecure_tls_skip_verify,omitempty"` ++ Base string `yaml:"base,omitempty"` ++ Filter string `yaml:"filter,omitempty"` ++ BindDN string `yaml:"bind_dn,omitempty"` ++ BindPasswordFile string `yaml:"bind_password_file,omitempty"` ++ LabelMaps map[string]LabelMap `yaml:"labels,omitempty"` ++ GroupBaseDN string `yaml:"group_base_dn,omitempty"` ++ GroupFilter string `yaml:"group_filter,omitempty"` + } + + type LDAPAuth struct { +@@ -71,22 +77,19 @@ func (la *LDAPAuth) Authenticate(account string, password PasswordString) (bool, + + filter := la.getFilter(account) + +- // dnAndGroupAttr := []string{"DN"} // example of no groups mapping attribute +- groupAttribute := "memberOf" +- dnAndGroupAttr := []string{"DN", groupAttribute} ++ labelAttributes, labelsConfigErr := la.getLabelAttributes() ++ if labelsConfigErr != nil { ++ return false, nil, labelsConfigErr ++ } + +- entryAttrMap, uSearchErr := la.ldapSearch(l, &la.config.Base, &filter, &dnAndGroupAttr) ++ accountEntryDN, entryAttrMap, uSearchErr := la.ldapSearch(l, &la.config.Base, &filter, &labelAttributes) + if uSearchErr != nil { + return false, nil, uSearchErr + } +- if len(entryAttrMap) < 1 || entryAttrMap["DN"] == nil || len(entryAttrMap["DN"]) != 1 { +- return false, nil, NoMatch // User does not exist +- } +- +- accountEntryDN := entryAttrMap["DN"][0] + if accountEntryDN == "" { + return false, nil, NoMatch // User does not exist + } ++ + // Bind as the user to verify their password + if len(accountEntryDN) > 0 { + err := l.Bind(accountEntryDN, string(password)) +@@ -102,21 +105,13 @@ func (la *LDAPAuth) Authenticate(account string, password PasswordString) (bool, + return false, nil, bindErr + } + +- // Extract group names from the attribute values +- if entryAttrMap[groupAttribute] != nil { +- rawGroups := entryAttrMap[groupAttribute] +- labels := make(map[string][]string) +- var groups []string +- for _, value := range rawGroups { +- cn := la.getCNFromDN(value) +- groups = append(groups, cn) +- } +- labels["groups"] = groups +- +- return true, labels, nil ++ // Extract labels from the attribute values ++ labels, labelsExtractErr := la.getLabelsFromMap(entryAttrMap) ++ if labelsExtractErr != nil { ++ return false, nil, labelsExtractErr + } + +- return true, nil, nil ++ return true, labels, nil + } + + func (la *LDAPAuth) bindReadOnlyUser(l *ldap.Conn) error { +@@ -193,9 +188,9 @@ func (la *LDAPAuth) getFilter(account string) string { + + //ldap search and return required attributes' value from searched entries + //default return entry's DN value if you leave attrs array empty +-func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, attrs *[]string) (map[string][]string, error) { ++func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, attrs *[]string) (string, map[string][]string, error) { + if l == nil { +- return nil, fmt.Errorf("No ldap connection!") ++ return "", nil, fmt.Errorf("No ldap connection!") + } + glog.V(2).Infof("Searching...basedDN:%s, filter:%s", *baseDN, *filter) + searchRequest := ldap.NewSearchRequest( +@@ -206,38 +201,66 @@ func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, att + nil) + sr, err := l.Search(searchRequest) + if err != nil { +- return nil, err ++ return "", nil, err + } + + if len(sr.Entries) == 0 { +- return nil, nil // User does not exist ++ return "", nil, nil // User does not exist + } else if len(sr.Entries) > 1 { +- return nil, fmt.Errorf("Too many entries returned.") ++ return "", nil, fmt.Errorf("Too many entries returned.") + } + +- result := make(map[string][]string) ++ attributes := make(map[string][]string) ++ var entryDn string + for _, entry := range sr.Entries { +- ++ entryDn = entry.DN + if len(*attrs) == 0 { +- glog.V(2).Infof("Entry DN = %s", entry.DN) +- result["DN"] = []string{entry.DN} ++ glog.V(2).Infof("Entry DN = %s", entryDn) + } else { + for _, attr := range *attrs { +- var values []string +- if attr == "DN" { +- // DN is excluded from attributes +- values = []string{entry.DN} +- } else { +- values = entry.GetAttributeValues(attr) +- } +- valuesString := strings.Join(values, "\n") +- glog.V(2).Infof("Entry %s = %s", attr, valuesString) +- result[attr] = values ++ values := entry.GetAttributeValues(attr) ++ glog.V(2).Infof("Entry %s = %s", attr, strings.Join(values, "\n")) ++ attributes[attr] = values + } + } + } + +- return result, nil ++ return entryDn, attributes, nil ++} ++ ++func (la *LDAPAuth) getLabelAttributes() ([]string, error) { ++ labelAttributes := make([]string, len(la.config.LabelMaps)) ++ i := 0 ++ for key, mapping := range la.config.LabelMaps { ++ if mapping.Attribute == "" { ++ return nil, fmt.Errorf("Label %s is missing 'attribute' to map from", key) ++ } ++ labelAttributes[i] = mapping.Attribute ++ i++ ++ } ++ return labelAttributes, nil ++} ++ ++func (la *LDAPAuth) getLabelsFromMap(attrMap map[string][]string) (map[string][]string, error) { ++ labels := make(map[string][]string) ++ for key, mapping := range la.config.LabelMaps { ++ if mapping.Attribute == "" { ++ return nil, fmt.Errorf("Label %s is missing 'attribute' to map from", key) ++ } ++ ++ mappingValues := attrMap[mapping.Attribute] ++ if mappingValues != nil { ++ if mapping.ParseCN { ++ // shorten attribute to its common name ++ for i, value := range mappingValues { ++ cn := la.getCNFromDN(value) ++ mappingValues[i] = cn ++ } ++ } ++ labels[key] = mappingValues ++ } ++ } ++ return labels, nil + } + + func (la *LDAPAuth) getCNFromDN(dn string) string { + +From cd37001980267a99a9faa19f1927891af63acb90 Mon Sep 17 00:00:00 2001 +From: Kevin <kcd83@users.noreply.github.com> +Date: Tue, 28 Feb 2017 18:27:16 +1300 +Subject: [PATCH 3/4] Remove unused configuration fields, never implemented? + +--- + auth_server/authn/ldap_auth.go | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/auth_server/authn/ldap_auth.go b/auth_server/authn/ldap_auth.go +index 6f733a2..9c8bcb8 100644 +--- a/auth_server/authn/ldap_auth.go ++++ b/auth_server/authn/ldap_auth.go +@@ -40,8 +40,6 @@ type LDAPAuthConfig struct { + BindDN string `yaml:"bind_dn,omitempty"` + BindPasswordFile string `yaml:"bind_password_file,omitempty"` + LabelMaps map[string]LabelMap `yaml:"labels,omitempty"` +- GroupBaseDN string `yaml:"group_base_dn,omitempty"` +- GroupFilter string `yaml:"group_filter,omitempty"` + } + + type LDAPAuth struct { + +From 2fd43be4e5c2cfe177d9e1d36bcd1b29f4d6f262 Mon Sep 17 00:00:00 2001 +From: Kevin <kcd83@users.noreply.github.com> +Date: Fri, 1 Sep 2017 22:50:19 +1200 +Subject: [PATCH 4/4] Add LDAP label map examples to the reference config + +--- + examples/reference.yml | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/examples/reference.yml b/examples/reference.yml +index b8bb08c..40c5762 100644 +--- a/examples/reference.yml ++++ b/examples/reference.yml +@@ -134,6 +134,16 @@ ldap_auth: + # User query settings. ${account} is expanded from auth request + base: o=example.com + filter: (&(uid=${account})(objectClass=person)) ++ # Labels can be mapped from LDAP attributes ++ labels: ++ # Add the user's title to a label called title ++ title: ++ attribute: title ++ # Add the user's memberOf values to a label called groups ++ groups: ++ attribute: memberOf ++ # Special handling to simplify the values to just the common name ++ parse_cn: true + + mongo_auth: + # Essentially all options are described here: https://godoc.org/gopkg.in/mgo.v2#DialInfo |