1 |
commit: d4aea241bbc1e07d25e3ae16407df9b9ce765921 |
2 |
Author: Manuel Rüger <mrueg <AT> gentoo <DOT> org> |
3 |
AuthorDate: Tue Sep 26 16:43:42 2017 +0000 |
4 |
Commit: Manuel Rüger <mrueg <AT> gentoo <DOT> org> |
5 |
CommitDate: Tue Sep 26 16:43:42 2017 +0000 |
6 |
URL: https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=d4aea241 |
7 |
|
8 |
sys-auth/docker_auth: Add patch to set ldap cert |
9 |
|
10 |
Package-Manager: Portage-2.3.10, Repoman-2.3.3 |
11 |
|
12 |
sys-auth/docker_auth/docker_auth-1.3-r1.ebuild | 88 +++++ |
13 |
.../files/docker_auth-ldap-cacert.patch | 67 ++++ |
14 |
.../files/docker_auth-ldap-group-support-1.patch | 394 +++++++++++++++++++++ |
15 |
3 files changed, 549 insertions(+) |
16 |
|
17 |
diff --git a/sys-auth/docker_auth/docker_auth-1.3-r1.ebuild b/sys-auth/docker_auth/docker_auth-1.3-r1.ebuild |
18 |
new file mode 100644 |
19 |
index 00000000000..ebeaf7fa15e |
20 |
--- /dev/null |
21 |
+++ b/sys-auth/docker_auth/docker_auth-1.3-r1.ebuild |
22 |
@@ -0,0 +1,88 @@ |
23 |
+# Copyright 1999-2017 Gentoo Foundation |
24 |
+# Distributed under the terms of the GNU General Public License v2 |
25 |
+ |
26 |
+EAPI=6 |
27 |
+EGO_PN="github.com/cesanta/docker_auth" |
28 |
+ |
29 |
+EGO_VENDOR=( |
30 |
+ "github.com/dchest/uniuri 8902c56451e9b58ff940bbe5fec35d5f9c04584a" |
31 |
+ "github.com/deckarep/golang-set fc8930a5e645572ee00bf66358ed3414f3c13b90" |
32 |
+ "github.com/docker/distribution 0700fa570d7bcc1b3e46ee127c4489fd25f4daa3" |
33 |
+ "github.com/docker/libtrust aabc10ec26b754e797f9028f4589c5b7bd90dc20" |
34 |
+ "github.com/facebookgo/httpdown a3b1354551a26449fbe05f5d855937f6e7acbd71" |
35 |
+ "github.com/facebookgo/clock 600d898af40aa09a7a93ecb9265d87b0504b6f03" |
36 |
+ "github.com/facebookgo/stats 1b76add642e42c6ffba7211ad7b3939ce654526e" |
37 |
+ "github.com/go-ldap/ldap 13cedcf58a1ea124045dea529a66c849d3444c8e" |
38 |
+ "github.com/cesanta/glog 22eb27a0ae192b290b25537b8e876556fc25129c" |
39 |
+ "github.com/schwarmco/go-cartesian-product c2c0aca869a6cbf51e017ce148b949d9dee09bc3" |
40 |
+ "github.com/syndtr/goleveldb 3c5717caf1475fd25964109a0fc640bd150fce43" |
41 |
+ "github.com/golang/snappy 553a641470496b2327abcac10b36396bd98e45c9" |
42 |
+ "gopkg.in/asn1-ber.v1 4e86f4367175e39f69d9358a5f17b4dda270378d github.com/go-asn1-ber/asn1-ber" |
43 |
+ "gopkg.in/fsnotify.v1 629574ca2a5df945712d3079857300b5e4da0236 github.com/fsnotify/fsnotify" |
44 |
+ "gopkg.in/mgo.v2 3f83fa5005286a7fe593b055f0d7771a7dce4655 github.com/go-mgo/mgo" |
45 |
+ "gopkg.in/yaml.v2 a3f3340b5840cee44f372bddb5880fcbc419b46a github.com/go-yaml/yaml" |
46 |
+ "golang.org/x/crypto e1a4589e7d3ea14a3352255d04b6f1a418845e5e github.com/golang/crypto" |
47 |
+ "golang.org/x/sys 493114f68206f85e7e333beccfabc11e98cba8dd github.com/golang/sys" |
48 |
+ "golang.org/x/net 859d1a86bb617c0c20d154590c3c5d3fcb670b07 github.com/golang/net" |
49 |
+ "google.golang.org/api 39c3dd417c5a443607650f18e829ad308da08dd2 github.com/google/google-api-go-client" |
50 |
+ "google.golang.org/grpc 35170916ff58e89ae03f52e778228e18207e0e02 github.com/grpc/grpc-go" |
51 |
+ "github.com/golang/protobuf 11b8df160996e00fd4b55cbaafb3d84ec6d50fa8" |
52 |
+ "golang.org/x/oauth2 13449ad91cb26cb47661c1b080790392170385fd github.com/golang/oauth2" |
53 |
+ "cloud.google.com/go bbf380d59635bf267fc8a8df03d6d261c448ee3c github.com/GoogleCloudPlatform/gcloud-golang" |
54 |
+ "golang.org/x/text ab5ac5f9a8deb4855a60fab02bc61a4ec770bd49 github.com/golang/text" |
55 |
+ "github.com/googleapis/gax-go 8c160ca1523d8eea3932fbaa494c8964b7724aa8" |
56 |
+ "google.golang.org/genproto 595979c8a7bf586b2d293fb42246bf91a0b893d9 github.com/google/go-genproto" |
57 |
+ ) |
58 |
+ |
59 |
+inherit user golang-build golang-vcs-snapshot |
60 |
+EGIT_COMMIT="2cd3699dab4e44a239db136a52734dab25897bee" |
61 |
+SHORT_COMMIT=${EGIT_COMMIT:0:7} |
62 |
+SRC_URI="https://${EGO_PN}/archive/${PV}.tar.gz -> ${P}.tar.gz |
63 |
+ ${EGO_VENDOR_URI}" |
64 |
+KEYWORDS="~amd64" |
65 |
+ |
66 |
+DESCRIPTION="Docker Registry 2 authentication server" |
67 |
+HOMEPAGE="https://github.com/cesanta/docker_auth" |
68 |
+ |
69 |
+LICENSE="Apache-2.0" |
70 |
+SLOT="0" |
71 |
+IUSE="" |
72 |
+ |
73 |
+RESTRICT="test" |
74 |
+ |
75 |
+pkg_setup() { |
76 |
+ enewgroup ${PN} |
77 |
+ enewuser ${PN} -1 -1 /dev/null ${PN} |
78 |
+} |
79 |
+ |
80 |
+src_prepare() { |
81 |
+ default |
82 |
+ pushd src/${EGO_PN} |
83 |
+ eapply "${FILESDIR}/${PN}-ldap-group-support-1.patch" |
84 |
+ eapply "${FILESDIR}/${PN}-ldap-cacert.patch" |
85 |
+ cp "${FILESDIR}/version.go" auth_server/version.go || die |
86 |
+ sed -i -e "s/{version}/${PV}/" -e "s/{build_id}/${SHORT_COMMIT}/" auth_server/version.go || die |
87 |
+ popd || die |
88 |
+} |
89 |
+ |
90 |
+src_compile() { |
91 |
+ pushd src/${EGO_PN}/auth_server || die |
92 |
+ GOPATH="${WORKDIR}/${P}" go build -o "bin/auth_server" || die |
93 |
+ popd || die |
94 |
+} |
95 |
+ |
96 |
+src_install() { |
97 |
+ pushd src/${EGO_PN} || die |
98 |
+ dodoc README.md docs/Backend_MongoDB.md |
99 |
+ insinto /usr/share/${PF} |
100 |
+ doins -r examples |
101 |
+ insinto /etc/docker_auth/ |
102 |
+ newins examples/reference.yml config.yml.example |
103 |
+ dobin auth_server/bin/auth_server |
104 |
+ popd || die |
105 |
+ newinitd "${FILESDIR}"/${PN}.initd ${PN} |
106 |
+ newconfd "${FILESDIR}"/${PN}.confd ${PN} |
107 |
+ insinto /etc/logrotate.d |
108 |
+ newins "${FILESDIR}"/${PN}.logrotated ${PN} |
109 |
+ keepdir /var/log/docker_auth |
110 |
+} |
111 |
|
112 |
diff --git a/sys-auth/docker_auth/files/docker_auth-ldap-cacert.patch b/sys-auth/docker_auth/files/docker_auth-ldap-cacert.patch |
113 |
new file mode 100644 |
114 |
index 00000000000..e43e9e6ca88 |
115 |
--- /dev/null |
116 |
+++ b/sys-auth/docker_auth/files/docker_auth-ldap-cacert.patch |
117 |
@@ -0,0 +1,67 @@ |
118 |
+From 5505de31a91aea88e0cf623ec8edfd928b5432a7 Mon Sep 17 00:00:00 2001 |
119 |
+From: =?UTF-8?q?Manuel=20R=C3=BCger?= <mrueg@g.o> |
120 |
+Date: Mon, 18 Sep 2017 14:02:38 +0200 |
121 |
+Subject: [PATCH] Set custom CA certificate for ldap cert verification |
122 |
+ |
123 |
+Code taken from: https://github.com/hashicorp/go-rootcerts/blob/master/rootcerts.go |
124 |
+Original author: Paul Hinze <phinze@××××××.com> |
125 |
+--- |
126 |
+ auth_server/authn/ldap_auth.go | 17 ++++++++++++++++- |
127 |
+ examples/reference.yml | 2 ++ |
128 |
+ 2 files changed, 18 insertions(+), 1 deletion(-) |
129 |
+ |
130 |
+diff --git a/auth_server/authn/ldap_auth.go b/auth_server/authn/ldap_auth.go |
131 |
+index 3bdf7c3..a3425ed 100644 |
132 |
+--- a/auth_server/authn/ldap_auth.go |
133 |
++++ b/auth_server/authn/ldap_auth.go |
134 |
+@@ -19,6 +19,7 @@ package authn |
135 |
+ import ( |
136 |
+ "bytes" |
137 |
+ "crypto/tls" |
138 |
++ "crypto/x509" |
139 |
+ "fmt" |
140 |
+ "io/ioutil" |
141 |
+ "strings" |
142 |
+@@ -31,6 +32,7 @@ type LDAPAuthConfig struct { |
143 |
+ Addr string `yaml:"addr,omitempty"` |
144 |
+ TLS string `yaml:"tls,omitempty"` |
145 |
+ InsecureTLSSkipVerify bool `yaml:"insecure_tls_skip_verify,omitempty"` |
146 |
++ CACertificate string `yaml:"ca_certificate,omitempty"` |
147 |
+ Base string `yaml:"base,omitempty"` |
148 |
+ Filter string `yaml:"filter,omitempty"` |
149 |
+ BindDN string `yaml:"bind_dn,omitempty"` |
150 |
+@@ -140,7 +142,20 @@ func (la *LDAPAuth) ldapConnection() (*ldap.Conn, error) { |
151 |
+ tlsConfig := &tls.Config{InsecureSkipVerify: true} |
152 |
+ if !la.config.InsecureTLSSkipVerify { |
153 |
+ addr := strings.Split(la.config.Addr, ":") |
154 |
+- tlsConfig = &tls.Config{InsecureSkipVerify: false, ServerName: addr[0]} |
155 |
++ if la.config.CACertificate != "" { |
156 |
++ pool := x509.NewCertPool() |
157 |
++ pem, err := ioutil.ReadFile(la.config.CACertificate) |
158 |
++ if err != nil { |
159 |
++ return nil, fmt.Errorf("Error loading CA File: %s", err) |
160 |
++ } |
161 |
++ ok := pool.AppendCertsFromPEM(pem) |
162 |
++ if !ok { |
163 |
++ return nil, fmt.Errorf("Error loading CA File: Couldn't parse PEM in: %s", la.config.CACertificate) |
164 |
++ } |
165 |
++ tlsConfig = &tls.Config{InsecureSkipVerify: false, ServerName: addr[0], RootCAs: pool} |
166 |
++ } else { |
167 |
++ tlsConfig = &tls.Config{InsecureSkipVerify: false, ServerName: addr[0]} |
168 |
++ } |
169 |
+ } |
170 |
+ |
171 |
+ if la.config.TLS == "" || la.config.TLS == "none" || la.config.TLS == "starttls" { |
172 |
+diff --git a/examples/reference.yml b/examples/reference.yml |
173 |
+index 3090978..769cc91 100644 |
174 |
+--- a/examples/reference.yml |
175 |
++++ b/examples/reference.yml |
176 |
+@@ -131,6 +131,8 @@ ldap_auth: |
177 |
+ tls: always |
178 |
+ # set to true to allow insecure tls |
179 |
+ insecure_tls_skip_verify: false |
180 |
++ # set this to specify the ca certificate path |
181 |
++ ca_cert: |
182 |
+ # In case bind DN and password is required for querying user information, |
183 |
+ # specify them here. Plain text password is read from the file. |
184 |
+ bind_dn: |
185 |
|
186 |
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 |
187 |
new file mode 100644 |
188 |
index 00000000000..f9e98f410c8 |
189 |
--- /dev/null |
190 |
+++ b/sys-auth/docker_auth/files/docker_auth-ldap-group-support-1.patch |
191 |
@@ -0,0 +1,394 @@ |
192 |
+From 4a33badac6b74617dfe3797a716a6907cf018b27 Mon Sep 17 00:00:00 2001 |
193 |
+From: Kevin <kcd83@××××××××××××××××××××.com> |
194 |
+Date: Mon, 27 Feb 2017 19:09:52 +1300 |
195 |
+Subject: [PATCH 1/4] Initial proof of concept mapping memberOf CN to the label |
196 |
+ groups #63 |
197 |
+ |
198 |
+--- |
199 |
+ auth_server/authn/ldap_auth.go | 73 ++++++++++++++++++++++++++++++++++-------- |
200 |
+ 1 file changed, 60 insertions(+), 13 deletions(-) |
201 |
+ |
202 |
+diff --git a/auth_server/authn/ldap_auth.go b/auth_server/authn/ldap_auth.go |
203 |
+index f8fc08f..42f5ad0 100644 |
204 |
+--- a/auth_server/authn/ldap_auth.go |
205 |
++++ b/auth_server/authn/ldap_auth.go |
206 |
+@@ -17,7 +17,6 @@ |
207 |
+ package authn |
208 |
+ |
209 |
+ import ( |
210 |
+- "bytes" |
211 |
+ "crypto/tls" |
212 |
+ "fmt" |
213 |
+ "io/ioutil" |
214 |
+@@ -71,10 +70,20 @@ func (la *LDAPAuth) Authenticate(account string, password PasswordString) (bool, |
215 |
+ account = la.escapeAccountInput(account) |
216 |
+ |
217 |
+ filter := la.getFilter(account) |
218 |
+- accountEntryDN, uSearchErr := la.ldapSearch(l, &la.config.Base, &filter, &[]string{}) |
219 |
++ |
220 |
++ // dnAndGroupAttr := []string{"DN"} // example of no groups mapping attribute |
221 |
++ groupAttribute := "memberOf" |
222 |
++ dnAndGroupAttr := []string{"DN", groupAttribute} |
223 |
++ |
224 |
++ entryAttrMap, uSearchErr := la.ldapSearch(l, &la.config.Base, &filter, &dnAndGroupAttr) |
225 |
+ if uSearchErr != nil { |
226 |
+ return false, nil, uSearchErr |
227 |
+ } |
228 |
++ if len(entryAttrMap) < 1 || entryAttrMap["DN"] == nil || len(entryAttrMap["DN"]) != 1 { |
229 |
++ return false, nil, NoMatch // User does not exist |
230 |
++ } |
231 |
++ |
232 |
++ accountEntryDN := entryAttrMap["DN"][0] |
233 |
+ if accountEntryDN == "" { |
234 |
+ return false, nil, NoMatch // User does not exist |
235 |
+ } |
236 |
+@@ -93,6 +102,20 @@ func (la *LDAPAuth) Authenticate(account string, password PasswordString) (bool, |
237 |
+ return false, nil, bindErr |
238 |
+ } |
239 |
+ |
240 |
++ // Extract group names from the attribute values |
241 |
++ if entryAttrMap[groupAttribute] != nil { |
242 |
++ rawGroups := entryAttrMap[groupAttribute] |
243 |
++ labels := make(map[string][]string) |
244 |
++ var groups []string |
245 |
++ for _, value := range rawGroups { |
246 |
++ cn := la.getCNFromDN(value) |
247 |
++ groups = append(groups, cn) |
248 |
++ } |
249 |
++ labels["groups"] = groups |
250 |
++ |
251 |
++ return true, labels, nil |
252 |
++ } |
253 |
++ |
254 |
+ return true, nil, nil |
255 |
+ } |
256 |
+ |
257 |
+@@ -170,9 +193,9 @@ func (la *LDAPAuth) getFilter(account string) string { |
258 |
+ |
259 |
+ //ldap search and return required attributes' value from searched entries |
260 |
+ //default return entry's DN value if you leave attrs array empty |
261 |
+-func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, attrs *[]string) (string, error) { |
262 |
++func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, attrs *[]string) (map[string][]string, error) { |
263 |
+ if l == nil { |
264 |
+- return "", fmt.Errorf("No ldap connection!") |
265 |
++ return nil, fmt.Errorf("No ldap connection!") |
266 |
+ } |
267 |
+ glog.V(2).Infof("Searching...basedDN:%s, filter:%s", *baseDN, *filter) |
268 |
+ searchRequest := ldap.NewSearchRequest( |
269 |
+@@ -183,30 +206,54 @@ func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, att |
270 |
+ nil) |
271 |
+ sr, err := l.Search(searchRequest) |
272 |
+ if err != nil { |
273 |
+- return "", err |
274 |
++ return nil, err |
275 |
+ } |
276 |
+ |
277 |
+ if len(sr.Entries) == 0 { |
278 |
+- return "", nil // User does not exist |
279 |
++ return nil, nil // User does not exist |
280 |
+ } else if len(sr.Entries) > 1 { |
281 |
+- return "", fmt.Errorf("Too many entries returned.") |
282 |
++ return nil, fmt.Errorf("Too many entries returned.") |
283 |
+ } |
284 |
+ |
285 |
+- var buffer bytes.Buffer |
286 |
++ result := make(map[string][]string) |
287 |
+ for _, entry := range sr.Entries { |
288 |
++ |
289 |
+ if len(*attrs) == 0 { |
290 |
+ glog.V(2).Infof("Entry DN = %s", entry.DN) |
291 |
+- buffer.WriteString(entry.DN) |
292 |
++ result["DN"] = []string{entry.DN} |
293 |
+ } else { |
294 |
+ for _, attr := range *attrs { |
295 |
+- values := strings.Join(entry.GetAttributeValues(attr), " ") |
296 |
+- glog.V(2).Infof("Entry %s = %s", attr, values) |
297 |
+- buffer.WriteString(values) |
298 |
++ var values []string |
299 |
++ if attr == "DN" { |
300 |
++ // DN is excluded from attributes |
301 |
++ values = []string{entry.DN} |
302 |
++ } else { |
303 |
++ values = entry.GetAttributeValues(attr) |
304 |
++ } |
305 |
++ valuesString := strings.Join(values, "\n") |
306 |
++ glog.V(2).Infof("Entry %s = %s", attr, valuesString) |
307 |
++ result[attr] = values |
308 |
++ } |
309 |
++ } |
310 |
++ } |
311 |
++ |
312 |
++ return result, nil |
313 |
++} |
314 |
++ |
315 |
++func (la *LDAPAuth) getCNFromDN(dn string) string { |
316 |
++ parsedDN, err := ldap.ParseDN(dn) |
317 |
++ if err != nil || len(parsedDN.RDNs) > 0 { |
318 |
++ for _, rdn := range parsedDN.RDNs { |
319 |
++ for _, rdnAttr := range rdn.Attributes { |
320 |
++ if rdnAttr.Type == "CN" { |
321 |
++ return rdnAttr.Value |
322 |
++ } |
323 |
+ } |
324 |
+ } |
325 |
+ } |
326 |
+ |
327 |
+- return buffer.String(), nil |
328 |
++ // else try using raw DN |
329 |
++ return dn |
330 |
+ } |
331 |
+ |
332 |
+ func (la *LDAPAuth) Stop() { |
333 |
+ |
334 |
+From ddde2fa779e746d7e74cd972a4c6795c72f17ee6 Mon Sep 17 00:00:00 2001 |
335 |
+From: Kevin <kcd83@××××××××××××××××××××.com> |
336 |
+Date: Tue, 28 Feb 2017 18:09:55 +1300 |
337 |
+Subject: [PATCH 2/4] Apply attribute mapping from configuration |
338 |
+ |
339 |
+--- |
340 |
+ auth_server/authn/ldap_auth.go | 125 ++++++++++++++++++++++++----------------- |
341 |
+ 1 file changed, 74 insertions(+), 51 deletions(-) |
342 |
+ |
343 |
+diff --git a/auth_server/authn/ldap_auth.go b/auth_server/authn/ldap_auth.go |
344 |
+index 42f5ad0..6f733a2 100644 |
345 |
+--- a/auth_server/authn/ldap_auth.go |
346 |
++++ b/auth_server/authn/ldap_auth.go |
347 |
+@@ -26,16 +26,22 @@ import ( |
348 |
+ "github.com/golang/glog" |
349 |
+ ) |
350 |
+ |
351 |
++type LabelMap struct { |
352 |
++ Attribute string `yaml:"attribute,omitempty"` |
353 |
++ ParseCN bool `yaml:"parse_cn,omitempty"` |
354 |
++} |
355 |
++ |
356 |
+ type LDAPAuthConfig struct { |
357 |
+- Addr string `yaml:"addr,omitempty"` |
358 |
+- TLS string `yaml:"tls,omitempty"` |
359 |
+- InsecureTLSSkipVerify bool `yaml:"insecure_tls_skip_verify,omitempty"` |
360 |
+- Base string `yaml:"base,omitempty"` |
361 |
+- Filter string `yaml:"filter,omitempty"` |
362 |
+- BindDN string `yaml:"bind_dn,omitempty"` |
363 |
+- BindPasswordFile string `yaml:"bind_password_file,omitempty"` |
364 |
+- GroupBaseDN string `yaml:"group_base_dn,omitempty"` |
365 |
+- GroupFilter string `yaml:"group_filter,omitempty"` |
366 |
++ Addr string `yaml:"addr,omitempty"` |
367 |
++ TLS string `yaml:"tls,omitempty"` |
368 |
++ InsecureTLSSkipVerify bool `yaml:"insecure_tls_skip_verify,omitempty"` |
369 |
++ Base string `yaml:"base,omitempty"` |
370 |
++ Filter string `yaml:"filter,omitempty"` |
371 |
++ BindDN string `yaml:"bind_dn,omitempty"` |
372 |
++ BindPasswordFile string `yaml:"bind_password_file,omitempty"` |
373 |
++ LabelMaps map[string]LabelMap `yaml:"labels,omitempty"` |
374 |
++ GroupBaseDN string `yaml:"group_base_dn,omitempty"` |
375 |
++ GroupFilter string `yaml:"group_filter,omitempty"` |
376 |
+ } |
377 |
+ |
378 |
+ type LDAPAuth struct { |
379 |
+@@ -71,22 +77,19 @@ func (la *LDAPAuth) Authenticate(account string, password PasswordString) (bool, |
380 |
+ |
381 |
+ filter := la.getFilter(account) |
382 |
+ |
383 |
+- // dnAndGroupAttr := []string{"DN"} // example of no groups mapping attribute |
384 |
+- groupAttribute := "memberOf" |
385 |
+- dnAndGroupAttr := []string{"DN", groupAttribute} |
386 |
++ labelAttributes, labelsConfigErr := la.getLabelAttributes() |
387 |
++ if labelsConfigErr != nil { |
388 |
++ return false, nil, labelsConfigErr |
389 |
++ } |
390 |
+ |
391 |
+- entryAttrMap, uSearchErr := la.ldapSearch(l, &la.config.Base, &filter, &dnAndGroupAttr) |
392 |
++ accountEntryDN, entryAttrMap, uSearchErr := la.ldapSearch(l, &la.config.Base, &filter, &labelAttributes) |
393 |
+ if uSearchErr != nil { |
394 |
+ return false, nil, uSearchErr |
395 |
+ } |
396 |
+- if len(entryAttrMap) < 1 || entryAttrMap["DN"] == nil || len(entryAttrMap["DN"]) != 1 { |
397 |
+- return false, nil, NoMatch // User does not exist |
398 |
+- } |
399 |
+- |
400 |
+- accountEntryDN := entryAttrMap["DN"][0] |
401 |
+ if accountEntryDN == "" { |
402 |
+ return false, nil, NoMatch // User does not exist |
403 |
+ } |
404 |
++ |
405 |
+ // Bind as the user to verify their password |
406 |
+ if len(accountEntryDN) > 0 { |
407 |
+ err := l.Bind(accountEntryDN, string(password)) |
408 |
+@@ -102,21 +105,13 @@ func (la *LDAPAuth) Authenticate(account string, password PasswordString) (bool, |
409 |
+ return false, nil, bindErr |
410 |
+ } |
411 |
+ |
412 |
+- // Extract group names from the attribute values |
413 |
+- if entryAttrMap[groupAttribute] != nil { |
414 |
+- rawGroups := entryAttrMap[groupAttribute] |
415 |
+- labels := make(map[string][]string) |
416 |
+- var groups []string |
417 |
+- for _, value := range rawGroups { |
418 |
+- cn := la.getCNFromDN(value) |
419 |
+- groups = append(groups, cn) |
420 |
+- } |
421 |
+- labels["groups"] = groups |
422 |
+- |
423 |
+- return true, labels, nil |
424 |
++ // Extract labels from the attribute values |
425 |
++ labels, labelsExtractErr := la.getLabelsFromMap(entryAttrMap) |
426 |
++ if labelsExtractErr != nil { |
427 |
++ return false, nil, labelsExtractErr |
428 |
+ } |
429 |
+ |
430 |
+- return true, nil, nil |
431 |
++ return true, labels, nil |
432 |
+ } |
433 |
+ |
434 |
+ func (la *LDAPAuth) bindReadOnlyUser(l *ldap.Conn) error { |
435 |
+@@ -193,9 +188,9 @@ func (la *LDAPAuth) getFilter(account string) string { |
436 |
+ |
437 |
+ //ldap search and return required attributes' value from searched entries |
438 |
+ //default return entry's DN value if you leave attrs array empty |
439 |
+-func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, attrs *[]string) (map[string][]string, error) { |
440 |
++func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, attrs *[]string) (string, map[string][]string, error) { |
441 |
+ if l == nil { |
442 |
+- return nil, fmt.Errorf("No ldap connection!") |
443 |
++ return "", nil, fmt.Errorf("No ldap connection!") |
444 |
+ } |
445 |
+ glog.V(2).Infof("Searching...basedDN:%s, filter:%s", *baseDN, *filter) |
446 |
+ searchRequest := ldap.NewSearchRequest( |
447 |
+@@ -206,38 +201,66 @@ func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, att |
448 |
+ nil) |
449 |
+ sr, err := l.Search(searchRequest) |
450 |
+ if err != nil { |
451 |
+- return nil, err |
452 |
++ return "", nil, err |
453 |
+ } |
454 |
+ |
455 |
+ if len(sr.Entries) == 0 { |
456 |
+- return nil, nil // User does not exist |
457 |
++ return "", nil, nil // User does not exist |
458 |
+ } else if len(sr.Entries) > 1 { |
459 |
+- return nil, fmt.Errorf("Too many entries returned.") |
460 |
++ return "", nil, fmt.Errorf("Too many entries returned.") |
461 |
+ } |
462 |
+ |
463 |
+- result := make(map[string][]string) |
464 |
++ attributes := make(map[string][]string) |
465 |
++ var entryDn string |
466 |
+ for _, entry := range sr.Entries { |
467 |
+- |
468 |
++ entryDn = entry.DN |
469 |
+ if len(*attrs) == 0 { |
470 |
+- glog.V(2).Infof("Entry DN = %s", entry.DN) |
471 |
+- result["DN"] = []string{entry.DN} |
472 |
++ glog.V(2).Infof("Entry DN = %s", entryDn) |
473 |
+ } else { |
474 |
+ for _, attr := range *attrs { |
475 |
+- var values []string |
476 |
+- if attr == "DN" { |
477 |
+- // DN is excluded from attributes |
478 |
+- values = []string{entry.DN} |
479 |
+- } else { |
480 |
+- values = entry.GetAttributeValues(attr) |
481 |
+- } |
482 |
+- valuesString := strings.Join(values, "\n") |
483 |
+- glog.V(2).Infof("Entry %s = %s", attr, valuesString) |
484 |
+- result[attr] = values |
485 |
++ values := entry.GetAttributeValues(attr) |
486 |
++ glog.V(2).Infof("Entry %s = %s", attr, strings.Join(values, "\n")) |
487 |
++ attributes[attr] = values |
488 |
+ } |
489 |
+ } |
490 |
+ } |
491 |
+ |
492 |
+- return result, nil |
493 |
++ return entryDn, attributes, nil |
494 |
++} |
495 |
++ |
496 |
++func (la *LDAPAuth) getLabelAttributes() ([]string, error) { |
497 |
++ labelAttributes := make([]string, len(la.config.LabelMaps)) |
498 |
++ i := 0 |
499 |
++ for key, mapping := range la.config.LabelMaps { |
500 |
++ if mapping.Attribute == "" { |
501 |
++ return nil, fmt.Errorf("Label %s is missing 'attribute' to map from", key) |
502 |
++ } |
503 |
++ labelAttributes[i] = mapping.Attribute |
504 |
++ i++ |
505 |
++ } |
506 |
++ return labelAttributes, nil |
507 |
++} |
508 |
++ |
509 |
++func (la *LDAPAuth) getLabelsFromMap(attrMap map[string][]string) (map[string][]string, error) { |
510 |
++ labels := make(map[string][]string) |
511 |
++ for key, mapping := range la.config.LabelMaps { |
512 |
++ if mapping.Attribute == "" { |
513 |
++ return nil, fmt.Errorf("Label %s is missing 'attribute' to map from", key) |
514 |
++ } |
515 |
++ |
516 |
++ mappingValues := attrMap[mapping.Attribute] |
517 |
++ if mappingValues != nil { |
518 |
++ if mapping.ParseCN { |
519 |
++ // shorten attribute to its common name |
520 |
++ for i, value := range mappingValues { |
521 |
++ cn := la.getCNFromDN(value) |
522 |
++ mappingValues[i] = cn |
523 |
++ } |
524 |
++ } |
525 |
++ labels[key] = mappingValues |
526 |
++ } |
527 |
++ } |
528 |
++ return labels, nil |
529 |
+ } |
530 |
+ |
531 |
+ func (la *LDAPAuth) getCNFromDN(dn string) string { |
532 |
+ |
533 |
+From cd37001980267a99a9faa19f1927891af63acb90 Mon Sep 17 00:00:00 2001 |
534 |
+From: Kevin <kcd83@××××××××××××××××××××.com> |
535 |
+Date: Tue, 28 Feb 2017 18:27:16 +1300 |
536 |
+Subject: [PATCH 3/4] Remove unused configuration fields, never implemented? |
537 |
+ |
538 |
+--- |
539 |
+ auth_server/authn/ldap_auth.go | 2 -- |
540 |
+ 1 file changed, 2 deletions(-) |
541 |
+ |
542 |
+diff --git a/auth_server/authn/ldap_auth.go b/auth_server/authn/ldap_auth.go |
543 |
+index 6f733a2..9c8bcb8 100644 |
544 |
+--- a/auth_server/authn/ldap_auth.go |
545 |
++++ b/auth_server/authn/ldap_auth.go |
546 |
+@@ -40,8 +40,6 @@ type LDAPAuthConfig struct { |
547 |
+ BindDN string `yaml:"bind_dn,omitempty"` |
548 |
+ BindPasswordFile string `yaml:"bind_password_file,omitempty"` |
549 |
+ LabelMaps map[string]LabelMap `yaml:"labels,omitempty"` |
550 |
+- GroupBaseDN string `yaml:"group_base_dn,omitempty"` |
551 |
+- GroupFilter string `yaml:"group_filter,omitempty"` |
552 |
+ } |
553 |
+ |
554 |
+ type LDAPAuth struct { |
555 |
+ |
556 |
+From 2fd43be4e5c2cfe177d9e1d36bcd1b29f4d6f262 Mon Sep 17 00:00:00 2001 |
557 |
+From: Kevin <kcd83@××××××××××××××××××××.com> |
558 |
+Date: Fri, 1 Sep 2017 22:50:19 +1200 |
559 |
+Subject: [PATCH 4/4] Add LDAP label map examples to the reference config |
560 |
+ |
561 |
+--- |
562 |
+ examples/reference.yml | 10 ++++++++++ |
563 |
+ 1 file changed, 10 insertions(+) |
564 |
+ |
565 |
+diff --git a/examples/reference.yml b/examples/reference.yml |
566 |
+index b8bb08c..40c5762 100644 |
567 |
+--- a/examples/reference.yml |
568 |
++++ b/examples/reference.yml |
569 |
+@@ -134,6 +134,16 @@ ldap_auth: |
570 |
+ # User query settings. ${account} is expanded from auth request |
571 |
+ base: o=example.com |
572 |
+ filter: (&(uid=${account})(objectClass=person)) |
573 |
++ # Labels can be mapped from LDAP attributes |
574 |
++ labels: |
575 |
++ # Add the user's title to a label called title |
576 |
++ title: |
577 |
++ attribute: title |
578 |
++ # Add the user's memberOf values to a label called groups |
579 |
++ groups: |
580 |
++ attribute: memberOf |
581 |
++ # Special handling to simplify the values to just the common name |
582 |
++ parse_cn: true |
583 |
+ |
584 |
+ mongo_auth: |
585 |
+ # Essentially all options are described here: https://godoc.org/gopkg.in/mgo.v2#DialInfo |