Compare commits

..

262 Commits

Author SHA1 Message Date
Chris F Ravenscroft bbb9f4a25e Merge branch 'master' into chore/rewire-ldap-packages 2023-12-10 15:22:10 -08:00
Alessandro Cabbia 01bdb7148e startTLS: ldap over TLS functionality (#364)
* feat: introduce tls parameters in config to allow startTLS

* feat: pass tls config for startTLS to ldap server

* build: update libs to use newer ldap

* feat: allow tls config for startTLS
2023-12-10 15:20:29 -08:00
Chris F Ravenscroft 0c0d742ea9 chore: reweure ldap packages 2023-12-10 15:13:23 -08:00
Chris F Ravenscroft 7174aba5ab chore: fix wrong goconvey hierarchy (#361) 2023-11-19 17:43:40 -08:00
Alessandro Cabbia 5ddbd4f610 docs: fix type on url link for last-commit badge (#358) 2023-11-05 00:13:49 -07:00
dependabot[bot] 0b6cfcb1b4 Bump github.com/smartystreets/goconvey from 1.6.4 to 1.8.1 in /v2 (#354)
Chore: Bumps [github.com/smartystreets/goconvey](https://github.com/smartystreets/goconvey) from 1.6.4 to 1.8.1.
- [Release notes](https://github.com/smartystreets/goconvey/releases)
- [Commits](https://github.com/smartystreets/goconvey/compare/v1.6.4...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/smartystreets/goconvey
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Chris F Ravenscroft <chris@voilaweb.com>
2023-10-21 15:31:19 -07:00
dependabot[bot] f3342fa709 Bump actions/checkout from 2 to 4 (#353)
Chore: Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-21 15:28:33 -07:00
dependabot[bot] 545188e5f2 Bump actions/setup-go from 2 to 4 (#352)
Chore: Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Chris F Ravenscroft <chris@voilaweb.com>
2023-10-21 15:17:09 -07:00
Chris F Ravenscroft c90cbfe5fb fix: formatting 2023-10-21 22:08:11 +00:00
Chris F Ravenscroft 1721e5701d Merge branch 'shipperizer-toml/vendor' 2023-10-21 22:04:14 +00:00
Chris F Ravenscroft ebdeae7073 Merge branch 'master' into toml/vendor 2023-10-21 15:02:48 -07:00
Alessandro Cabbia 94fe62cfe1 monitoring: metrics enhancements and proposal for dropping expvar (#351)
* feat: introduce new prometheus monitor object

* feat: add LDAPMonitorWatcher as a potential replacement for v0 Collector

* feat: pass monitor object as dependency and instrument core operations

* feat: instantiate monitor in main

* ci: exclude mock files

* ci: generate mocks before running tests and go vet

* ci: make vet command keep going in case of errors

reason is due to the following happening in the GetStats method of the ldap.Stats struct

```
internal/monitoring/mock_interfaces.go:92:13: assignment copies lock value to ret0: (github.com/nmcclain/ldap.Stats, bool) contains github.com/nmcclain/ldap.Stats contains sync.Mutex
internal/monitoring/mock_interfaces.go:93:9: return copies lock value: github.com/nmcclain/ldap.Stats contains sync.Mutex
internal/monitoring/ldap_test.go:23:56: call of mockLDAPServer.EXPECT().GetStats().MinTimes(1).Return copies lock value: github.com/nmcclain/ldap.Stats contains sync.Mutex
```

* deps:  move to use go.uber.org/mock/gomock
2023-10-21 11:58:12 -07:00
shipperizer f9addbc2c5 fix: upgrade to use BurntSushi/toml 2023-10-15 23:58:51 +01:00
shipperizer 5fe8aca852 fix: removed config setup from main, reshoring log configuration 2023-10-15 23:54:09 +01:00
shipperizer 24455e3988 fix: drop vendored toml 2023-10-15 23:54:09 +01:00
shipperizer 3ba8e11132 fix: move all toml parsing into a new internal package, drop the mappings in favour of toml.Primitive decoding 2023-10-15 23:54:09 +01:00
Alessandro Cabbia accf15410c refactor: separating http handlers from routing setup (#350)
* fix: offload prometheus logic to the monitoring package

* fix: offload ui logic to the asset package

* refactor: use handlers coming from assets and monitoring package

* ci: add unit test run and vet command

* fix: make vet happy and adjust code
2023-10-14 11:19:58 -07:00
Alessandro Cabbia 8739fd6e4f ci: dependabot and dependencies update (#348)
* fix: update and upgrade libraries

side note:
* using commit to trigger release-please workflow in light of recent changes regarding branch strategy

* ci: introduce dependabot
2023-10-14 11:15:59 -07:00
Alessandro Cabbia 97118ff186 docs: update last commit ref to use master branch (#347) 2023-10-14 11:07:26 -07:00
Chris F Ravenscroft 3943e7dc21 Graceful shutdown (#346)
* build: make build vars optional and passable via env

* build: use . for BUILD_FILES var to allow buildvcs flag

* build: create mkbindir, build and sha256 targets

* build: reuse build target for each platform specific compilation

* Ensure same go version for main and plugins

* chore: release 2.3.0

Release-As: 2.3.0

* release-please

* Fix(plugins)!: Rename the groups table to ldapgroups (#326)

The groups table name is a reserved keyword in mysql and requires that
the table name be escaped with backticks in order to use the groups
name. Other databases such as postgres do not support the use of the
backticks in the query statements. This change renames the groups table
to ldapgroups in oder to avoid this problem altogether.

This does have a dependency on the backend sql plugins to alter the
schema to match the new name.

Signed-off-by: Billy Olsen <billy.olsen@gmail.com>
Co-authored-by: Chris F Ravenscroft <chris@voilaweb.com>

* chore: update docker sqlite with ldapgroups (#338)

* feat: Update migration code to support table names (#339)

* chore(dev): release 2.3.0 (#337)

* ci: change pr name pattern for release please

* ci: switch to use master as default branch and plan to drop dev

* fix: exploit release-please for tagging in code

* fix: use runtime/debug to fetch git build info

* fix: use newer function to fetch git info for version

* build: drop build tags

* feat: graceful shutdown (#343)

* fix: start servers on separate goroutines

* fix: listen to signals for graceful shutdowns

* fix: exit with 0 in case of programmed termination

---------

Signed-off-by: Billy Olsen <billy.olsen@gmail.com>
Co-authored-by: shipperizer <alexcabb@gmail.com>
Co-authored-by: Billy Olsen <wolsen@users.noreply.github.com>
2023-10-07 18:04:53 -07:00
Chris F Ravenscroft 114b36f677 Final merge from Dev to Master (#344)
* build: make build vars optional and passable via env

* build: use . for BUILD_FILES var to allow buildvcs flag

* build: create mkbindir, build and sha256 targets

* build: reuse build target for each platform specific compilation

* Ensure same go version for main and plugins

* chore: release 2.3.0

Release-As: 2.3.0

* release-please

* Fix(plugins)!: Rename the groups table to ldapgroups (#326)

The groups table name is a reserved keyword in mysql and requires that
the table name be escaped with backticks in order to use the groups
name. Other databases such as postgres do not support the use of the
backticks in the query statements. This change renames the groups table
to ldapgroups in oder to avoid this problem altogether.

This does have a dependency on the backend sql plugins to alter the
schema to match the new name.

Signed-off-by: Billy Olsen <billy.olsen@gmail.com>
Co-authored-by: Chris F Ravenscroft <chris@voilaweb.com>

* chore: update docker sqlite with ldapgroups (#338)

* feat: Update migration code to support table names (#339)

* chore(dev): release 2.3.0 (#337)

* ci: change pr name pattern for release please

* ci: switch to use master as default branch and plan to drop dev

* fix: exploit release-please for tagging in code

* fix: use runtime/debug to fetch git build info

* fix: use newer function to fetch git info for version

* build: drop build tags

---------

Signed-off-by: Billy Olsen <billy.olsen@gmail.com>
Co-authored-by: shipperizer <alexcabb@gmail.com>
Co-authored-by: Billy Olsen <wolsen@users.noreply.github.com>
2023-10-07 12:51:00 -07:00
Chris F Ravenscroft 0904a24564 v2.3.0 (#341)
* build: make build vars optional and passable via env

* build: use . for BUILD_FILES var to allow buildvcs flag

* build: create mkbindir, build and sha256 targets

* build: reuse build target for each platform specific compilation

* Ensure same go version for main and plugins

* chore: release 2.3.0

Release-As: 2.3.0

* release-please

* Fix(plugins)!: Rename the groups table to ldapgroups (#326)

The groups table name is a reserved keyword in mysql and requires that
the table name be escaped with backticks in order to use the groups
name. Other databases such as postgres do not support the use of the
backticks in the query statements. This change renames the groups table
to ldapgroups in oder to avoid this problem altogether.

This does have a dependency on the backend sql plugins to alter the
schema to match the new name.

Signed-off-by: Billy Olsen <billy.olsen@gmail.com>
Co-authored-by: Chris F Ravenscroft <chris@voilaweb.com>

* chore: update docker sqlite with ldapgroups (#338)

* feat: Update migration code to support table names (#339)

* chore(dev): release 2.3.0 (#337)

---------

Signed-off-by: Billy Olsen <billy.olsen@gmail.com>
Co-authored-by: shipperizer <alexcabb@gmail.com>
Co-authored-by: Billy Olsen <wolsen@users.noreply.github.com>
2023-10-01 22:55:23 -07:00
Chris F Ravenscroft 498cad48b1 Merge branch 'dev' 2023-09-23 18:50:42 +00:00
Chris F Ravenscroft 19fa5045ee Merge branch 'master' of github.com:glauth/glauth 2023-09-23 18:50:38 +00:00
Chris F Ravenscroft 08a89a9861 Sum types, preparation (#330) 2023-09-09 13:35:42 -07:00
Daniel Barney a4450d4558 Allow custom S3 endpoint to be used (#329)
Co-authored-by: Daniel Barney <daniel@thoughtplot.io>
2023-09-09 13:31:40 -07:00
Chris F Ravenscroft 0b7910c03c users is an organizational unit (#323) 2023-09-09 13:09:52 -07:00
Billy Olsen 944d602077 Update the TRIM_FLAGS to use -trimpath (#327)
The current TRIM_FLAGS specify gcflags and asmflags directly in order to
trim the path of the resulting binaries. This results in an error where
the plugins cannot be loaded due to the error: "built with a different
version of package". Per https://github.com/golang/go/issues/51955 in the
golang repository, this is explicitly not supported. Switching to use the
go build command's -trimpath flag resolves the issue.

Signed-off-by: Billy Olsen <billy.olsen@gmail.com>
2023-09-03 19:15:25 -07:00
guangwu 596d28efbd chore: remove refs to deprecated io/ioutil (#322)
Thanks!
2023-07-22 13:02:24 -07:00
Chris F Ravenscroft 6a58f35b14 v2.2.0 merge to master
Cross-platform changes:
- No SysLog on Windows (#289)
  
Code quality:
- CodeSee integration
- Add CodeQL workflow for GitHub code scanning (#295)
  
Correctness:
- Construct DNs under ou=users for uniqueMember group attribute
- Use NameFormat/GroupFormat when constructing RDN attributes
- Brian Candler (@candlerb) completed the above by making sure they were supported where it matters
- The directory structure can be navigated up and down using any LDAP browser
- searchMaybePosixAccounts: add filter to respect searchBaseDN (#304)
- Feature: introduce legacy behavior, based on past versions (#318)
  
Security/Updates:
- Bump golang.org/x/crypto in /v2
- Update Go Versions to 1.19, 1.20 (#314)
- Added anonymousdse backend option to config sample (#316)
  
Qualify of life:
- Add devcontainer (#306)
- Add help command to Makefile (#307)
- Update go.work to 1.19 (#315)
2023-07-03 18:59:23 -07:00
Chris F Ravenscroft 7f925565a6 Merge branch 'candlerb-candlerb/rdn' into dev 2023-07-03 22:43:22 +00:00
Chris F Ravenscroft f5ea3d93bf Merge branch 'candlerb/rdn' of github.com:candlerb/glauth into candlerb-candlerb/rdn 2023-07-03 22:37:56 +00:00
Chris F Ravenscroft 34a6b30b8f Feature: introduce legacy behavior, based on past versions (#318) 2023-07-03 15:33:01 -07:00
Chris F Ravenscroft 9bbad511cb Merge branch 'candlerb-candlerb/uniquemember' into dev 2023-07-03 14:59:17 -07:00
Nick Borgers 7cef07931c Update go.work to 1.19 (#315) 2023-05-28 13:18:00 -07:00
Konstantin Shalygin 37fa74892e Added anonymousdse backend option to config sample (#316)
Fixes #83
2023-05-28 13:12:07 -07:00
Nick Borgers efbd64504d add help command to Makefile (#307)
* add fanciness to Makefile
2023-04-22 13:36:40 -07:00
Nick Borgers 1920c4324f Add devcontainer (#306)
* add option for devcontainer
2023-04-22 13:31:35 -07:00
Chris F Ravenscroft 7e7a5f59d1 Update Go Versions to 1.19, 1.20 (#314)
* Update Go Versions to 1.19, 1.20
2023-04-22 13:28:21 -07:00
NW 83f4305e36 searchMaybePosixAccounts: add filter to respect searchBaseDN (#304)
* searchMaybePosixAccounts: add filter to respect searchBaseDN
2023-04-22 13:09:05 -07:00
dependabot[bot] b45259d567 Bump golang.org/x/crypto from 0.0.0-20210711020723-a769d52b0f97 to 0.1.0 in /v2 (#303)
* Bump golang.org/x/crypto in /v2

Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.0.0-20210711020723-a769d52b0f97 to 0.1.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/commits/v0.1.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-22 13:01:02 -07:00
lgtm-com[bot] 3f9f67375f Add CodeQL workflow for GitHub code scanning (#295) 2023-04-22 12:56:32 -07:00
Sven Wick cb761e8e70 Update sample-simple.cfg (#275)
Fix typo
2023-04-22 12:44:03 -07:00
securitytime 7c3314a504 Update README.md (#266)
Fixed a broken link

Co-authored-by: Chris F Ravenscroft <chris@voilaweb.com>
2023-04-22 12:38:28 -07:00
Brian Candler 690917e4e0 Use NameFormat/GroupFormat when constructing RDN attributes
Before:

dn: ou=vpn,ou=users,dc=glauth,dc=com
cn: vpn

After:

dn: ou=vpn,ou=users,dc=glauth,dc=com
ou: vpn    <<< THIS HAS CHANGED

NOTE: whereas previously a search filter (cn=vpn) would have found the
group, now you need (ou=vpn)
2023-04-12 16:23:55 +01:00
Brian Candler 0ccf4926ff Construct DNs under ou=users for uniqueMember group attribute
Before:

dn: ou=vpn,ou=users,dc=glauth,dc=com
...
uniqueMember: cn=hackers,ou=superheros,dc=glauth,dc=com

After:

dn: ou=vpn,ou=users,dc=glauth,dc=com
...
uniqueMember: cn=hackers,ou=superheros,ou=users,dc=glauth,dc=com
                                       ^^^^^^^^
2023-04-12 16:01:07 +01:00
Chris F Ravenscroft 598a688214 CodeSee integration 2022-12-24 13:42:20 -08:00
Chris F Ravenscroft 0e7769ff84 No SysLog on Windows (#289) (#290) 2022-11-12 14:59:43 -08:00
Chris F Ravenscroft 6500bbaf36 No SysLog on Windows (#289) 2022-11-12 14:55:55 -08:00
Chris F Ravenscroft e92197ac08 Resync dev to master 2022-11-06 20:38:20 -08:00
Chris F Ravenscroft 65385fb82d Resync dev without old crud 2022-11-06 20:34:11 -08:00
Chris F Ravenscroft aadf150fec Dev -> Master get rid of old Docker files (#288)
* Reverse pull request from master to dev.

Reverse pull request.

* Fix/docker latest tag (#260)

* Dev (#254)

* use functional options pattern to inject logr (#124)

* use functional options pattern to inject logr

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* cleanup log formatting

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* allow clean shutdown (#126)

* fix owncloud posix query, log message and provisioning api results (#128)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* don't hardcode graphapi endpoint

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* add support for write handlers (#135)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* config: match shadowaccount objectlcass (#136)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Makefile compatiblility (#134)

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* update travis.yml (#154)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* check owncloud status code is ok (#153)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Bulid and push multiarch docker images (#142)

Fix #141.

With this change, a github action is added that builds a multiarch docker image on every commit, supporting x86_64, aarch64 and arm/v7.

When the action is triggered on a release or tag, it also uploads the versioned image to github container image registry.

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* don't mix graph and provisioning api (#157)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Reuse http client as much as possible and allow insecure transport (#160)

* Allow using configmaps when deploying in kubernetes (#161)

* Ensure config watcher also works in kubernetes

When using configmaps in kubernetes, the file is a symlink, and then
file-watcher is not opdated with a write event. Instead it receives a
CHMOD and a REMOVE event.

This change adds two things
1) Removal of the current wacther and adding of a new watcher for the
same path
2) The do-reload conditional is updated to also include the remove
event.

* Align write and removed conditionals

* Fix API server json formating (#163) (#164)

* Update README.md (#167)

Fixing bad port in quickstart

* Use port 3893 with ldapsearch in example (#150)

* feat: add flags for ldap listen addresses (#169)

Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>

* Assets build fix. (#171)

* Docker build fix.

* Makefile fix.

* update readme, config and deps (#170)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Remove byanke's donation fields

* Database plugins (#133)

This commit actually covers a few items. In future commits, I will keep features distinct. This is only happening this time around due to how long it took to merge this branch.

Covered:

- Database plugins (at this time: SQLite, MySQL, Postgres)
- Backends acting as middleware: added the [[Backends]] configuration directive while retaining backward compatibility with [Backend]
- Schema introspection (root DSE query with base scope)
- When proxying, insert queried attribute back in upstream response, if absent, so that the LDAP library does not filter out all entries
- When chaining backends, any backend can be used to inject OTP value in password, before reaching a non-OTP-aware backend (currently guarded by a True statement in case we find an issue (I did thoroughly test but you never know))
- Handling of special "1.1" attributes filter meaning "I do not want attributes" (RFC 4511, 4.5.1.8)
- Support for "want types only" queries, even when proxying

* Mac M1 Support and LDAP Req Attributes (#192)

* Mac M1 Support

* Augmented root DSS and schema discovery based on content of schema directory

* LDAP workaround where req. attribute gets injected in response now works with combined filters

* SubSchema query can return a minimal set, freeipa or openldap's schemas

* Feature/upgrade ldap library version (#194)

* Stronger, salted paswords using bcrypt. (#195)

* Fixed badges in README file and added a couple improvements (#196)

* fix lock for ownCloud / graph backend (#198)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* the config struct is only needed by the config backend (#199)

* Variable "unixid" is now respectively "UIDNumber" and "GIDNumber" for… (#201)

* fix: keep watch config file when changed,renamed,removed (#189)

I will need to run `glauth.go` through `gofmt` myself but that is fine.

* Returning when unable to start config watcher (#203)

* Introducing goconvey testing and refactoring of config and ... (#204)

* Introducing goconvey testing and refactoring of config and plugin backends.

* gofmt, oops

* Refactored re-insertion of requested attributes

* Allow bind operations with no group provided (#205)

* Support for userPrincipalName binding and browsing. (#206)

* Support for userPrincipalName binding and browsing.

* Fixed travis CI test for userprincipalname

* fixing travis tests for more users

* Rate limit after failed binds (#207)

* Capabilities -- part 2 (#214) + do not dump config at startup

* Attempting migration from Travis to Github Actions

* Migration Step#2

* Migration Step#3

* Migration Step#4

* Migration Step#5

* Migration Step#6

* Migration Step#7

* Migration Step#8

* Migration Step#9

* Migration Step#9

* Migration dev...

* Migration dev...

* Migration dev...

* Migration dev...

* Docker with plugins, first step (#215)

* Docker with plugins, first step

* Build better docker images, including plugins

* Merge back from dev to feature branch step #2

* Added capabilities to CI

* - Now creating two docker images, including one with plugins
- Moving from Travis CI to GitHub Actions

* Plugins now run on distroless (#217)

* Getting rid of Travis CI now that it doesn't support FOSS anymore.

* Internal Stats -- performance view (#221)

* Local merge

* Update CI to forget about bindata

* Forgot to cleanup the cleanup...

* assets: use stdlib "embed" package (#200)

This should make it easier to include GLAuth in other projects using `go get`

* 'Airgapping' web assets for security and preventing breakage (#227)

* v2 -- V2 hierarchy (#228)

* Search refactoring, tree traversal and scope correctness (#229)

Search refactoring, tree traversal and scope correctness.

* Feature/multi cfg (#233)

* Feature/custom attributes (#240)

* Makefiles: platform releases (#241)

* Feature/release script (#242)

* Bug/fix docker build in v2 (#244)

* Fix #246 and #252

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>

* Cleanup

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>

* Feature/dbsshkeys (#262)

* Dev -> Master after v1/v2 cleanup (#261)

* Reverse pull request from master to dev.

Reverse pull request.

* Fix/docker latest tag (#260)

* Dev (#254)

* use functional options pattern to inject logr (#124)

* use functional options pattern to inject logr

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* cleanup log formatting

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* allow clean shutdown (#126)

* fix owncloud posix query, log message and provisioning api results (#128)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* don't hardcode graphapi endpoint

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* add support for write handlers (#135)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* config: match shadowaccount objectlcass (#136)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Makefile compatiblility (#134)

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* update travis.yml (#154)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* check owncloud status code is ok (#153)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Bulid and push multiarch docker images (#142)

Fix #141.

With this change, a github action is added that builds a multiarch docker image on every commit, supporting x86_64, aarch64 and arm/v7.

When the action is triggered on a release or tag, it also uploads the versioned image to github container image registry.

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* don't mix graph and provisioning api (#157)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Reuse http client as much as possible and allow insecure transport (#160)

* Allow using configmaps when deploying in kubernetes (#161)

* Ensure config watcher also works in kubernetes

When using configmaps in kubernetes, the file is a symlink, and then
file-watcher is not opdated with a write event. Instead it receives a
CHMOD and a REMOVE event.

This change adds two things
1) Removal of the current wacther and adding of a new watcher for the
same path
2) The do-reload conditional is updated to also include the remove
event.

* Align write and removed conditionals

* Fix API server json formating (#163) (#164)

* Update README.md (#167)

Fixing bad port in quickstart

* Use port 3893 with ldapsearch in example (#150)

* feat: add flags for ldap listen addresses (#169)

Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>

* Assets build fix. (#171)

* Docker build fix.

* Makefile fix.

* update readme, config and deps (#170)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Remove byanke's donation fields

* Database plugins (#133)

This commit actually covers a few items. In future commits, I will keep features distinct. This is only happening this time around due to how long it took to merge this branch.

Covered:

- Database plugins (at this time: SQLite, MySQL, Postgres)
- Backends acting as middleware: added the [[Backends]] configuration directive while retaining backward compatibility with [Backend]
- Schema introspection (root DSE query with base scope)
- When proxying, insert queried attribute back in upstream response, if absent, so that the LDAP library does not filter out all entries
- When chaining backends, any backend can be used to inject OTP value in password, before reaching a non-OTP-aware backend (currently guarded by a True statement in case we find an issue (I did thoroughly test but you never know))
- Handling of special "1.1" attributes filter meaning "I do not want attributes" (RFC 4511, 4.5.1.8)
- Support for "want types only" queries, even when proxying

* Mac M1 Support and LDAP Req Attributes (#192)

* Mac M1 Support

* Augmented root DSS and schema discovery based on content of schema directory

* LDAP workaround where req. attribute gets injected in response now works with combined filters

* SubSchema query can return a minimal set, freeipa or openldap's schemas

* Feature/upgrade ldap library version (#194)

* Stronger, salted paswords using bcrypt. (#195)

* Fixed badges in README file and added a couple improvements (#196)

* fix lock for ownCloud / graph backend (#198)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* the config struct is only needed by the config backend (#199)

* Variable "unixid" is now respectively "UIDNumber" and "GIDNumber" for… (#201)

* fix: keep watch config file when changed,renamed,removed (#189)

I will need to run `glauth.go` through `gofmt` myself but that is fine.

* Returning when unable to start config watcher (#203)

* Introducing goconvey testing and refactoring of config and ... (#204)

* Introducing goconvey testing and refactoring of config and plugin backends.

* gofmt, oops

* Refactored re-insertion of requested attributes

* Allow bind operations with no group provided (#205)

* Support for userPrincipalName binding and browsing. (#206)

* Support for userPrincipalName binding and browsing.

* Fixed travis CI test for userprincipalname

* fixing travis tests for more users

* Rate limit after failed binds (#207)

* Capabilities -- part 2 (#214) + do not dump config at startup

* Attempting migration from Travis to Github Actions

* Migration Step#2

* Migration Step#3

* Migration Step#4

* Migration Step#5

* Migration Step#6

* Migration Step#7

* Migration Step#8

* Migration Step#9

* Migration Step#9

* Migration dev...

* Migration dev...

* Migration dev...

* Migration dev...

* Docker with plugins, first step (#215)

* Docker with plugins, first step

* Build better docker images, including plugins

* Merge back from dev to feature branch step #2

* Added capabilities to CI

* - Now creating two docker images, including one with plugins
- Moving from Travis CI to GitHub Actions

* Plugins now run on distroless (#217)

* Getting rid of Travis CI now that it doesn't support FOSS anymore.

* Internal Stats -- performance view (#221)

* Local merge

* Update CI to forget about bindata

* Forgot to cleanup the cleanup...

* assets: use stdlib "embed" package (#200)

This should make it easier to include GLAuth in other projects using `go get`

* 'Airgapping' web assets for security and preventing breakage (#227)

* v2 -- V2 hierarchy (#228)

* Search refactoring, tree traversal and scope correctness (#229)

Search refactoring, tree traversal and scope correctness.

* Feature/multi cfg (#233)

* Feature/custom attributes (#240)

* Makefiles: platform releases (#241)

* Feature/release script (#242)

* Bug/fix docker build in v2 (#244)

* Fix #246 and #252

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>

* Cleanup

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>

* SSH Keys support in database plugins

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>

* Better plugin build for darwin

* add link to documentation, only discovered it while perusing issues list (#276)

* Plugin: Unix PAM Authentication (https://github.com/glauth/glauth/pull/263) (#277)

* Plugin: Unix PAM Authentication (#263)

* Add plugin using pam authentication

Adds an additional plugin which us authenticating against
the PAM unix backend and exposing users and groups local
to the machine glauth is running on.

This can be used to expose local users for authentication
in other services which support ldap only.

* plugins: pam: Rewrite Bind() to use ldapopshelper

Modernizes the implementation of Bind() to make use of the helper
functions provided by LDAPOpsHelper.

In order to support custom authentication the existing config.User
has received an additional PassAppCustom property which allows to
specify a custom authentication callback for a user.

In case of the PAM backend this will be used to authenticate against
the local PAM database.

* plugins: pam: Rewrite Search() to use ldapopshelper

Modernizes the implementation of Search() to make use of the helper
functions provided by LDAPOpsHelper.

* plugins: pam: Capability through group membership

Adds a configuration option which decides if a user gets the search
capability or not based on the group memberships of a user.

* plugins: pam: Apply formatting

Runs gofmt and go get on all changes done earlier

* plugins: pam: Address feedback from CodeClimate

- reduce code similarity
- document new exports
- address casing of variables and functions
- reduce complexity of FindPosixGroups()
- reduce complexity of FindPosixAccounts()
- fix else branch in ldapopshelper

Co-authored-by: Marius Zwicker <marius.zwicker@mlba-team.de>

* Updated README for pam plugin

* Updated README for pam plugin

Co-authored-by: Marius Zwicker <marius@mlba-team.de>
Co-authored-by: Marius Zwicker <marius.zwicker@mlba-team.de>

* Removing db plugins

* Changing plugind package

* Remove main frmo plugin

* Move plugins to their own repos (#283)

* Plugins release build delegated to plugin Makefile

* Build and push docker containers

* README points to documentation

* Shortened README

* Prometheus exported (#284)

* Prometheus exporter

* Feature/zerolog (#285)

* Zerolog

* Adjusted logging levels

* Structured logging including ldap library

* Feature/check config (#286)

Add config check `--check-config`

* Removed last trace of old docker files

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>
Co-authored-by: dlitster <davidlitster@gmail.com>
Co-authored-by: Marius Zwicker <marius@mlba-team.de>
Co-authored-by: Marius Zwicker <marius.zwicker@mlba-team.de>
2022-11-06 20:10:22 -08:00
Chris F Ravenscroft 122c86c446 Merge branch 'master' into dev 2022-11-06 20:03:23 -08:00
Chris F Ravenscroft 0d03b226b6 Removed last trace of old docker files 2022-11-06 20:01:06 -08:00
Chris F Ravenscroft f41b5ee19e Dev to Master for 2.2.0-RC1 (#287)
* Better plugin build for darwin

* add link to documentation, only discovered it while perusing issues list (#276)

* Plugin: Unix PAM Authentication (https://github.com/glauth/glauth/pull/263) (#277)

* Plugin: Unix PAM Authentication (#263)

* Add plugin using pam authentication

Adds an additional plugin which us authenticating against
the PAM unix backend and exposing users and groups local
to the machine glauth is running on.

This can be used to expose local users for authentication
in other services which support ldap only.

* plugins: pam: Rewrite Bind() to use ldapopshelper

Modernizes the implementation of Bind() to make use of the helper
functions provided by LDAPOpsHelper.

In order to support custom authentication the existing config.User
has received an additional PassAppCustom property which allows to
specify a custom authentication callback for a user.

In case of the PAM backend this will be used to authenticate against
the local PAM database.

* plugins: pam: Rewrite Search() to use ldapopshelper

Modernizes the implementation of Search() to make use of the helper
functions provided by LDAPOpsHelper.

* plugins: pam: Capability through group membership

Adds a configuration option which decides if a user gets the search
capability or not based on the group memberships of a user.

* plugins: pam: Apply formatting

Runs gofmt and go get on all changes done earlier

* plugins: pam: Address feedback from CodeClimate

- reduce code similarity
- document new exports
- address casing of variables and functions
- reduce complexity of FindPosixGroups()
- reduce complexity of FindPosixAccounts()
- fix else branch in ldapopshelper

Co-authored-by: Marius Zwicker <marius.zwicker@mlba-team.de>

* Updated README for pam plugin

* Updated README for pam plugin

Co-authored-by: Marius Zwicker <marius@mlba-team.de>
Co-authored-by: Marius Zwicker <marius.zwicker@mlba-team.de>

* Removing db plugins

* Changing plugind package

* Remove main frmo plugin

* Move plugins to their own repos (#283)

* Plugins release build delegated to plugin Makefile

* Build and push docker containers

* README points to documentation

* Shortened README

* Prometheus exported (#284)

* Prometheus exporter

* Feature/zerolog (#285)

* Zerolog

* Adjusted logging levels

* Structured logging including ldap library

* Feature/check config (#286)

Add config check `--check-config`

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>
Co-authored-by: dlitster <davidlitster@gmail.com>
Co-authored-by: Marius Zwicker <marius@mlba-team.de>
Co-authored-by: Marius Zwicker <marius.zwicker@mlba-team.de>
2022-11-06 19:57:58 -08:00
Chris F Ravenscroft aad166b3ec Merge branch 'master' into dev 2022-11-06 19:50:51 -08:00
Chris F Ravenscroft 6b14714eb4 Feature/check config (#286)
Add config check `--check-config`
2022-11-06 00:36:51 -07:00
Chris F Ravenscroft 3db5c5e54a Feature/zerolog (#285)
* Zerolog

* Adjusted logging levels

* Structured logging including ldap library
2022-11-06 00:22:14 -07:00
Chris F Ravenscroft 1a78b51174 Prometheus exported (#284)
* Prometheus exporter
2022-10-30 23:48:16 -07:00
Chris F Ravenscroft 9865de68ac Shortened README 2022-10-29 22:56:53 -07:00
Chris F Ravenscroft 32750d2abd Move plugins to their own repos (#283)
* Plugins release build delegated to plugin Makefile

* Build and push docker containers

* README points to documentation
2022-10-29 15:16:57 -07:00
Chris F Ravenscroft 021324c775 Remove main frmo plugin 2022-10-17 20:47:57 -07:00
Chris F Ravenscroft edaddca4e7 Changing plugind package 2022-10-17 20:46:02 -07:00
Chris F Ravenscroft 1af3cf4f69 Removing db plugins 2022-10-17 20:32:38 -07:00
Chris F Ravenscroft 3e2e24cadb Plugin: Unix PAM Authentication (https://github.com/glauth/glauth/pull/263) (#277)
* Plugin: Unix PAM Authentication (#263)

* Add plugin using pam authentication

Adds an additional plugin which us authenticating against
the PAM unix backend and exposing users and groups local
to the machine glauth is running on.

This can be used to expose local users for authentication
in other services which support ldap only.

* plugins: pam: Rewrite Bind() to use ldapopshelper

Modernizes the implementation of Bind() to make use of the helper
functions provided by LDAPOpsHelper.

In order to support custom authentication the existing config.User
has received an additional PassAppCustom property which allows to
specify a custom authentication callback for a user.

In case of the PAM backend this will be used to authenticate against
the local PAM database.

* plugins: pam: Rewrite Search() to use ldapopshelper

Modernizes the implementation of Search() to make use of the helper
functions provided by LDAPOpsHelper.

* plugins: pam: Capability through group membership

Adds a configuration option which decides if a user gets the search
capability or not based on the group memberships of a user.

* plugins: pam: Apply formatting

Runs gofmt and go get on all changes done earlier

* plugins: pam: Address feedback from CodeClimate

- reduce code similarity
- document new exports
- address casing of variables and functions
- reduce complexity of FindPosixGroups()
- reduce complexity of FindPosixAccounts()
- fix else branch in ldapopshelper

Co-authored-by: Marius Zwicker <marius.zwicker@mlba-team.de>

* Updated README for pam plugin

* Updated README for pam plugin

Co-authored-by: Marius Zwicker <marius@mlba-team.de>
Co-authored-by: Marius Zwicker <marius.zwicker@mlba-team.de>
2022-08-14 20:58:36 -07:00
dlitster 249388fdd1 add link to documentation, only discovered it while perusing issues list (#276) 2022-08-14 16:50:50 -07:00
Chris F Ravenscroft 45651645e7 Better plugin build for darwin 2022-06-11 20:53:01 +00:00
Chris F Ravenscroft 42ff05f141 Feature/dbsshkeys (#262)
* Dev -> Master after v1/v2 cleanup (#261)

* Reverse pull request from master to dev.

Reverse pull request.

* Fix/docker latest tag (#260)

* Dev (#254)

* use functional options pattern to inject logr (#124)

* use functional options pattern to inject logr

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* cleanup log formatting

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* allow clean shutdown (#126)

* fix owncloud posix query, log message and provisioning api results (#128)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* don't hardcode graphapi endpoint

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* add support for write handlers (#135)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* config: match shadowaccount objectlcass (#136)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Makefile compatiblility (#134)

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* update travis.yml (#154)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* check owncloud status code is ok (#153)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Bulid and push multiarch docker images (#142)

Fix #141.

With this change, a github action is added that builds a multiarch docker image on every commit, supporting x86_64, aarch64 and arm/v7.

When the action is triggered on a release or tag, it also uploads the versioned image to github container image registry.

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* don't mix graph and provisioning api (#157)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Reuse http client as much as possible and allow insecure transport (#160)

* Allow using configmaps when deploying in kubernetes (#161)

* Ensure config watcher also works in kubernetes

When using configmaps in kubernetes, the file is a symlink, and then
file-watcher is not opdated with a write event. Instead it receives a
CHMOD and a REMOVE event.

This change adds two things
1) Removal of the current wacther and adding of a new watcher for the
same path
2) The do-reload conditional is updated to also include the remove
event.

* Align write and removed conditionals

* Fix API server json formating (#163) (#164)

* Update README.md (#167)

Fixing bad port in quickstart

* Use port 3893 with ldapsearch in example (#150)

* feat: add flags for ldap listen addresses (#169)

Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>

* Assets build fix. (#171)

* Docker build fix.

* Makefile fix.

* update readme, config and deps (#170)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Remove byanke's donation fields

* Database plugins (#133)

This commit actually covers a few items. In future commits, I will keep features distinct. This is only happening this time around due to how long it took to merge this branch.

Covered:

- Database plugins (at this time: SQLite, MySQL, Postgres)
- Backends acting as middleware: added the [[Backends]] configuration directive while retaining backward compatibility with [Backend]
- Schema introspection (root DSE query with base scope)
- When proxying, insert queried attribute back in upstream response, if absent, so that the LDAP library does not filter out all entries
- When chaining backends, any backend can be used to inject OTP value in password, before reaching a non-OTP-aware backend (currently guarded by a True statement in case we find an issue (I did thoroughly test but you never know))
- Handling of special "1.1" attributes filter meaning "I do not want attributes" (RFC 4511, 4.5.1.8)
- Support for "want types only" queries, even when proxying

* Mac M1 Support and LDAP Req Attributes (#192)

* Mac M1 Support

* Augmented root DSS and schema discovery based on content of schema directory

* LDAP workaround where req. attribute gets injected in response now works with combined filters

* SubSchema query can return a minimal set, freeipa or openldap's schemas

* Feature/upgrade ldap library version (#194)

* Stronger, salted paswords using bcrypt. (#195)

* Fixed badges in README file and added a couple improvements (#196)

* fix lock for ownCloud / graph backend (#198)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* the config struct is only needed by the config backend (#199)

* Variable "unixid" is now respectively "UIDNumber" and "GIDNumber" for… (#201)

* fix: keep watch config file when changed,renamed,removed (#189)

I will need to run `glauth.go` through `gofmt` myself but that is fine.

* Returning when unable to start config watcher (#203)

* Introducing goconvey testing and refactoring of config and ... (#204)

* Introducing goconvey testing and refactoring of config and plugin backends.

* gofmt, oops

* Refactored re-insertion of requested attributes

* Allow bind operations with no group provided (#205)

* Support for userPrincipalName binding and browsing. (#206)

* Support for userPrincipalName binding and browsing.

* Fixed travis CI test for userprincipalname

* fixing travis tests for more users

* Rate limit after failed binds (#207)

* Capabilities -- part 2 (#214) + do not dump config at startup

* Attempting migration from Travis to Github Actions

* Migration Step#2

* Migration Step#3

* Migration Step#4

* Migration Step#5

* Migration Step#6

* Migration Step#7

* Migration Step#8

* Migration Step#9

* Migration Step#9

* Migration dev...

* Migration dev...

* Migration dev...

* Migration dev...

* Docker with plugins, first step (#215)

* Docker with plugins, first step

* Build better docker images, including plugins

* Merge back from dev to feature branch step #2

* Added capabilities to CI

* - Now creating two docker images, including one with plugins
- Moving from Travis CI to GitHub Actions

* Plugins now run on distroless (#217)

* Getting rid of Travis CI now that it doesn't support FOSS anymore.

* Internal Stats -- performance view (#221)

* Local merge

* Update CI to forget about bindata

* Forgot to cleanup the cleanup...

* assets: use stdlib "embed" package (#200)

This should make it easier to include GLAuth in other projects using `go get`

* 'Airgapping' web assets for security and preventing breakage (#227)

* v2 -- V2 hierarchy (#228)

* Search refactoring, tree traversal and scope correctness (#229)

Search refactoring, tree traversal and scope correctness.

* Feature/multi cfg (#233)

* Feature/custom attributes (#240)

* Makefiles: platform releases (#241)

* Feature/release script (#242)

* Bug/fix docker build in v2 (#244)

* Fix #246 and #252

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>

* Cleanup

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>

* SSH Keys support in database plugins

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>
2022-05-08 19:33:27 -07:00
Chris F Ravenscroft 598d746ee8 Dev -> Master after v1/v2 cleanup (#261)
* Reverse pull request from master to dev.

Reverse pull request.

* Fix/docker latest tag (#260)

* Dev (#254)

* use functional options pattern to inject logr (#124)

* use functional options pattern to inject logr

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* cleanup log formatting

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* allow clean shutdown (#126)

* fix owncloud posix query, log message and provisioning api results (#128)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* don't hardcode graphapi endpoint

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* add support for write handlers (#135)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* config: match shadowaccount objectlcass (#136)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Makefile compatiblility (#134)

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* update travis.yml (#154)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* check owncloud status code is ok (#153)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Bulid and push multiarch docker images (#142)

Fix #141.

With this change, a github action is added that builds a multiarch docker image on every commit, supporting x86_64, aarch64 and arm/v7.

When the action is triggered on a release or tag, it also uploads the versioned image to github container image registry.

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* don't mix graph and provisioning api (#157)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Reuse http client as much as possible and allow insecure transport (#160)

* Allow using configmaps when deploying in kubernetes (#161)

* Ensure config watcher also works in kubernetes

When using configmaps in kubernetes, the file is a symlink, and then
file-watcher is not opdated with a write event. Instead it receives a
CHMOD and a REMOVE event.

This change adds two things
1) Removal of the current wacther and adding of a new watcher for the
same path
2) The do-reload conditional is updated to also include the remove
event.

* Align write and removed conditionals

* Fix API server json formating (#163) (#164)

* Update README.md (#167)

Fixing bad port in quickstart

* Use port 3893 with ldapsearch in example (#150)

* feat: add flags for ldap listen addresses (#169)

Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>

* Assets build fix. (#171)

* Docker build fix.

* Makefile fix.

* update readme, config and deps (#170)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Remove byanke's donation fields

* Database plugins (#133)

This commit actually covers a few items. In future commits, I will keep features distinct. This is only happening this time around due to how long it took to merge this branch.

Covered:

- Database plugins (at this time: SQLite, MySQL, Postgres)
- Backends acting as middleware: added the [[Backends]] configuration directive while retaining backward compatibility with [Backend]
- Schema introspection (root DSE query with base scope)
- When proxying, insert queried attribute back in upstream response, if absent, so that the LDAP library does not filter out all entries
- When chaining backends, any backend can be used to inject OTP value in password, before reaching a non-OTP-aware backend (currently guarded by a True statement in case we find an issue (I did thoroughly test but you never know))
- Handling of special "1.1" attributes filter meaning "I do not want attributes" (RFC 4511, 4.5.1.8)
- Support for "want types only" queries, even when proxying

* Mac M1 Support and LDAP Req Attributes (#192)

* Mac M1 Support

* Augmented root DSS and schema discovery based on content of schema directory

* LDAP workaround where req. attribute gets injected in response now works with combined filters

* SubSchema query can return a minimal set, freeipa or openldap's schemas

* Feature/upgrade ldap library version (#194)

* Stronger, salted paswords using bcrypt. (#195)

* Fixed badges in README file and added a couple improvements (#196)

* fix lock for ownCloud / graph backend (#198)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* the config struct is only needed by the config backend (#199)

* Variable "unixid" is now respectively "UIDNumber" and "GIDNumber" for… (#201)

* fix: keep watch config file when changed,renamed,removed (#189)

I will need to run `glauth.go` through `gofmt` myself but that is fine.

* Returning when unable to start config watcher (#203)

* Introducing goconvey testing and refactoring of config and ... (#204)

* Introducing goconvey testing and refactoring of config and plugin backends.

* gofmt, oops

* Refactored re-insertion of requested attributes

* Allow bind operations with no group provided (#205)

* Support for userPrincipalName binding and browsing. (#206)

* Support for userPrincipalName binding and browsing.

* Fixed travis CI test for userprincipalname

* fixing travis tests for more users

* Rate limit after failed binds (#207)

* Capabilities -- part 2 (#214) + do not dump config at startup

* Attempting migration from Travis to Github Actions

* Migration Step#2

* Migration Step#3

* Migration Step#4

* Migration Step#5

* Migration Step#6

* Migration Step#7

* Migration Step#8

* Migration Step#9

* Migration Step#9

* Migration dev...

* Migration dev...

* Migration dev...

* Migration dev...

* Docker with plugins, first step (#215)

* Docker with plugins, first step

* Build better docker images, including plugins

* Merge back from dev to feature branch step #2

* Added capabilities to CI

* - Now creating two docker images, including one with plugins
- Moving from Travis CI to GitHub Actions

* Plugins now run on distroless (#217)

* Getting rid of Travis CI now that it doesn't support FOSS anymore.

* Internal Stats -- performance view (#221)

* Local merge

* Update CI to forget about bindata

* Forgot to cleanup the cleanup...

* assets: use stdlib "embed" package (#200)

This should make it easier to include GLAuth in other projects using `go get`

* 'Airgapping' web assets for security and preventing breakage (#227)

* v2 -- V2 hierarchy (#228)

* Search refactoring, tree traversal and scope correctness (#229)

Search refactoring, tree traversal and scope correctness.

* Feature/multi cfg (#233)

* Feature/custom attributes (#240)

* Makefiles: platform releases (#241)

* Feature/release script (#242)

* Bug/fix docker build in v2 (#244)

* Fix #246 and #252

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>

* Cleanup

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>
2022-05-07 12:39:08 -07:00
Chris F Ravenscroft 6800da8bf8 Merge branch 'master' into dev 2022-05-07 12:29:52 -07:00
Chris F Ravenscroft 57ee724556 Fix/docker latest tag (#260)
* Dev (#254)

* use functional options pattern to inject logr (#124)

* use functional options pattern to inject logr

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* cleanup log formatting

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* allow clean shutdown (#126)

* fix owncloud posix query, log message and provisioning api results (#128)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* don't hardcode graphapi endpoint

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* add support for write handlers (#135)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* config: match shadowaccount objectlcass (#136)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Makefile compatiblility (#134)

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* update travis.yml (#154)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* check owncloud status code is ok (#153)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Bulid and push multiarch docker images (#142)

Fix #141.

With this change, a github action is added that builds a multiarch docker image on every commit, supporting x86_64, aarch64 and arm/v7.

When the action is triggered on a release or tag, it also uploads the versioned image to github container image registry.

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* don't mix graph and provisioning api (#157)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Reuse http client as much as possible and allow insecure transport (#160)

* Allow using configmaps when deploying in kubernetes (#161)

* Ensure config watcher also works in kubernetes

When using configmaps in kubernetes, the file is a symlink, and then
file-watcher is not opdated with a write event. Instead it receives a
CHMOD and a REMOVE event.

This change adds two things
1) Removal of the current wacther and adding of a new watcher for the
same path
2) The do-reload conditional is updated to also include the remove
event.

* Align write and removed conditionals

* Fix API server json formating (#163) (#164)

* Update README.md (#167)

Fixing bad port in quickstart

* Use port 3893 with ldapsearch in example (#150)

* feat: add flags for ldap listen addresses (#169)

Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>

* Assets build fix. (#171)

* Docker build fix.

* Makefile fix.

* update readme, config and deps (#170)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Remove byanke's donation fields

* Database plugins (#133)

This commit actually covers a few items. In future commits, I will keep features distinct. This is only happening this time around due to how long it took to merge this branch.

Covered:

- Database plugins (at this time: SQLite, MySQL, Postgres)
- Backends acting as middleware: added the [[Backends]] configuration directive while retaining backward compatibility with [Backend]
- Schema introspection (root DSE query with base scope)
- When proxying, insert queried attribute back in upstream response, if absent, so that the LDAP library does not filter out all entries
- When chaining backends, any backend can be used to inject OTP value in password, before reaching a non-OTP-aware backend (currently guarded by a True statement in case we find an issue (I did thoroughly test but you never know))
- Handling of special "1.1" attributes filter meaning "I do not want attributes" (RFC 4511, 4.5.1.8)
- Support for "want types only" queries, even when proxying

* Mac M1 Support and LDAP Req Attributes (#192)

* Mac M1 Support

* Augmented root DSS and schema discovery based on content of schema directory

* LDAP workaround where req. attribute gets injected in response now works with combined filters

* SubSchema query can return a minimal set, freeipa or openldap's schemas

* Feature/upgrade ldap library version (#194)

* Stronger, salted paswords using bcrypt. (#195)

* Fixed badges in README file and added a couple improvements (#196)

* fix lock for ownCloud / graph backend (#198)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* the config struct is only needed by the config backend (#199)

* Variable "unixid" is now respectively "UIDNumber" and "GIDNumber" for… (#201)

* fix: keep watch config file when changed,renamed,removed (#189)

I will need to run `glauth.go` through `gofmt` myself but that is fine.

* Returning when unable to start config watcher (#203)

* Introducing goconvey testing and refactoring of config and ... (#204)

* Introducing goconvey testing and refactoring of config and plugin backends.

* gofmt, oops

* Refactored re-insertion of requested attributes

* Allow bind operations with no group provided (#205)

* Support for userPrincipalName binding and browsing. (#206)

* Support for userPrincipalName binding and browsing.

* Fixed travis CI test for userprincipalname

* fixing travis tests for more users

* Rate limit after failed binds (#207)

* Capabilities -- part 2 (#214) + do not dump config at startup

* Attempting migration from Travis to Github Actions

* Migration Step#2

* Migration Step#3

* Migration Step#4

* Migration Step#5

* Migration Step#6

* Migration Step#7

* Migration Step#8

* Migration Step#9

* Migration Step#9

* Migration dev...

* Migration dev...

* Migration dev...

* Migration dev...

* Docker with plugins, first step (#215)

* Docker with plugins, first step

* Build better docker images, including plugins

* Merge back from dev to feature branch step #2

* Added capabilities to CI

* - Now creating two docker images, including one with plugins
- Moving from Travis CI to GitHub Actions

* Plugins now run on distroless (#217)

* Getting rid of Travis CI now that it doesn't support FOSS anymore.

* Internal Stats -- performance view (#221)

* Local merge

* Update CI to forget about bindata

* Forgot to cleanup the cleanup...

* assets: use stdlib "embed" package (#200)

This should make it easier to include GLAuth in other projects using `go get`

* 'Airgapping' web assets for security and preventing breakage (#227)

* v2 -- V2 hierarchy (#228)

* Search refactoring, tree traversal and scope correctness (#229)

Search refactoring, tree traversal and scope correctness.

* Feature/multi cfg (#233)

* Feature/custom attributes (#240)

* Makefiles: platform releases (#241)

* Feature/release script (#242)

* Bug/fix docker build in v2 (#244)

* Fix #246 and #252

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>

* Cleanup

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>
2022-05-07 12:28:51 -07:00
Chris F Ravenscroft ea4cd57233 Reverse pull request from master to dev.
Reverse pull request.
2022-02-27 17:29:42 -08:00
Chris F Ravenscroft 5d79fd9b03 Merge branch 'dev' into master 2022-02-27 17:28:33 -08:00
Chris F Ravenscroft 385cc335ca Dev (#254)
* use functional options pattern to inject logr (#124)

* use functional options pattern to inject logr

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* cleanup log formatting

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* allow clean shutdown (#126)

* fix owncloud posix query, log message and provisioning api results (#128)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* don't hardcode graphapi endpoint

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* add support for write handlers (#135)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* config: match shadowaccount objectlcass (#136)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Makefile compatiblility (#134)

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* update travis.yml (#154)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* check owncloud status code is ok (#153)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Bulid and push multiarch docker images (#142)

Fix #141.

With this change, a github action is added that builds a multiarch docker image on every commit, supporting x86_64, aarch64 and arm/v7.

When the action is triggered on a release or tag, it also uploads the versioned image to github container image registry.

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* don't mix graph and provisioning api (#157)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Reuse http client as much as possible and allow insecure transport (#160)

* Allow using configmaps when deploying in kubernetes (#161)

* Ensure config watcher also works in kubernetes

When using configmaps in kubernetes, the file is a symlink, and then
file-watcher is not opdated with a write event. Instead it receives a
CHMOD and a REMOVE event.

This change adds two things
1) Removal of the current wacther and adding of a new watcher for the
same path
2) The do-reload conditional is updated to also include the remove
event.

* Align write and removed conditionals

* Fix API server json formating (#163) (#164)

* Update README.md (#167)

Fixing bad port in quickstart

* Use port 3893 with ldapsearch in example (#150)

* feat: add flags for ldap listen addresses (#169)

Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>

* Assets build fix. (#171)

* Docker build fix.

* Makefile fix.

* update readme, config and deps (#170)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* Remove byanke's donation fields

* Database plugins (#133)

This commit actually covers a few items. In future commits, I will keep features distinct. This is only happening this time around due to how long it took to merge this branch.

Covered:

- Database plugins (at this time: SQLite, MySQL, Postgres)
- Backends acting as middleware: added the [[Backends]] configuration directive while retaining backward compatibility with [Backend]
- Schema introspection (root DSE query with base scope)
- When proxying, insert queried attribute back in upstream response, if absent, so that the LDAP library does not filter out all entries
- When chaining backends, any backend can be used to inject OTP value in password, before reaching a non-OTP-aware backend (currently guarded by a True statement in case we find an issue (I did thoroughly test but you never know))
- Handling of special "1.1" attributes filter meaning "I do not want attributes" (RFC 4511, 4.5.1.8)
- Support for "want types only" queries, even when proxying

* Mac M1 Support and LDAP Req Attributes (#192)

* Mac M1 Support

* Augmented root DSS and schema discovery based on content of schema directory

* LDAP workaround where req. attribute gets injected in response now works with combined filters

* SubSchema query can return a minimal set, freeipa or openldap's schemas

* Feature/upgrade ldap library version (#194)

* Stronger, salted paswords using bcrypt. (#195)

* Fixed badges in README file and added a couple improvements (#196)

* fix lock for ownCloud / graph backend (#198)

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* the config struct is only needed by the config backend (#199)

* Variable "unixid" is now respectively "UIDNumber" and "GIDNumber" for… (#201)

* fix: keep watch config file when changed,renamed,removed (#189)

I will need to run `glauth.go` through `gofmt` myself but that is fine.

* Returning when unable to start config watcher (#203)

* Introducing goconvey testing and refactoring of config and ... (#204)

* Introducing goconvey testing and refactoring of config and plugin backends.

* gofmt, oops

* Refactored re-insertion of requested attributes

* Allow bind operations with no group provided (#205)

* Support for userPrincipalName binding and browsing. (#206)

* Support for userPrincipalName binding and browsing.

* Fixed travis CI test for userprincipalname

* fixing travis tests for more users

* Rate limit after failed binds (#207)

* Capabilities -- part 2 (#214) + do not dump config at startup

* Attempting migration from Travis to Github Actions

* Migration Step#2

* Migration Step#3

* Migration Step#4

* Migration Step#5

* Migration Step#6

* Migration Step#7

* Migration Step#8

* Migration Step#9

* Migration Step#9

* Migration dev...

* Migration dev...

* Migration dev...

* Migration dev...

* Docker with plugins, first step (#215)

* Docker with plugins, first step

* Build better docker images, including plugins

* Merge back from dev to feature branch step #2

* Added capabilities to CI

* - Now creating two docker images, including one with plugins
- Moving from Travis CI to GitHub Actions

* Plugins now run on distroless (#217)

* Getting rid of Travis CI now that it doesn't support FOSS anymore.

* Internal Stats -- performance view (#221)

* Local merge

* Update CI to forget about bindata

* Forgot to cleanup the cleanup...

* assets: use stdlib "embed" package (#200)

This should make it easier to include GLAuth in other projects using `go get`

* 'Airgapping' web assets for security and preventing breakage (#227)

* v2 -- V2 hierarchy (#228)

* Search refactoring, tree traversal and scope correctness (#229)

Search refactoring, tree traversal and scope correctness.

* Feature/multi cfg (#233)

* Feature/custom attributes (#240)

* Makefiles: platform releases (#241)

* Feature/release script (#242)

* Bug/fix docker build in v2 (#244)

* Fix #246 and #252

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>
2022-02-27 16:35:57 -08:00
Chris F Ravenscroft 851c21df04 Merge branch 'master' into dev 2022-02-27 16:34:44 -08:00
Chris F Ravenscroft ceefc66dbb Fix #246 and #252 2022-01-30 13:31:40 -08:00
Chris F Ravenscroft e26219c2f4 Dev (#245) -- Docker fix for V2 hierarchy
Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>
2021-11-26 17:42:20 -08:00
Chris F Ravenscroft 2222a791e4 Merge branch 'master' into dev 2021-11-27 01:01:24 +00:00
Chris F Ravenscroft eef3a5c6a0 Bug/fix docker build in v2 (#244) 2021-11-26 16:37:47 -08:00
Chris F Ravenscroft e7195ed983 Dev to Master 2.1.0 RC1 (#243)
Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: fanlix <fanlix@gmail.com>
Co-authored-by: Hank Donnay <hdonnay@gmail.com>
2021-11-25 17:56:59 -08:00
Chris F Ravenscroft 1299a6d002 Deprecated glauth.go merge from master 2021-11-26 01:22:04 +00:00
Chris F Ravenscroft 95ac9708ed Feature/release script (#242) 2021-11-25 16:35:36 -08:00
Chris F Ravenscroft 363cd76fa2 Makefiles: platform releases (#241) 2021-11-24 23:55:59 -08:00
Chris F Ravenscroft 82bc471735 Feature/custom attributes (#240) 2021-11-24 18:22:42 -08:00
Chris F Ravenscroft e1e586f464 Feature/multi cfg (#233) 2021-11-06 15:43:25 -07:00
Chris F Ravenscroft 31fbd790e8 Search refactoring, tree traversal and scope correctness (#229)
Search refactoring, tree traversal and scope correctness.
2021-10-25 13:41:22 -07:00
Chris F Ravenscroft ef3151c287 v2 -- V2 hierarchy (#228) 2021-10-20 18:13:45 -07:00
Chris F Ravenscroft de3d1cec1c 'Airgapping' web assets for security and preventing breakage (#227) 2021-10-20 12:07:42 -07:00
Hank Donnay 7faac6df69 assets: use stdlib "embed" package (#200)
This should make it easier to include GLAuth in other projects using `go get`
2021-10-20 11:26:04 -07:00
Chris F Ravenscroft e86d6000d9 Forgot to cleanup the cleanup... 2021-10-20 11:09:42 -07:00
Chris F Ravenscroft c7098406d0 Update CI to forget about bindata 2021-10-20 10:54:48 -07:00
Chris F Ravenscroft 911e933020 Merge branch 'hdonnay-embed' into dev 2021-10-20 10:28:44 -07:00
Chris F Ravenscroft 53eddd67c9 Local merge 2021-10-20 10:28:36 -07:00
Chris F Ravenscroft c2360259af Internal Stats -- performance view (#221) 2021-10-04 23:31:13 -07:00
Chris F Ravenscroft 7e49d25591 Merge branch 'dev' of github.com:glauth/glauth into dev 2021-09-12 04:46:15 +00:00
Chris F Ravenscroft 7267d7b129 Getting rid of Travis CI now that it doesn't support FOSS anymore. 2021-09-12 04:45:44 +00:00
Chris F Ravenscroft 0e8937c61e Plugins now run on distroless (#217) 2021-09-11 21:34:55 -07:00
Chris F Ravenscroft fd85683ff0 Merge branch 'dev' of github.com:glauth/glauth into dev 2021-09-06 23:37:40 -07:00
Chris F Ravenscroft 2b45aefd74 - Now creating two docker images, including one with plugins
- Moving from Travis CI to GitHub Actions
2021-09-06 23:34:59 -07:00
Chris F Ravenscroft 05c537dabd Docker with plugins, first step (#215)
* Docker with plugins, first step

* Build better docker images, including plugins

* Merge back from dev to feature branch step #2

* Added capabilities to CI
2021-09-06 23:28:30 -07:00
Chris F Ravenscroft 8c8bc06c3b Migration dev... 2021-09-06 21:29:52 -07:00
Chris F Ravenscroft fed91e4aef Migration dev... 2021-09-06 21:24:29 -07:00
Chris F Ravenscroft 7be05404e1 Migration dev... 2021-09-06 21:07:03 -07:00
Chris F Ravenscroft af9062f1a1 Migration dev... 2021-09-06 21:05:16 -07:00
Chris F Ravenscroft aed137909a Migration Step#9 2021-09-06 21:00:45 -07:00
Chris F Ravenscroft 846f0509d6 Migration Step#9 2021-09-06 20:54:26 -07:00
Chris F Ravenscroft 06afbdcee5 Migration Step#8 2021-09-06 20:52:26 -07:00
Chris F Ravenscroft 2e0f7f7335 Migration Step#7 2021-09-06 20:50:15 -07:00
Chris F Ravenscroft b07cd1f256 Migration Step#6 2021-09-06 19:03:22 -07:00
Chris F Ravenscroft 2ccb8ce02e Migration Step#5 2021-09-06 18:57:27 -07:00
Chris F Ravenscroft 5dfbbb2f4b Migration Step#4 2021-09-06 18:31:30 -07:00
Chris F Ravenscroft 3a30656c18 Migration Step#3 2021-09-06 18:29:48 -07:00
Chris F Ravenscroft 3f53bb983a Migration Step#2 2021-09-06 18:24:21 -07:00
Chris F Ravenscroft 3b7a548389 Attempting migration from Travis to Github Actions 2021-09-06 18:22:58 -07:00
Chris F Ravenscroft 0ec6f15957 Capabilities -- part 2 (#214) + do not dump config at startup 2021-09-05 20:54:47 -07:00
Chris F Ravenscroft be26675ef8 Revert "Capabilities." from Master. (#211)
This reverts commit 8ef3e2b8d8.
2021-08-23 00:44:23 -07:00
Chris F Ravenscroft 8ef3e2b8d8 Capabilities. (#210) 2021-08-23 00:42:21 -07:00
Chris F Ravenscroft ccb839f0a5 Create and push docker image using podman (#209) 2021-08-22 13:46:49 -07:00
Jörn Friedrich Dreyer 1b92cfbae6 v2.0.0 RC1 (#125)
Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
Co-authored-by: Zhou Wenzong <wenzong@users.noreply.github.com>
Co-authored-by: Jairo Llopis <Yajo@users.noreply.github.com>
Co-authored-by: Benedikt Kulmann <benedikt@kulmann.biz>
Co-authored-by: Nicolai Willems <172633+nwillems@users.noreply.github.com>
Co-authored-by: Thibault Soubiran <soubi.thibault@gmail.com>
Co-authored-by: Clement JACOB <clems71@gmail.com>
Co-authored-by: Lutz Horn <code@lhorn.de>
Co-authored-by: Márk Sági-Kazár <sagikazarmark@users.noreply.github.com>
Co-authored-by: Peter Heise <pheise@koprolalie.com>
Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: Chris F Ravenscroft <chris@voilaweb.com>
Co-authored-by: fanlix <fanlix@gmail.com>
2021-08-12 21:41:58 -07:00
Chris F Ravenscroft 8223029345 Rate limit after failed binds (#207) 2021-08-12 12:59:14 -07:00
Chris F Ravenscroft 7c7934aea3 Support for userPrincipalName binding and browsing. (#206)
* Support for userPrincipalName binding and browsing.

* Fixed travis CI test for userprincipalname

* fixing travis tests for more users
2021-08-11 18:12:45 -07:00
Chris F Ravenscroft 9c9294485b Allow bind operations with no group provided (#205) 2021-08-11 15:45:30 -07:00
Chris F Ravenscroft 4cf38893ac Introducing goconvey testing and refactoring of config and ... (#204)
* Introducing goconvey testing and refactoring of config and plugin backends.

* gofmt, oops

* Refactored re-insertion of requested attributes
2021-08-10 20:48:14 -07:00
Chris F Ravenscroft 674a6bdbf4 Returning when unable to start config watcher (#203) 2021-08-08 17:31:06 -07:00
fanlix 7b082dccb8 fix: keep watch config file when changed,renamed,removed (#189)
I will need to run `glauth.go` through `gofmt` myself but that is fine.
2021-08-02 17:23:41 -07:00
Chris F Ravenscroft 10b0191818 Variable "unixid" is now respectively "UIDNumber" and "GIDNumber" for… (#201) 2021-08-02 17:20:42 -07:00
Jörn Friedrich Dreyer 53ddd3c4f4 the config struct is only needed by the config backend (#199) 2021-07-29 14:50:47 -07:00
Jörn Friedrich Dreyer 2065fc39f4 fix lock for ownCloud / graph backend (#198)
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2021-07-29 20:58:42 +02:00
Chris F Ravenscroft e2724eadc2 Fixed badges in README file and added a couple improvements (#196) 2021-07-25 22:09:45 -07:00
Chris F Ravenscroft 9c197cc35e Stronger, salted paswords using bcrypt. (#195) 2021-07-25 17:00:20 -07:00
Chris F Ravenscroft 25a1648706 Feature/upgrade ldap library version (#194) 2021-07-25 13:01:31 -07:00
Chris F Ravenscroft cbb84b8641 Mac M1 Support and LDAP Req Attributes (#192)
* Mac M1 Support

* Augmented root DSS and schema discovery based on content of schema directory

* LDAP workaround where req. attribute gets injected in response now works with combined filters

* SubSchema query can return a minimal set, freeipa or openldap's schemas
2021-07-25 00:36:18 -07:00
Chris F Ravenscroft 2eb2215ae4 Database plugins (#133)
This commit actually covers a few items. In future commits, I will keep features distinct. This is only happening this time around due to how long it took to merge this branch.

Covered:

- Database plugins (at this time: SQLite, MySQL, Postgres)
- Backends acting as middleware: added the [[Backends]] configuration directive while retaining backward compatibility with [Backend]
- Schema introspection (root DSE query with base scope)
- When proxying, insert queried attribute back in upstream response, if absent, so that the LDAP library does not filter out all entries
- When chaining backends, any backend can be used to inject OTP value in password, before reaching a non-OTP-aware backend (currently guarded by a True statement in case we find an issue (I did thoroughly test but you never know))
- Handling of special "1.1" attributes filter meaning "I do not want attributes" (RFC 4511, 4.5.1.8)
- Support for "want types only" queries, even when proxying
2021-07-17 11:45:36 -07:00
Jörn Friedrich Dreyer 225f1b441b Merge branch 'master' into dev 2021-02-27 21:19:34 +01:00
Ben Yanke 5681dc4d02 Remove byanke's donation fields 2021-02-27 07:51:46 -06:00
Ben Yanke bf5ddba2be Remove byanke's donation fields 2021-02-27 07:50:23 -06:00
Jörn Friedrich Dreyer fcf784ecf0 update readme, config and deps (#170)
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2021-02-25 21:42:50 +01:00
Peter Heise 07763f52e6 Assets build fix. (#171)
* Docker build fix.

* Makefile fix.
2021-02-24 19:29:07 +01:00
Márk Sági-Kazár 262465d63b feat: add flags for ldap listen addresses (#169)
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
2021-02-16 19:31:08 +01:00
Lutz Horn f28f1a115b Use port 3893 with ldapsearch in example (#150) 2021-02-11 21:37:47 +01:00
Clement JACOB f4ceb0c3d4 Update README.md (#167)
Fixing bad port in quickstart
2021-01-14 18:52:16 +01:00
Thibault Soubiran 9fb9c87625 Fix API server json formating (#163) (#164) 2021-01-11 13:15:06 +01:00
Nicolai Willems faed82ad44 Allow using configmaps when deploying in kubernetes (#161)
* Ensure config watcher also works in kubernetes

When using configmaps in kubernetes, the file is a symlink, and then
file-watcher is not opdated with a write event. Instead it receives a
CHMOD and a REMOVE event.

This change adds two things
1) Removal of the current wacther and adding of a new watcher for the
same path
2) The do-reload conditional is updated to also include the remove
event.

* Align write and removed conditionals
2020-12-08 22:27:37 +01:00
Benedikt Kulmann fd3ac7e4bb Reuse http client as much as possible and allow insecure transport (#160) 2020-11-10 13:46:27 +01:00
Jörn Friedrich Dreyer 4f029234b2 don't mix graph and provisioning api (#157)
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-10-30 14:08:13 +01:00
Jairo Llopis d6ba7d0cbc Bulid and push multiarch docker images (#142)
Fix #141.

With this change, a github action is added that builds a multiarch docker image on every commit, supporting x86_64, aarch64 and arm/v7.

When the action is triggered on a release or tag, it also uploads the versioned image to github container image registry.

Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-10-05 23:24:13 +02:00
Jörn Friedrich Dreyer 4d42af8aac check owncloud status code is ok (#153)
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-10-05 22:19:19 +02:00
Jörn Friedrich Dreyer fda48f5524 update travis.yml (#154)
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-10-05 21:57:20 +02:00
Zhou Wenzong b7cfc11f5c Makefile compatiblility (#134)
Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-05-15 13:53:10 +02:00
Jörn Friedrich Dreyer bbb381a68e config: match shadowaccount objectlcass (#136)
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-05-15 13:41:00 +02:00
Jörn Friedrich Dreyer 28136302b7 add support for write handlers (#135)
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-05-15 12:49:53 +02:00
Jörn Friedrich Dreyer 4e600736f2 don't hardcode graphapi endpoint
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-05-07 18:09:26 +02:00
Jörn Friedrich Dreyer 2d4f5d5476 fix owncloud posix query, log message and provisioning api results (#128)
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-02-28 17:01:18 +01:00
Jörn Friedrich Dreyer 29cf021694 allow clean shutdown (#126) 2020-02-27 10:26:48 +01:00
Jörn Friedrich Dreyer 592fdf4e79 use functional options pattern to inject logr (#124)
* use functional options pattern to inject logr

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* cleanup log formatting

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-02-26 15:01:31 +01:00
Jörn Friedrich Dreyer a92f9a7028 remove unsupported deploy option: body (#114)
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-02-24 16:03:07 +01:00
Jörn Friedrich Dreyer 182d7c0496 Use travis/dpv v1 body option for release message (#113)
dpl v2 is not yet used in production for travis, so I ran into https://travis-ci.org/glauth/glauth/jobs/654427357#L556

v1 directly uses octokits parameters: https://github.com/octokit/octokit.rb/blob/master/lib/octokit/client/releases.rb#L35
2020-02-24 15:40:51 +01:00
Jörn Friedrich Dreyer 6278bc34d4 fix release message escaping (#112)
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-02-24 15:12:27 +01:00
Jörn Friedrich Dreyer 78a98d75a9 use bot for travis (#111)
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-02-24 14:01:34 +01:00
Jörn Friedrich Dreyer 6928b3b608 Merge branch 'dev' 2020-02-24 12:16:02 +01:00
Jörn Friedrich Dreyer ac16a07a63 Merge branch 'master' into dev 2020-02-24 12:09:13 +01:00
Jörn Friedrich Dreyer 363336bf94 update travis deploy condition
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-02-24 11:47:48 +01:00
Jörn Friedrich Dreyer 07644bd957 Update dev (#109)
* Pulling in latest changes and fixes for  1.1.1 (#56)

* Adding a few test dockerfiles

* Continued work on docker builds

* Basic example of docker build and run works - see build.sh for now

* Continuing to work on docker build

* Removing testing from build script, since it belongs later in process

* Implementing dumb init

* Docker build fully switched to alpine

* Setting up alpine build

* Cleaning up dockerfile

* Cleaning up repo in prep for merge

* Updating permissions for clean merge

* Removing old ansible cruft, as per #12

* Removing binaries (since we now build)

* Updating sample config to use new fields now available

* Testing travis

* another travis test

* Still working on travis builds

* Fix missing amazon packages

* Merge in Travis config feature branch (#26)

* Adding version string

* Fixing and cleaning up docker build

* Adding image hashing and verification to 'make all'

* Testing build

* Running gofmt to fix build

* Testing switching travis to make

* Testing build again

* Merging in Fixes from travis-build feature branch (#33)

This includes an integration test which runs glauth and compares ldapsearch snapshot output stored in the repo compared to the snapshot output of the glauth.

Additionally, removing old versions of go, and adding windows and linuxar builds (but not tests) to the makefile, and consequently, the travis build.

While the mac, linux-arm, and windows binaries are not able to be run in travis, they are able to be at least compiled.

* Remove needless comment from integration test

* Adding 'is-process-running' check before integration tests run, to clearly note if the program crashes on run

* Switching from 1 sec to 2 sec timeout for integration test

* Adding back config file to fix build. Previous commit intended to see if build failed (and it did)

* Add support for including groups in groups (#23)

* Add support for including groups in groups
* Pulling in Makefile structure to allow easier testing and builds

* Forgot to remove spare test

* Add Version Info at Buildtime  (#39)

Adds the build info to the binary at buildtime via buildtime variables. This is shown by `./glauth --version`.

Example output for a non-release:
```
GLauth
Non-release build from branch feature/buildversion

Build time: 20180531_181011Z
Commit: 07ba631c2f
```

And example of a built release:
```
GLauth v2.3.4

Build time: 20180601_041330Z
Commit: 931d666262

```

* Fixing Broken Docker Build (#41)

Forgot to include go-bindata run in Dockerfile.

* Removing leftover TODO from merge

* Testing releases

* Testing releases

* Testing releases

* Adjusting logging

* Add new group tests to integration tests

* add cleanup to test process

* Add documentation about otherGroups, which was a previously undocumented field.

* Auto fetching bindata during build so it's not needed to be done manually

* Syntax fix

* starting framework for unit tests

* Merging in ongoing progress from Feature/travis build (#44)

Fixes a few minor issues:

#42 - now uses the makefile in the docker build, which means version info is correctly embedded at runtime - also now outputs version data at the top of the log when the container starts

Fixing a simple issue found by go vet. Need to do more work on fixing issues found in go vet.

* Forgot to run 'go fmt'

* Adding codecov for go now that a single test is written

* Testing not quite ready yet, removing from build

* Add Support For 2 Factor Authentication

Merging in feature from @ryskov (PR #24) adding 2FA support during LDAP binds. This is accomplished by concatenating the code to the end of the password. 

Also added integration tests for the TOTP method to run in CI. Could not, however, add automated tests for the yubikey, due the physical nature.

* Expose LDAPS ports in Docker container (#49)

Currently, the LDAPS ports are not exposed in the docker container.

* Adding better logging to docker start script

* Add more logging info to docker startup script

* Fix Arm32 Build (#52)

As discussed in issue #51, Arm32 builds were using 368 (intel/amd) arch accidentally, generating linux 32 bit binaries instead of arm 32 bit binaries. This commit fixes this in the Makefile, which will fix both the local builds, as well as the travis CI and release builds.

fixes #52

* Update docker hub image badge to a working one

previous one simply returned 0.

* Removing 3893 - fixes #50

* Fixing readme MD formatting

* Adding travis_retry to fix against intermittent network outages

* Add TLS options for running both with TLS and without on the same time (#27)

* Add TLS options for running both with TLS and without on the same time.

This commit expands on the settings available for using TLS. It puts TLS settings under the [frontend.tls] section and adds a new setting to [frontend] called TLSExclusive (bool).
TLSExclusive specifies whether or not to only run TLS when it is enabled, and is 'true' by default. Setting it to 'false' and having TLS enabled, causes the server to start both a LDAP and LDAPS server,
and therefore requires to seperate 'listen' options (to run on different ports) - the Frontend.Listen and the Frontend.TLS.Listen. If TLSExclusive is set to 'true' and no Frontend.TLS.Listen is specified, it will use the Frontend.Listen.

* Adding PR template and improving integration test tooling

* Updating formatting

* Add get dependencies step to makefile setup

* Add go 1.11

* Add App Password Support (#60)

App passwords can now be used to allow easier OTP use alongside applications which need to bind with a static password. Use the key `passappsha256` and specify an array of password hashes. See the readme and sample configuration file for more information.

Fixes #54

* Adding NCoC as official project code of conduct

We happily accept contributions based on the merit of the contributions.

* Properly handling paramaters in logs - fixes #64

* Adding ldapsearch for healthchecks

* fixed quoting in docker startup script (#77)

Thanks @cxcv cxcv

* Minor tweak of help text

* More durable Dockerfile (#92)

Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: Andrea Cervesato <andrea.cervesato@gmail.com>
2020-02-21 21:26:13 +01:00
Jörn Friedrich Dreyer 558c7de15a Refactor packages (#105)
* Pulling in latest changes and fixes for  1.1.1 (#56)

* Adding a few test dockerfiles

* Continued work on docker builds

* Basic example of docker build and run works - see build.sh for now

* Continuing to work on docker build

* Removing testing from build script, since it belongs later in process

* Implementing dumb init

* Docker build fully switched to alpine

* Setting up alpine build

* Cleaning up dockerfile

* Cleaning up repo in prep for merge

* Updating permissions for clean merge

* Removing old ansible cruft, as per #12

* Removing binaries (since we now build)

* Updating sample config to use new fields now available

* Testing travis

* another travis test

* Still working on travis builds

* Fix missing amazon packages

* Merge in Travis config feature branch (#26)

* Adding version string

* Fixing and cleaning up docker build

* Adding image hashing and verification to 'make all'

* Testing build

* Running gofmt to fix build

* Testing switching travis to make

* Testing build again

* Merging in Fixes from travis-build feature branch (#33)

This includes an integration test which runs glauth and compares ldapsearch snapshot output stored in the repo compared to the snapshot output of the glauth.

Additionally, removing old versions of go, and adding windows and linuxar builds (but not tests) to the makefile, and consequently, the travis build.

While the mac, linux-arm, and windows binaries are not able to be run in travis, they are able to be at least compiled.

* Remove needless comment from integration test

* Adding 'is-process-running' check before integration tests run, to clearly note if the program crashes on run

* Switching from 1 sec to 2 sec timeout for integration test

* Adding back config file to fix build. Previous commit intended to see if build failed (and it did)

* Add support for including groups in groups (#23)

* Add support for including groups in groups
* Pulling in Makefile structure to allow easier testing and builds

* Forgot to remove spare test

* Add Version Info at Buildtime  (#39)

Adds the build info to the binary at buildtime via buildtime variables. This is shown by `./glauth --version`.

Example output for a non-release:
```
GLauth
Non-release build from branch feature/buildversion

Build time: 20180531_181011Z
Commit: 07ba631c2f
```

And example of a built release:
```
GLauth v2.3.4

Build time: 20180601_041330Z
Commit: 931d666262

```

* Fixing Broken Docker Build (#41)

Forgot to include go-bindata run in Dockerfile.

* Removing leftover TODO from merge

* Testing releases

* Testing releases

* Testing releases

* Adjusting logging

* Add new group tests to integration tests

* add cleanup to test process

* Add documentation about otherGroups, which was a previously undocumented field.

* Auto fetching bindata during build so it's not needed to be done manually

* Syntax fix

* starting framework for unit tests

* Merging in ongoing progress from Feature/travis build (#44)

Fixes a few minor issues:

#42 - now uses the makefile in the docker build, which means version info is correctly embedded at runtime - also now outputs version data at the top of the log when the container starts

Fixing a simple issue found by go vet. Need to do more work on fixing issues found in go vet.

* Forgot to run 'go fmt'

* Adding codecov for go now that a single test is written

* Testing not quite ready yet, removing from build

* Add Support For 2 Factor Authentication

Merging in feature from @ryskov (PR #24) adding 2FA support during LDAP binds. This is accomplished by concatenating the code to the end of the password. 

Also added integration tests for the TOTP method to run in CI. Could not, however, add automated tests for the yubikey, due the physical nature.

* Expose LDAPS ports in Docker container (#49)

Currently, the LDAPS ports are not exposed in the docker container.

* Adding better logging to docker start script

* Add more logging info to docker startup script

* Fix Arm32 Build (#52)

As discussed in issue #51, Arm32 builds were using 368 (intel/amd) arch accidentally, generating linux 32 bit binaries instead of arm 32 bit binaries. This commit fixes this in the Makefile, which will fix both the local builds, as well as the travis CI and release builds.

fixes #52

* Update docker hub image badge to a working one

previous one simply returned 0.

* Removing 3893 - fixes #50

* Fixing readme MD formatting

* Adding travis_retry to fix against intermittent network outages

* Add TLS options for running both with TLS and without on the same time (#27)

* Add TLS options for running both with TLS and without on the same time.

This commit expands on the settings available for using TLS. It puts TLS settings under the [frontend.tls] section and adds a new setting to [frontend] called TLSExclusive (bool).
TLSExclusive specifies whether or not to only run TLS when it is enabled, and is 'true' by default. Setting it to 'false' and having TLS enabled, causes the server to start both a LDAP and LDAPS server,
and therefore requires to seperate 'listen' options (to run on different ports) - the Frontend.Listen and the Frontend.TLS.Listen. If TLSExclusive is set to 'true' and no Frontend.TLS.Listen is specified, it will use the Frontend.Listen.

* Adding PR template and improving integration test tooling

* Updating formatting

* Add get dependencies step to makefile setup

* Add go 1.11

* Add App Password Support (#60)

App passwords can now be used to allow easier OTP use alongside applications which need to bind with a static password. Use the key `passappsha256` and specify an array of password hashes. See the readme and sample configuration file for more information.

Fixes #54

* Adding NCoC as official project code of conduct

We happily accept contributions based on the merit of the contributions.

* Properly handling paramaters in logs - fixes #64

* Adding ldapsearch for healthchecks

* fixed quoting in docker startup script (#77)

Thanks @cxcv cxcv

* Minor tweak of help text

* More durable Dockerfile (#92)

* refactor with latest dev

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

* fix modules, update .gitignore

* pass in logger

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>

Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: Andrea Cervesato <andrea.cervesato@gmail.com>
2020-02-21 17:10:41 +01:00
Thomas Müller 9b90466286 Feature/owncloud10 backend graphapi (#104)
* Add ownCloud 10 backend supporting provisioning api as well as graph api

* Use github.com/DeepDiver1975/msgraph.go/v1.0

* List members in groups when using graph api

* Handle searchBaseDN pointing to a user
2020-02-21 11:26:33 +01:00
Dan Anderson 3445f388b0 add "trimpath" to build arguments to remove build path from released binaries (#67)
Co-authored-by: dankn0x <60980300+dankn0x@users.noreply.github.com>
Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-02-21 09:34:37 +01:00
Thomas Müller 14139e878d travis-ci: run with go 1.11, 1.12 and 1.13 (#102)
Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2020-02-21 09:22:38 +01:00
Jörn Friedrich Dreyer b6c6730cfd Update dev (#101)
* Pulling in latest changes and fixes for  1.1.1 (#56)

* Adding a few test dockerfiles

* Continued work on docker builds

* Basic example of docker build and run works - see build.sh for now

* Continuing to work on docker build

* Removing testing from build script, since it belongs later in process

* Implementing dumb init

* Docker build fully switched to alpine

* Setting up alpine build

* Cleaning up dockerfile

* Cleaning up repo in prep for merge

* Updating permissions for clean merge

* Removing old ansible cruft, as per #12

* Removing binaries (since we now build)

* Updating sample config to use new fields now available

* Testing travis

* another travis test

* Still working on travis builds

* Fix missing amazon packages

* Merge in Travis config feature branch (#26)

* Adding version string

* Fixing and cleaning up docker build

* Adding image hashing and verification to 'make all'

* Testing build

* Running gofmt to fix build

* Testing switching travis to make

* Testing build again

* Merging in Fixes from travis-build feature branch (#33)

This includes an integration test which runs glauth and compares ldapsearch snapshot output stored in the repo compared to the snapshot output of the glauth.

Additionally, removing old versions of go, and adding windows and linuxar builds (but not tests) to the makefile, and consequently, the travis build.

While the mac, linux-arm, and windows binaries are not able to be run in travis, they are able to be at least compiled.

* Remove needless comment from integration test

* Adding 'is-process-running' check before integration tests run, to clearly note if the program crashes on run

* Switching from 1 sec to 2 sec timeout for integration test

* Adding back config file to fix build. Previous commit intended to see if build failed (and it did)

* Add support for including groups in groups (#23)

* Add support for including groups in groups
* Pulling in Makefile structure to allow easier testing and builds

* Forgot to remove spare test

* Add Version Info at Buildtime  (#39)

Adds the build info to the binary at buildtime via buildtime variables. This is shown by `./glauth --version`.

Example output for a non-release:
```
GLauth
Non-release build from branch feature/buildversion

Build time: 20180531_181011Z
Commit: 07ba631c2f
```

And example of a built release:
```
GLauth v2.3.4

Build time: 20180601_041330Z
Commit: 931d666262

```

* Fixing Broken Docker Build (#41)

Forgot to include go-bindata run in Dockerfile.

* Removing leftover TODO from merge

* Testing releases

* Testing releases

* Testing releases

* Adjusting logging

* Add new group tests to integration tests

* add cleanup to test process

* Add documentation about otherGroups, which was a previously undocumented field.

* Auto fetching bindata during build so it's not needed to be done manually

* Syntax fix

* starting framework for unit tests

* Merging in ongoing progress from Feature/travis build (#44)

Fixes a few minor issues:

#42 - now uses the makefile in the docker build, which means version info is correctly embedded at runtime - also now outputs version data at the top of the log when the container starts

Fixing a simple issue found by go vet. Need to do more work on fixing issues found in go vet.

* Forgot to run 'go fmt'

* Adding codecov for go now that a single test is written

* Testing not quite ready yet, removing from build

* Add Support For 2 Factor Authentication

Merging in feature from @ryskov (PR #24) adding 2FA support during LDAP binds. This is accomplished by concatenating the code to the end of the password. 

Also added integration tests for the TOTP method to run in CI. Could not, however, add automated tests for the yubikey, due the physical nature.

* Expose LDAPS ports in Docker container (#49)

Currently, the LDAPS ports are not exposed in the docker container.

* Adding better logging to docker start script

* Add more logging info to docker startup script

* Fix Arm32 Build (#52)

As discussed in issue #51, Arm32 builds were using 368 (intel/amd) arch accidentally, generating linux 32 bit binaries instead of arm 32 bit binaries. This commit fixes this in the Makefile, which will fix both the local builds, as well as the travis CI and release builds.

fixes #52

* Update docker hub image badge to a working one

previous one simply returned 0.

* Removing 3893 - fixes #50

* Fixing readme MD formatting

* Adding travis_retry to fix against intermittent network outages

* Add TLS options for running both with TLS and without on the same time (#27)

* Add TLS options for running both with TLS and without on the same time.

This commit expands on the settings available for using TLS. It puts TLS settings under the [frontend.tls] section and adds a new setting to [frontend] called TLSExclusive (bool).
TLSExclusive specifies whether or not to only run TLS when it is enabled, and is 'true' by default. Setting it to 'false' and having TLS enabled, causes the server to start both a LDAP and LDAPS server,
and therefore requires to seperate 'listen' options (to run on different ports) - the Frontend.Listen and the Frontend.TLS.Listen. If TLSExclusive is set to 'true' and no Frontend.TLS.Listen is specified, it will use the Frontend.Listen.

* Adding PR template and improving integration test tooling

* Updating formatting

* Add get dependencies step to makefile setup

* Add go 1.11

* Add App Password Support (#60)

App passwords can now be used to allow easier OTP use alongside applications which need to bind with a static password. Use the key `passappsha256` and specify an array of password hashes. See the readme and sample configuration file for more information.

Fixes #54

* Adding NCoC as official project code of conduct

We happily accept contributions based on the merit of the contributions.

* Properly handling paramaters in logs - fixes #64

* Adding ldapsearch for healthchecks

* fixed quoting in docker startup script (#77)

Thanks @cxcv cxcv

* Minor tweak of help text

* More durable Dockerfile (#92)

Co-authored-by: Ben Yanke <ben@benyanke.com>
Co-authored-by: Andrea Cervesato <andrea.cervesato@gmail.com>
2020-02-21 09:02:08 +01:00
Icarus Sparry b93d612e36 Fix mutex (#88)
* goimports fixups

Adjust imports lists to match that produced by goimports.

Signed-off-by: Icarus Sparry <icarus.sparry@amd.com>

* Fix mutex handling

Mutexes must not be copied after their first use.

Create a global mutex for ldap and pass a pointer to this around.
Fixes #43

Signed-off-by: Icarus Sparry <icarus.sparry@amd.com>
2020-02-21 08:28:35 +01:00
Andrea Cervesato 63879925c5 More durable Dockerfile (#92) 2019-08-29 21:48:49 -05:00
Travis Groth 0ff1daac96 Support shadowAccount ObjectClass (#84) 2019-07-01 14:46:08 -04:00
Andy Lo-A-Foe bc4ab91888 Fix unkeyed fields. Refs #43 (#80) 2019-07-01 14:24:23 -04:00
Andy Lo-A-Foe b8fd08b92f Remove duplicate. Fixes #71 (#79) 2019-05-20 15:46:31 +02:00
Ben Yanke c9b40b87d4 Minor tweak of help text 2019-04-24 21:13:07 -05:00
Jeppe Ryskov Larsen c69168c3d1 Allow for configuring DN format (#57)
* Add config options for customizing group and nate attribute prefixes and name of ssh-key output

* Rename nameattr and groupattr to respectively nameformat and groupformat

* Messed up setting up default values on merge for ldap and ldaps section

* Fix good-results after 'via LDAP' suffix was removed from gecos and description

* Added uid for good-results aswell

* Revert "Added uid for good-results aswell"

This reverts commit 21b86d2570.

* Adjust tests

* order of cn and uid should be same for both posixaccount and posixgroup to pass tests
2019-03-22 10:45:02 +01:00
Benjamin Schweizer 98ea83c9e0 fixed quoting in docker startup script (#77)
Thanks @cxcv cxcv
2019-03-12 12:25:53 -05:00
Jeppe Ryskov Larsen 2243d3d049 Add config option 'watchconfig' to enable hot-reloading of config on change (#58) 2019-02-15 11:34:50 +01:00
Ben Yanke f793f3a08c Shell script typo fix - thanks @sleleko 2018-12-28 08:16:26 -06:00
Sergey Leleko ba364f12c9 Update host (#70)
For correct work port forwarding in docker
2018-12-28 08:14:44 -06:00
Ben Yanke 28802ff87d Pulling in latest changes and fixes for 1.1.1 (#56)
* Adding a few test dockerfiles

* Continued work on docker builds

* Basic example of docker build and run works - see build.sh for now

* Continuing to work on docker build

* Removing testing from build script, since it belongs later in process

* Implementing dumb init

* Docker build fully switched to alpine

* Setting up alpine build

* Cleaning up dockerfile

* Cleaning up repo in prep for merge

* Updating permissions for clean merge

* Removing old ansible cruft, as per #12

* Removing binaries (since we now build)

* Updating sample config to use new fields now available

* Testing travis

* another travis test

* Still working on travis builds

* Fix missing amazon packages

* Merge in Travis config feature branch (#26)

* Adding version string

* Fixing and cleaning up docker build

* Adding image hashing and verification to 'make all'

* Testing build

* Running gofmt to fix build

* Testing switching travis to make

* Testing build again

* Merging in Fixes from travis-build feature branch (#33)

This includes an integration test which runs glauth and compares ldapsearch snapshot output stored in the repo compared to the snapshot output of the glauth.

Additionally, removing old versions of go, and adding windows and linuxar builds (but not tests) to the makefile, and consequently, the travis build.

While the mac, linux-arm, and windows binaries are not able to be run in travis, they are able to be at least compiled.

* Remove needless comment from integration test

* Adding 'is-process-running' check before integration tests run, to clearly note if the program crashes on run

* Switching from 1 sec to 2 sec timeout for integration test

* Adding back config file to fix build. Previous commit intended to see if build failed (and it did)

* Add support for including groups in groups (#23)

* Add support for including groups in groups
* Pulling in Makefile structure to allow easier testing and builds

* Forgot to remove spare test

* Add Version Info at Buildtime  (#39)

Adds the build info to the binary at buildtime via buildtime variables. This is shown by `./glauth --version`.

Example output for a non-release:
```
GLauth
Non-release build from branch feature/buildversion

Build time: 20180531_181011Z
Commit: 07ba631c2f
```

And example of a built release:
```
GLauth v2.3.4

Build time: 20180601_041330Z
Commit: 931d666262

```

* Fixing Broken Docker Build (#41)

Forgot to include go-bindata run in Dockerfile.

* Removing leftover TODO from merge

* Testing releases

* Testing releases

* Testing releases

* Adjusting logging

* Add new group tests to integration tests

* add cleanup to test process

* Add documentation about otherGroups, which was a previously undocumented field.

* Auto fetching bindata during build so it's not needed to be done manually

* Syntax fix

* starting framework for unit tests

* Merging in ongoing progress from Feature/travis build (#44)

Fixes a few minor issues:

#42 - now uses the makefile in the docker build, which means version info is correctly embedded at runtime - also now outputs version data at the top of the log when the container starts

Fixing a simple issue found by go vet. Need to do more work on fixing issues found in go vet.

* Forgot to run 'go fmt'

* Adding codecov for go now that a single test is written

* Testing not quite ready yet, removing from build

* Add Support For 2 Factor Authentication

Merging in feature from @ryskov (PR #24) adding 2FA support during LDAP binds. This is accomplished by concatenating the code to the end of the password. 

Also added integration tests for the TOTP method to run in CI. Could not, however, add automated tests for the yubikey, due the physical nature.

* Expose LDAPS ports in Docker container (#49)

Currently, the LDAPS ports are not exposed in the docker container.

* Adding better logging to docker start script

* Add more logging info to docker startup script

* Fix Arm32 Build (#52)

As discussed in issue #51, Arm32 builds were using 368 (intel/amd) arch accidentally, generating linux 32 bit binaries instead of arm 32 bit binaries. This commit fixes this in the Makefile, which will fix both the local builds, as well as the travis CI and release builds.

fixes #52

* Update docker hub image badge to a working one

previous one simply returned 0.

* Removing 3893 - fixes #50

* Fixing readme MD formatting

* Adding travis_retry to fix against intermittent network outages

* Add TLS options for running both with TLS and without on the same time (#27)

* Add TLS options for running both with TLS and without on the same time.

This commit expands on the settings available for using TLS. It puts TLS settings under the [frontend.tls] section and adds a new setting to [frontend] called TLSExclusive (bool).
TLSExclusive specifies whether or not to only run TLS when it is enabled, and is 'true' by default. Setting it to 'false' and having TLS enabled, causes the server to start both a LDAP and LDAPS server,
and therefore requires to seperate 'listen' options (to run on different ports) - the Frontend.Listen and the Frontend.TLS.Listen. If TLSExclusive is set to 'true' and no Frontend.TLS.Listen is specified, it will use the Frontend.Listen.

* Adding PR template and improving integration test tooling

* Updating formatting

* Add get dependencies step to makefile setup

* Add go 1.11

* Add App Password Support (#60)

App passwords can now be used to allow easier OTP use alongside applications which need to bind with a static password. Use the key `passappsha256` and specify an array of password hashes. See the readme and sample configuration file for more information.

Fixes #54

* Adding NCoC as official project code of conduct

We happily accept contributions based on the merit of the contributions.

* Properly handling paramaters in logs - fixes #64

* Adding ldapsearch for healthchecks
2018-12-25 11:25:42 -06:00
Ben Yanke 75fa168e9a Merge branch 'master' into dev 2018-12-25 11:24:55 -06:00
Ben Yanke 7a573aad93 Adding ldapsearch for healthchecks 2018-12-25 11:18:53 -06:00
Ben Yanke 9f349d10c8 Properly handling paramaters in logs - fixes #64 2018-11-15 06:56:00 -06:00
kost a02544ac8b Fix Legacy URL (#63) 2018-10-22 11:51:41 -05:00
kost 3acd492e61 uidnumber typo fix (#62) 2018-10-19 22:03:46 -05:00
Ben Yanke bd9a5e47ee Adding NCoC as official project code of conduct
We happily accept contributions based on the merit of the contributions.
2018-09-20 12:38:20 -05:00
Ben Yanke 2e70a47dd6 Adding NCoC as official project code of conduct
We happily accept contributions based on the merit of the contributions.
2018-09-20 12:38:03 -05:00
Ben Yanke e546a1a289 Add App Password Support (#60)
App passwords can now be used to allow easier OTP use alongside applications which need to bind with a static password. Use the key `passappsha256` and specify an array of password hashes. See the readme and sample configuration file for more information.

Fixes #54
2018-09-09 22:41:29 -05:00
Ben Yanke c550eb60e9 Add go 1.11 2018-09-09 20:18:32 -05:00
Ben Yanke 2b4aa3b78d Update README.md 2018-08-30 11:51:52 -05:00
Ben Yanke 6fbd8105cd Add get dependencies step to makefile setup 2018-08-23 00:24:10 -05:00
Ben Yanke a03a26960d Updating formatting 2018-08-22 09:09:53 -05:00
Ben Yanke 45a2671f46 Changing formatting 2018-08-22 09:09:17 -05:00
Ben Yanke 5e1ed70865 Add github PR template 2018-08-22 09:07:47 -05:00
Ben Yanke 3a13387be2 Adding PR template and improving integration test tooling 2018-08-22 08:55:54 -05:00
Ben Yanke e606ecbb56 Merge branch 'master' into dev 2018-08-21 22:58:21 -05:00
Jeppe Ryskov Larsen d9183b8f74 Add TLS options for running both with TLS and without on the same time (#27)
* Add TLS options for running both with TLS and without on the same time.

This commit expands on the settings available for using TLS. It puts TLS settings under the [frontend.tls] section and adds a new setting to [frontend] called TLSExclusive (bool).
TLSExclusive specifies whether or not to only run TLS when it is enabled, and is 'true' by default. Setting it to 'false' and having TLS enabled, causes the server to start both a LDAP and LDAPS server,
and therefore requires to seperate 'listen' options (to run on different ports) - the Frontend.Listen and the Frontend.TLS.Listen. If TLSExclusive is set to 'true' and no Frontend.TLS.Listen is specified, it will use the Frontend.Listen.
2018-08-21 22:18:42 -05:00
Ben Yanke 72c8e84a0d Adding travis_retry to fix against intermittent network outages 2018-08-09 23:18:41 -05:00
Ben Yanke c162ed8366 Fixing readme MD formatting 2018-08-04 22:56:30 -05:00
Ben Yanke 986a9187ee Removing 3893 - fixes #50 2018-08-04 22:14:05 -05:00
Ben Yanke a835e569cd Update docker hub image badge to a working one
previous one simply returned 0.
2018-07-31 16:03:21 -05:00
Ben Yanke d410b01955 Switching docker hub size badge to a working one 2018-07-31 16:02:52 -05:00
Felix Bartels ff6cb9a9d5 Fix Arm32 Build (#52)
As discussed in issue #51, Arm32 builds were using 368 (intel/amd) arch accidentally, generating linux 32 bit binaries instead of arm 32 bit binaries. This commit fixes this in the Makefile, which will fix both the local builds, as well as the travis CI and release builds.

fixes #52
2018-07-22 12:16:20 -05:00
Ben Yanke 6516741b3c Add more logging info to docker startup script 2018-07-21 20:17:40 -05:00
Ben Yanke 5c1a39bd47 Adding better logging to docker start script 2018-07-21 16:18:13 -05:00
Andreas Krüger a21d93bbd7 Expose LDAPS ports in Docker container (#49)
Currently, the LDAPS ports are not exposed in the docker container.
2018-07-21 07:31:07 -05:00
Ben Yanke 2020579614 Merging branches 2018-07-20 23:24:59 -05:00
Jeppe Ryskov Larsen 47401aac5f Add Support For 2 Factor Authentication
Merging in feature from @ryskov (PR #24) adding 2FA support during LDAP binds. This is accomplished by concatenating the code to the end of the password. 

Also added integration tests for the TOTP method to run in CI. Could not, however, add automated tests for the yubikey, due the physical nature.
2018-07-20 23:08:12 -05:00
Ben Yanke 6cd27aa790 Testing not quite ready yet, removing from build 2018-06-15 23:43:40 -05:00
Ben Yanke 9495943701 Adding codecov for go now that a single test is written 2018-06-15 23:38:42 -05:00
Ben Yanke 8de56c87a9 Forgot to run 'go fmt' 2018-06-15 23:35:18 -05:00
Ben Yanke 208fb1e33d Merge branch 'dev' of github.com:glauth/glauth into dev 2018-06-15 23:29:49 -05:00
Ben Yanke 32a13ec85a Merging in ongoing progress from Feature/travis build (#44)
Fixes a few minor issues:

#42 - now uses the makefile in the docker build, which means version info is correctly embedded at runtime - also now outputs version data at the top of the log when the container starts

Fixing a simple issue found by go vet. Need to do more work on fixing issues found in go vet.
2018-06-15 23:29:45 -05:00
Ben Yanke a75743ef7d starting framework for unit tests 2018-06-15 23:29:15 -05:00
Ben Yanke 93a6a5542f Syntax fix 2018-06-07 11:18:30 -05:00
Ben Yanke c431a6e426 Auto fetching bindata during build so it's not needed to be done manually 2018-06-07 11:14:27 -05:00
Ben Yanke a9837f3112 Add documentation about otherGroups, which was a previously undocumented field. 2018-06-06 12:49:56 -05:00
Ben Yanke 2d7a2ab8ce add cleanup to test process 2018-06-05 21:10:30 -05:00
Ben Yanke bc94f47c9e Add new group tests to integration tests 2018-06-05 20:21:42 -05:00
Ben Yanke 39b50ec92a Adjusting logging 2018-06-05 12:22:49 -05:00
Ben Yanke 50a78e3c56 Testing releases 2018-06-01 22:02:47 -05:00
Ben Yanke 5e2114e04b Testing releases 2018-06-01 21:55:56 -05:00
Ben Yanke 706c144f82 Testing releases 2018-06-01 21:47:01 -05:00
Ben Yanke 5519634a3e Removing leftover TODO from merge 2018-06-01 12:49:49 -05:00
Ben Yanke b32db95f4f Fixing Broken Docker Build (#41)
Forgot to include go-bindata run in Dockerfile.
2018-05-31 23:32:24 -05:00
Ben Yanke 0b1c979247 Add Version Info at Buildtime (#39)
Adds the build info to the binary at buildtime via buildtime variables. This is shown by `./glauth --version`.

Example output for a non-release:
```
GLauth
Non-release build from branch feature/buildversion

Build time: 20180531_181011Z
Commit: 07ba631c2f
```

And example of a built release:
```
GLauth v2.3.4

Build time: 20180601_041330Z
Commit: 931d666262

```
2018-05-31 23:18:12 -05:00
Ben Yanke b04ff2dd67 Forgot to remove spare test 2018-05-24 12:52:57 -05:00
Jeppe Ryskov Larsen 94574e50a7 Add support for including groups in groups (#23)
* Add support for including groups in groups
* Pulling in Makefile structure to allow easier testing and builds
2018-05-24 12:50:47 -05:00
Ben Yanke 1155d214ed Adding back config file to fix build. Previous commit intended to see if build failed (and it did) 2018-05-14 12:49:48 -05:00
Ben Yanke 9ae4613a7f Switching from 1 sec to 2 sec timeout for integration test 2018-05-14 12:44:14 -05:00
Ben Yanke 8d808c80a0 Adding 'is-process-running' check before integration tests run, to clearly note if the program crashes on run 2018-05-13 22:05:05 -05:00
Ben Yanke 5728484e61 Remove needless comment from integration test 2018-05-12 21:01:48 -05:00
Ben Yanke 6df835dd68 Merging in Fixes from travis-build feature branch (#33)
This includes an integration test which runs glauth and compares ldapsearch snapshot output stored in the repo compared to the snapshot output of the glauth.

Additionally, removing old versions of go, and adding windows and linuxar builds (but not tests) to the makefile, and consequently, the travis build.

While the mac, linux-arm, and windows binaries are not able to be run in travis, they are able to be at least compiled.
2018-05-12 20:55:32 -05:00
Ben Yanke 80883e4a57 Testing build again 2018-05-12 19:37:46 -05:00
Ben Yanke 02970c82ff Testing switching travis to make 2018-05-12 19:32:59 -05:00
Ben Yanke 7124c108a7 Running gofmt to fix build 2018-05-12 19:29:37 -05:00
Ben Yanke 136d7d7833 Testing build 2018-05-12 19:25:54 -05:00
Ben Yanke f0cf6488e4 Adding image hashing and verification to 'make all' 2018-05-11 22:58:18 -05:00
Ben Yanke dc90cd6819 Fixing and cleaning up docker build 2018-05-10 22:28:03 -05:00
Ben Yanke c2aca63246 Adding version string 2018-05-10 22:17:26 -05:00
Ben Yanke 60ac4368c6 Merge in Travis config feature branch (#26) 2018-05-03 23:45:33 -05:00
Ben Yanke 64fbf20dcd Merge pull request #22 from pasientskyhosting/amazon_fix_dev
Fix missing amazon packages
2018-05-03 22:48:48 -05:00
Jeppe Larsen de571e8cb6 Fix missing amazon packages 2018-05-03 16:02:46 +02:00
Ben Yanke c232ee0476 Adding travis builds to dev
Adding travis builds
2018-04-28 23:18:55 -05:00
Ben Yanke 4f82bf2baf Still working on travis builds 2018-04-28 23:12:59 -05:00
Ben Yanke 7608bc0133 another travis test 2018-04-28 16:22:50 -05:00
Ben Yanke 2ae84a9034 Testing travis 2018-04-28 16:05:30 -05:00
Ben Yanke ac85255d86 Updating sample config to use new fields now available 2018-04-25 13:05:27 -05:00
Ben Yanke 2a16b13a01 Removing binaries (since we now build) 2018-04-25 13:05:07 -05:00
Ben Yanke 5b84deb1c1 Removing old ansible cruft, as per #12 2018-04-25 12:30:55 -05:00
Ben Yanke c5cc0f5572 Merge pull request #19 from nmcclain/feature/dockerfile
Merging in Docker Build Tools
2018-04-22 22:59:22 -05:00
Ben Yanke dd71d9ac4e Updating permissions for clean merge 2018-04-22 22:57:39 -05:00
Ben Yanke 5f5c12e78d Cleaning up repo in prep for merge 2018-04-22 22:56:57 -05:00
Ben Yanke d5a1b6f216 Cleaning up dockerfile 2018-04-22 22:51:51 -05:00
Ben Yanke 82bff3fc36 Setting up alpine build 2018-04-22 22:49:14 -05:00
Ben Yanke fd9aaa243d Docker build fully switched to alpine 2018-04-22 22:41:08 -05:00
Ben Yanke e2c9210beb Implementing dumb init 2018-04-22 11:11:24 -05:00
Ben Yanke e03e9216fa Removing testing from build script, since it belongs later in process 2018-04-22 10:34:32 -05:00
Ben Yanke 70ff3600ec Continuing to work on docker build 2018-04-22 10:33:31 -05:00
Ben Yanke 68edcdc228 Basic example of docker build and run works - see build.sh for now 2018-04-21 22:33:36 -05:00
Ben Yanke f076f868dc Continued work on docker builds 2018-04-21 22:33:36 -05:00
Ben Yanke f039bbb964 Adding a few test dockerfiles 2018-04-18 21:41:19 -05:00
128 changed files with 8691 additions and 1884 deletions
+54
View File
@@ -0,0 +1,54 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/go
{
"name": "glauth",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/go:0-1.19-bullseye",
// Features to add to the dev container. More info: https://containers.dev/features.
"features": {
"docker-in-docker": {
"version": "latest",
"moby": true
},
"ghcr.io/rocker-org/devcontainer-features/apt-packages:1": {
"packages": "oathtool,ldap-utils"
}
},
// Configure tool-specific properties.
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
"settings": {},
"extensions": [
"golang.go",
"ms-azuretools.vscode-docker"
]
}
},
"containerEnv": {
"REPO": "glauth"
},
"capAdd": ["SYS_PTRACE"],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [9000],
// Use 'portsAttributes' to set default properties for specific forwarded ports.
// More info: https://containers.dev/implementors/json_reference/#port-attributes
"portsAttributes": {
"3893": {
"label": "LDAP",
"onAutoForward": "notify"
},
"5555": {
"label": "Web UI",
"onAutoForward": "openBrowser"
}
},
"postCreateCommand": "cd v2 && make setup"
}
+24
View File
@@ -0,0 +1,24 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
# \todo Eliminate duplication when/if Dependabot supports YAML anchors
version: 2
updates:
- package-ecosystem: github-actions
directory: /
labels:
- dependencies
- actions
schedule:
day: sunday
interval: weekly
- package-ecosystem: gomod
directory: /v2
labels:
- dependencies
- go
schedule:
interval: weekly
day: friday
+25
View File
@@ -0,0 +1,25 @@
Thank you for making a pull request!
A few things to be aware of as you're working on your PR:
## WIP Tag
Incomplete PRs are more than welcome - it can be useful to collaborate before
implementation of an idea is complete. However, if your PR is not ready
for merge, please add [WIP] to the end of the title (work-in-progress).
## Tests
Before committing, you are encouraged to run the small but growing test
suite. This is accomplished by `make test`. Additionally, if you are adding
new functionality, consider adding tests covering your feature.
## CI
Each push to a branch connected to a PR will be run through GLAuth's
CI system. Please use these to your advantage. In particular, the Github Actions
integration tests rely on the LDAP queries returning with a set result,
so if your changes will change the output, CI will likely fail.
To update, run `make fast && make updatetest && make test`. This will
delete the output snapshots provided and make new ones. You can then
inspect the changes and commit them.
Similarly, check codeclimate and try to fix what you find there if it fails.
+39
View File
@@ -0,0 +1,39 @@
name: ci
on:
pull_request:
push:
branches:
- dev
jobs:
build:
runs-on: ubuntu-20.04
strategy:
matrix:
go-version: [1.19.x, 1.20.x]
steps:
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}
- name: Updating packages database
run: sudo apt-get -qq update
- name: Install CLI Dependencies
run: sudo apt-get install -y ldap-utils oathtool libpam0g-dev
- name: Checkout code
uses: actions/checkout@v4
- name: Fetch Go dependencies
working-directory: ./v2
run: go get -t -v ./...
- name: Fail if a .go file hasn't been formatted with 'go fmt'
working-directory: ./v2
run: diff -u <(echo -n) <(gofmt -d ./)
- name: Full compile test
working-directory: ./v2
run: make fast
- name: Output hashes to the build log
working-directory: ./v2
run: cat bin/*/glauth.sha256
- name: Integration test with compiled binary
working-directory: ./v2
run: make test
+41
View File
@@ -0,0 +1,41 @@
name: "CodeQL"
on:
push:
branches: [ "master", "dev" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: "46 1 * * 3"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ javascript ]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"
@@ -0,0 +1,22 @@
# This workflow was added by CodeSee. Learn more at https://codesee.io/
# This is v2.0 of this workflow file
on:
push:
branches:
- master
pull_request_target:
types: [opened, synchronize, reopened]
name: CodeSee
permissions: read-all
jobs:
codesee:
runs-on: ubuntu-latest
continue-on-error: true
name: Analyze the repo with CodeSee
steps:
- uses: Codesee-io/codesee-action@v2
with:
codesee-token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }}
+27
View File
@@ -0,0 +1,27 @@
on:
pull_request:
push:
branches:
- dev
permissions:
contents: write
pull-requests: write
name: release-please
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: google-github-actions/release-please-action@v3
with:
release-type: go
package-name: GLAuth
path: v2
default-branch: master
pull-request-title-pattern: "ci: release ${version}"
token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
extra-files: |
v2/internal/version/const.go
+15 -13
View File
@@ -1,15 +1,17 @@
# Ignore default config file for testing
glauth.cfg
# Ignore the built binary during testing
glauth
# Built binaries
bin
bindata.go
.*.swp
v2/certs/
v2/bin
v2/docker/assets/linux
v2/gl.db
.vscode/
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
### Go Patch ###
/vendor/
/Godeps/
# gomocks auto-generated
**/mock_*.go
# Ignoring travis CLI tool logs
*travis.log
+12
View File
@@ -0,0 +1,12 @@
[submodule "v2/pkg/plugins/glauth-sqlite"]
path = v2/pkg/plugins/glauth-sqlite
url = https://github.com/glauth/glauth-sqlite
[submodule "v2/pkg/plugins/glauth-mysql"]
path = v2/pkg/plugins/glauth-mysql
url = https://github.com/glauth/glauth-mysql
[submodule "v2/pkg/plugins/glauth-postgres"]
path = v2/pkg/plugins/glauth-postgres
url = https://github.com/glauth/glauth-postgres
[submodule "v2/pkg/plugins/glauth-pam"]
path = v2/pkg/plugins/glauth-pam
url = https://github.com/glauth/glauth-pam
-78
View File
@@ -1,78 +0,0 @@
language: go
# USE THIS alongside makefile tag detection: $TRAVIS_TAG
go:
- 1.9.x # last version
- 1.10.x # current version
# Speed up git clones
git:
depth: 1
# Anything in before_script that returns a nonzero exit code will
# flunk the build and immediately stop. It's sorta like having
# set -e enabled in bash.
before_script:
- sudo apt-get -qq update && sudo apt-get install -y ldap-utils oathtool # need this for integration tests
- sudo apt-get -qq update && sudo apt-get install -y go-bindata # need this for makefile
- GO_FILES=$(find . -iname '*.go' -type f | grep -v /vendor/) # All the .go files, excluding vendor/
#- go get github.com/golang/lint/golint # Linter
#- go get honnef.co/go/tools/cmd/megacheck # Badass static analyzer/linter
# - go get github.com/fzipp/gocyclo # Not sure what this is
# Run bindata for assets
- make bindata
# Fetch dependencies
- go get -t -v ./...
script:
- env # Dump the env to make debugging easier
- diff -u <(echo -n) <(gofmt -d ./) # Fail if a .go file hasn't been formatted with 'go fmt'
# - go vet ./... " # go vet is the official Go static analyzer
# - go test -v -race ./... # Run all the tests with the race detector enabled
# - megacheck ./... # "go vet on steroids" + linter
# - gocyclo -over 19 $GO_FILES # forbid code with huge functions
# - golint -set_exit_status $(go list ./...) # one last linter
# Unit tests
# - tbd - use 'go test' here when we have unit tests written. example command below. This will generate output for codecov
# - go test -race -coverprofile=coverage.txt -covermode=atomic
# Full compile test
# - go build -o ./glauth glauth.go bindata.go ldapbackend.go webapi.go configbackend.go
- make all
# Output hashes to the build log
- cat bin/*.sha256
# Integration test with compiled binary
- make test
# after_success:
# - bash <(curl -s https://codecov.io/bash)
deploy:
provider: releases
api_key:
secure: CsQb802CJJlum5suwo3Yvw1TkmX56O6TPrtJqMI0Qm6A878dhrZIjyVmfG3Iq9rtZvfFhK+Y3SIfPZp8BjV3cicc+X1Yh1VvMG12ukqHSruvGvLG49dtnq40P31u85Y4HdBk+W8YIsCQHEonsID9l+q2LbhuiC358UF+HNgzESEz6ZyTZz0AvAJzm1uyiUiPTHMFwqN7qQsookmLXf490rqp9YmFFqte+o3/JwoYXc3pE/rBMJitPSJNGULzu2QofV5gJ0JxgPWlfF5DsHHEi1jhpBcTfiFzEvkdcl1SVme9MMNPue7j8u9+PLJ6uIdlnTeBcMPKgtzOg/kBqY4+3QzY4aPXjKm3+MF3Cau66+g3QC7dKenvYefapQYN7t1SSpiR9X3TLWZrBbw2ovMe3em+uMnIM08bemzj8DapRgKXmYKWnwTlA9KLf0Vl22255OzMGarkhx94cDvUHmEkNo7qBXqmUxPo0Fc5XJyD7JmUNgYoWj0/NjyN09VG22FL2FW44HR3eitq2wSFSMO/jsC2rLuCnyLHViP+O2YJu0T4Q3gdQUcdQmjT7y8ogH5Shn9t2wY4NRxI3MyXO7+fGEOj1DaD6CFQwcSwbJVpIzS6J8z7+rRrzE9QkXeCmwNNt5UkpR9lYmOEzpc00rTRimwjfmJn/nTXsIDiQg9iZJM=
file_glob: true
file: bin/*
draft: true
name: "DRAFT RELEASE: $TRAVIS_TAG"
body: "Draft release, committed with message: $TRAVIS_COMMIT_MESSAGE"
body: "# Commit Details<br />$TRAVIS_COMMIT_MESSAGE<br /><br /># Note About Non-Linux 64bit Files<br /> While I expect the code is system-independent, there is not a good (and free) CI system which can be easily used to continuously test releases on ARM, BSD, Linux-32bit, and Windows.<br /><br />As such, the primary release platform is Linux 64 bit. The extent of testing on these packages consists solely of cross-compiling for these architectures from a linux 64 bit system.<br /><br />We will accept PRs which fix bugs on these platforms, but be aware these binaries will not be tested regularly, and instead are provided for the convenience of those who feel comfortable using them with this knowledge."
skip_cleanup: true
on:
branch: master
tags: true
condition: $TRAVIS_GO_VERSION = "1.10.1"
-55
View File
@@ -1,55 +0,0 @@
#################
# Build Step
#################
FROM golang:alpine as build
MAINTAINER Ben Yanke <ben@benyanke.com>
# Setup work env
RUN mkdir /app /tmp/gocode
ADD . /app/
WORKDIR /app
# Required envs for GO
ENV GOPATH=/tmp/gocode
ENV GOOS=linux
ENV GOARCH=amd64
# Only needed for alpine builds
RUN apk add --no-cache git bzr make
# Install deps
RUN go get -d -v ./...
# Run go-bindata to embed data for API
RUN go get -u github.com/jteeuwen/go-bindata/... && $GOPATH/bin/go-bindata -pkg=main assets && gofmt -w bindata.go
# Build and copy final result
RUN make linux64 && cp ./bin/glauth64 /app/glauth
#################
# Run Step
#################
FROM golang:alpine as run
MAINTAINER Ben Yanke <ben@benyanke.com>
# Copies a sample config to be used if a volume isn't mounted with user's config
ADD sample-simple.cfg /app/config/config.cfg
# Copy binary from build container
COPY --from=build /app/glauth /app/glauth
# Copy docker specific scripts from build container
COPY --from=build /app/scripts/docker/start.sh /app/docker/
COPY --from=build /app/scripts/docker/default-config.cfg /app/docker/
# Install init
RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.1/dumb-init_1.2.1_amd64 && chmod +x /usr/local/bin/dumb-init
# Expose web and LDAP ports
EXPOSE 389 5555
ENTRYPOINT ["/usr/local/bin/dumb-init", "--"]
CMD ["/bin/sh", "/app/docker/start.sh"]
-89
View File
@@ -1,89 +0,0 @@
VERSION=$(shell bin/glauth64 --version)
GIT_COMMIT=$(shell git rev-list -1 HEAD )
BUILD_TIME=$(shell date --utc +%Y%m%d_%H%M%SZ)
GIT_CLEAN=$(shell git status | grep -E "working (tree|directory) clean" | wc -l)
# Last git tag
LAST_GIT_TAG=$(shell git describe --abbrev=0 --tags 2> /dev/null)
# this=1 if the current commit is the tagged commit (ie, if this is a release build)
GIT_IS_TAG_COMMIT=$(shell git describe --abbrev=0 --tags > /dev/null 2> /dev/null && echo "1" || echo "0")
# Used when a tag isn't available
GIT_BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
# Build variables
BUILD_VARS=-X main.GitCommit=${GIT_COMMIT} -X main.GitBranch=${GIT_BRANCH} -X main.BuildTime=${BUILD_TIME} -X main.GitClean=${GIT_CLEAN} -X main.LastGitTag=${LAST_GIT_TAG} -X main.GitTagIsCommit=${GIT_IS_TAG_COMMIT}
BUILD_FILES=glauth.go bindata.go ldapbackend.go webapi.go configbackend.go
#####################
# High level commands
#####################
# Build and run - used for development
run: setup devrun cleanup
# Run the integration test on linux64 (eventually allow the binary to be set)
test: runtest
# Run build process for all binaries
all: setup binaries verify cleanup
# Run build process for only linux64
fast: setup linux64 verify cleanup
# list of binary formats to build
binaries: linux32 linux64 linuxarm32 linuxarm64 darwin64 win32 win64
# Setup commands to always run
setup: bindata format
#####################
# Subcommands
#####################
# Run integration test
runtest:
./scripts/travis/integration-test.sh cleanup
bindata:
go get -u github.com/jteeuwen/go-bindata/... && ${GOPATH}/bin/go-bindata -pkg=main assets && gofmt -w bindata.go
cleanup:
rm bindata.go
format:
go fmt
devrun:
go run ${BUILD_FILES} -c glauth.cfg
linux32:
GOOS=linux GOARCH=386 go build -ldflags "${BUILD_VARS}" -o bin/glauth32 ${BUILD_FILES} && cd bin && sha256sum glauth32 > glauth32.sha256
linux64:
GOOS=linux GOARCH=amd64 go build -ldflags "${BUILD_VARS}" -o bin/glauth64 ${BUILD_FILES} && cd bin && sha256sum glauth64 > glauth64.sha256
linuxarm32:
GOOS=linux GOARCH=386 go build -ldflags "${BUILD_VARS}" -o bin/glauth-arm32 ${BUILD_FILES} && cd bin && sha256sum glauth-arm32 > glauth-arm32.sha256
linuxarm64:
GOOS=linux GOARCH=arm64 go build -ldflags "${BUILD_VARS}" -o bin/glauth-arm64 ${BUILD_FILES} && cd bin && sha256sum glauth-arm64 > glauth-arm64.sha256
darwin64:
GOOS=darwin GOARCH=amd64 go build -ldflags "${BUILD_VARS}" -o bin/glauthOSX ${BUILD_FILES} && cd bin && sha256sum glauthOSX > glauthOSX.sha256
win32:
GOOS=windows GOARCH=386 go build -ldflags "${BUILD_VARS}" -o bin/glauth-win32 ${BUILD_FILES} && cd bin && sha256sum glauth-win32 > glauth-win32.sha256
win64:
GOOS=windows GOARCH=amd64 go build -ldflags "${BUILD_VARS}" -o bin/glauth-win64 ${BUILD_FILES} && cd bin && sha256sum glauth-win64 > glauth-win64.sha256
verify:
cd bin && sha256sum *.sha256 -c && cd ../;
+40 -132
View File
@@ -1,48 +1,51 @@
# GLAuth: LDAP authentication server for developers
Go-lang LDAP Authentication (GLAuth) is a secure, easy-to-use, LDAP server w/ configurable backends.
[![Travis Build - Master](https://img.shields.io/travis/glauth/glauth.svg)](https://travis-ci.org/glauth/glauth)
[![Last Commit](https://img.shields.io/github/last-commit/glauth/glauth.svg)](https://github.com/glauth/glauth/graphs/commit-activity)
[![Gitter](https://badges.gitter.im/glauth/community.svg)](https://gitter.im/glauth/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Matrix](https://img.shields.io/badge/chat-%2fjoin%20%23glauth_community:gitter.im-green)](hey)
[![](https://img.shields.io/docker/build/glauth/glauth.svg)](https://hub.docker.com/r/glauth/glauth/)
[![DockerHub Image Size](https://img.shields.io/imagelayers/image-size/glauth/glauth/latest.svg)](https://hub.docker.com/r/glauth/glauth/)
![GitHub all releases](https://img.shields.io/github/downloads/glauth/glauth/total)
![Docker pulls](https://badgen.net/docker/pulls/glauth/glauth)
[![Maintainability](https://img.shields.io/codeclimate/maintainability/glauth/glauth.svg)](https://codeclimate.com/github/glauth/glauth/maintainability)
[![Test Coverage](https://img.shields.io/codeclimate/coverage/glauth/glauth.svg)](https://codeclimate.com/github/glauth/glauth/test_coverage)
[![Donate via Paypal](https://img.shields.io/badge/Donate-PayPal-green.svg)](http://paypal.me/benyanke)
![GitHub last commit (branch)](https://img.shields.io/github/last-commit/glauth/glauth/master)
![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability-percentage/glauth/glauth)
* Centrally manage accounts across your infrastructure
* Centrally manage SSH keys, Linux accounts, and passwords for cloud servers.
* Lightweight alternative to OpenLDAP and Active Directory for development, or a homelab.
* Store your user directory in a local file, S3 or proxy to existing LDAP servers.
* Store your user directory in a file, local or in S3; SQL database; or proxy to existing LDAP servers.
* Two Factor Authentication (transparent to applications)
* Multiple backends can be chained to inject features
Use it to centralize account management across your Linux servers, your OSX machines, and your support applications (Jenkins, Apache/Nginx, Graylog2, and many more!).
### Contributing
Please base all PRs on [dev](https://github.com/nmcclain/glauth/tree/dev), not master.
- Please base all Pull Requests on [dev](https://github.com/glauth/glauth/tree/dev), not master.
- Format your code autonmatically using `gofmt -d ./` before committing
### Quickstart
This quickstart is a great way to try out GLAuth in a non-production environment. *Be warned that you should take the extra steps to setup SSL (TLS) for production use!*
1. Download a precompiled binary from the [releases](https://github.com/glauth/glauth/releases) page.
2. Download the [example config file](https://github.com/glauth/glauth/blob/master/sample-simple.cfg).
2. Download the [example config file](https://github.com/glauth/glauth/blob/master/v2/sample-simple.cfg).
3. Start the GLAuth server, referencing the path to the desired config file with `-c`.
- `sudo ./glauth64 -c sample-simple.cfg`
- `./glauth64 -c sample-simple.cfg`
4. Test with traditional LDAP tools
- For example: `ldapsearch -LLL -H ldap://localhost:389 -D cn=serviceuser,ou=svcaccts,dc=glauth,dc=com -w mysecret -x -bdc=glauth,dc=com cn=hackers`
- For example: `ldapsearch -LLL -H ldap://localhost:3893 -D cn=serviceuser,ou=svcaccts,dc=glauth,dc=com -w mysecret -x -bdc=glauth,dc=com cn=hackers`
### Make Commands
Note - makefile uses git data to inject build-time variables. For best results, run in the context of the git repo.
*make all* - run build binaries for platforms
*make fast* - run build for only linux 64 bit
*make run* - wrapper for the 'go run' command, setting up the needed tooling
*make test* - run the integration test on linux64 binary
### Documentation
<h4 align="center">:point_right: The latest version of GLauth's documentation is available at https://glauth.github.io/ :point_left:</h4>
<hr>
### Quickstart
Get started in three short [steps](https://glauth.github.io/docs/quickstart.html)
### Usage:
```
@@ -58,6 +61,10 @@ Options:
-K <aws_key_id> AWS Key ID.
-S <aws_secret_key> AWS Secret Key.
-r <aws_region> AWS Region [default: us-east-1].
--ldap <address> Listen address for the LDAP server.
--ldaps <address> Listen address for the LDAPS server.
--ldaps-cert <cert-file> Path to cert file for the LDAPS server.
--ldaps-key <key-file> Path to key file for the LDAPS server.
-h, --help Show this screen.
--version Show version.
```
@@ -74,132 +81,33 @@ Here's a sample config wth hardcoded users and groups:
baseDN = "dc=glauth,dc=com"
[[users]]
name = "hackers"
unixid = 5001
uidnumber = 5001
primarygroup = 5501
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
sshkeys = [ "ssh-dss AAAAB3..." ]
[[users]]
name = "uberhackers"
uidnumber = 5006
primarygroup = 5501
passbcrypt = "243261243130244B62463462656F7265504F762E794F324957746D656541326B4B46596275674A79336A476845764B616D65446169784E41384F4432" # dogood
[[groups]]
name = "superheros"
unixid = 5501
gidnumber = 5501
```
To create the password SHA hash, use this command: `echo -n "mysecret" | openssl dgst -sha256`
Instead of a local configuration file, GLAuth can fetch its configuration from S3. This is an easy way to ensure redundant GLAuth servers are always in-sync.
```unix
glauth -c s3://bucketname/glauth.cfg
```
In order to use S3, you must set your AWS credentials. Either:
1. set the -K and -S command-line flags **OR**
2. set the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables.
More configuration options are documented here: https://github.com/nmcclain/glauth/blob/master/sample-simple.cfg
### Required Fields
* Name
* The user's username
* ou
* ID of the user's primary group
* uidbumber
* The user's unix user id
* sshPublicKey
* Specify an array of public keys
### Optional Fields
* otherGroups
* Array of IDs of groups the user is a member of.
* Example: [5501, 5002]
* default = blank
* givenname
* First name
* Example: John
* default = blank
* sn
* Last name
* Example: Doe
* default = blank
* disabled
* Specify if account is active.
* Set to 'true' (without quotes) to make the LDAP entry add 'AccountStatus = inactive'
* default = false (active)
* mail
* Specify an email
* example: jdoe@example.com
* default = blank
* loginshell
* Specify a different login shell for the user
* Example: /bin/sh, or /sbin/nologin
* default = /bin/bash
* homedirectory
* Specify an overridden home directory for the user
* Example: /home/itadmin
* default = /home/[username]
* otpsecret
* Specify OTP secret used to validate OTP passcode
* Example: 3hnvnk4ycv44glzigd6s25j4dougs3rk
* default = blank
* yubikey
* Specify Yubikey ID for maching Yubikey OTP against the user
* Example: cccjgjgkhcbb
* default = blank
### OpenSSH keys:
GLAuth can store a user's SSH authorized keys. Add one or more keys per user as shown above, then setup the goklp helper: https://github.com/appliedtrust/goklp
### Two Factor Authentication
GLAuth can be configured to accept OTP tokens as appended to a users password. Support is added for both **TOTP tokens** (often known by it's most prominent implementation, "Google Authenticator") and **Yubikey OTP tokens**.
When using 2FA, append the 2FA code to the end of the password when authenticating. For example, if your password is "monkey" and your otp is "123456", enter "monkey123456" as your password.
#### TOTP Configuration
To enable TOTP authentication on a user, you can use a tool [like this](https://freeotp.github.io/qrcode.html) to generate a QR code (pick 'Timeout' and optionally let it generate a random secret for you), which can be scanned and used with the [Google Authenticator](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en) app. To enable TOTP authentication, configure the `otpsecret` for the user with the TOTP secret.
#### Yubikey Configuration
For Yubikey OTP token authentication, first [configure your Yubikey](https://www.yubico.com/products/services-software/personalization-tools/yubikey-otp/). After this, make sure to [request a `Client ID` and `Secret key` pair](https://upgrade.yubico.com/getapikey/).
Now configure the the `yubikeyclientid` and `yubikeysecret` fields in the general section in the configuration file.
To enable Yubikey OTP authentication for a user, you must specify their Yubikey ID on the users `yubikey` field. The Yubikey ID is the first 12 characters of the Yubikey OTP, as explained in the below chart.
![Yubikey OTP](https://developers.yubico.com/OTP/otp_details.png)
When a user has been configured with either one of the OTP options, the OTP authentication is required for the user. If both are configured, either one will work.
More configuration options are documented [here](https://glauth.github.io/docs/file.html) and in this [sample file](https://github.com/glauth/glauth/blob/master/v2/sample-simple.cfg)
### Backends:
For advanced users, GLAuth supports pluggable backends. Currently, it can use a local file, S3 or an existing LDAP infrastructure. In the future, we hope to have backends that support Mongo, SQL, and other datastores.
For advanced users, GLAuth supports pluggable backends. Currently, it can use a local file, S3 or an existing LDAP infrastructure. Through the use of optional plugins, you can connect SQL databases, PAM, and other datastores.
```toml
[backend]
datastore = "ldap"
servers = [ "ldaps://server1:636", "ldaps://server2:636" ]
```
### Production:
Any of the architectures above will work for production. Just remember:
# Stargazers over time
* Always use legit SSL certs for production!
[![Stargazers over time](https://starchart.cc/glauth/glauth.svg)](https://starchart.cc/glauth/glauth)
# Other Architectures
A small note about other architectures: while I expect the code is, for the most part, system-independent, there is not a good (and free) CI system which can be easily used to continuously test releases on ARM, BSD, Linux-32bit, and Windows. As such, all of the non-linux-64bit packages are provided as is. The extent of testing on these packages consists solely of cross-compiling for these architectures from a linux 64 bit system.
We will accept PRs which fix bugs on these platforms, but be aware these binaries will not be tested regularly, and instead are provided for the convenience of those who feel comfortable with this.
### Building:
You'll need go-bindata to build GLAuth. Then use the Makefile.
```unix
go get github.com/jteeuwen/go-bindata/...
make all
```
### Support
Support the ongoing development of GLAuth!
**Paypal**
[![Donate via Paypal](https://img.shields.io/badge/Donate-PayPal-green.svg)](http://paypal.me/benyanke)
**Bitcoin Address**
39z2Zkoc24LsuiqCQNFe7QrX4da3mzbGjK
-358
View File
@@ -1,358 +0,0 @@
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"github.com/GeertJohan/yubigo"
"github.com/nmcclain/ldap"
"github.com/pquerna/otp/totp"
"net"
"sort"
"strings"
)
type configHandler struct {
cfg *config
yubikeyAuth *yubigo.YubiAuth
}
func newConfigHandler(cfg *config, yubikeyAuth *yubigo.YubiAuth) Backend {
handler := configHandler{
cfg: cfg,
yubikeyAuth: yubikeyAuth}
return handler
}
//
func (h configHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resultCode ldap.LDAPResultCode, err error) {
bindDN = strings.ToLower(bindDN)
baseDN := strings.ToLower("," + h.cfg.Backend.BaseDN)
log.Debug(fmt.Sprintf("Bind request: bindDN: %s, BaseDN: %s, source: %s", bindDN, h.cfg.Backend.BaseDN, conn.RemoteAddr().String()))
stats_frontend.Add("bind_reqs", 1)
// parse the bindDN - ensure that the bindDN ends with the BaseDN
if !strings.HasSuffix(bindDN, baseDN) {
log.Warning(fmt.Sprintf("Bind Error: BindDN %s not our BaseDN %s", bindDN, h.cfg.Backend.BaseDN))
// log.Warning(fmt.Sprintf("Bind Error: BindDN %s not our BaseDN %s", bindDN, baseDN))
return ldap.LDAPResultInvalidCredentials, nil
}
parts := strings.Split(strings.TrimSuffix(bindDN, baseDN), ",")
groupName := ""
userName := ""
if len(parts) == 1 {
userName = strings.TrimPrefix(parts[0], "cn=")
} else if len(parts) == 2 {
userName = strings.TrimPrefix(parts[0], "cn=")
groupName = strings.TrimPrefix(parts[1], "ou=")
} else {
log.Warning(fmt.Sprintf("Bind Error: BindDN %s should have only one or two parts (has %d)", bindDN, len(parts)))
return ldap.LDAPResultInvalidCredentials, nil
}
// find the user
user := configUser{}
found := false
for _, u := range h.cfg.Users {
if u.Name == userName {
found = true
user = u
}
}
if !found {
log.Warning(fmt.Sprintf("Bind Error: User %s not found.", userName))
return ldap.LDAPResultInvalidCredentials, nil
}
// find the group
group := configGroup{}
found = false
for _, g := range h.cfg.Groups {
if g.Name == groupName {
found = true
group = g
}
}
if !found {
log.Warning(fmt.Sprintf("Bind Error: Group %s not found.", groupName))
return ldap.LDAPResultInvalidCredentials, nil
}
// validate group membership
if user.PrimaryGroup != group.UnixID {
log.Warning(fmt.Sprintf("Bind Error: User %s primary group is not %s.", userName, groupName))
return ldap.LDAPResultInvalidCredentials, nil
}
validotp := false
if len(user.Yubikey) == 0 && len(user.OTPSecret) == 0 {
validotp = true
}
if len(user.Yubikey) > 0 && h.yubikeyAuth != nil {
if len(bindSimplePw) > 44 {
otp := bindSimplePw[len(bindSimplePw)-44:]
yubikeyid := otp[0:12]
bindSimplePw = bindSimplePw[:len(bindSimplePw)-44]
if user.Yubikey == yubikeyid {
_, ok, _ := h.yubikeyAuth.Verify(otp)
if ok {
validotp = true
}
}
}
}
if len(user.OTPSecret) > 0 && !validotp {
if len(bindSimplePw) > 6 {
otp := bindSimplePw[len(bindSimplePw)-6:]
bindSimplePw = bindSimplePw[:len(bindSimplePw)-6]
validotp = totp.Validate(otp, user.OTPSecret)
}
}
if !validotp {
log.Warning(fmt.Sprintf("Bind Error: invalid token as %s from %s", bindDN, conn.RemoteAddr().String()))
return ldap.LDAPResultInvalidCredentials, nil
}
// finally, validate user's pw
hash := sha256.New()
hash.Write([]byte(bindSimplePw))
if user.PassSHA256 != hex.EncodeToString(hash.Sum(nil)) {
log.Warning(fmt.Sprintf("Bind Error: invalid credentials as %s from %s", bindDN, conn.RemoteAddr().String()))
return ldap.LDAPResultInvalidCredentials, nil
}
stats_frontend.Add("bind_successes", 1)
log.Debug("Bind success as %s from %s", bindDN, conn.RemoteAddr().String())
return ldap.LDAPResultSuccess, nil
}
//
func (h configHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (result ldap.ServerSearchResult, err error) {
bindDN = strings.ToLower(bindDN)
baseDN := strings.ToLower("," + h.cfg.Backend.BaseDN)
searchBaseDN := strings.ToLower(searchReq.BaseDN)
log.Debug("Search request as %s from %s for %s", bindDN, conn.RemoteAddr().String(), searchReq.Filter)
stats_frontend.Add("search_reqs", 1)
// validate the user is authenticated and has appropriate access
if len(bindDN) < 1 {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: Anonymous BindDN not allowed %s", bindDN)
}
if !strings.HasSuffix(bindDN, baseDN) {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: BindDN %s not in our BaseDN %s", bindDN, h.cfg.Backend.BaseDN)
}
if !strings.HasSuffix(searchBaseDN, h.cfg.Backend.BaseDN) {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: search BaseDN %s is not in our BaseDN %s", searchBaseDN, h.cfg.Backend.BaseDN)
}
// return all users in the config file - the LDAP library will filter results for us
entries := []*ldap.Entry{}
filterEntity, err := ldap.GetFilterObjectClass(searchReq.Filter)
if err != nil {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: error parsing filter: %s", searchReq.Filter)
}
switch filterEntity {
default:
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: unhandled filter type: %s [%s]", filterEntity, searchReq.Filter)
case "posixgroup":
for _, g := range h.cfg.Groups {
attrs := []*ldap.EntryAttribute{}
attrs = append(attrs, &ldap.EntryAttribute{"cn", []string{g.Name}})
attrs = append(attrs, &ldap.EntryAttribute{"description", []string{fmt.Sprintf("%s via LDAP", g.Name)}})
attrs = append(attrs, &ldap.EntryAttribute{"gidNumber", []string{fmt.Sprintf("%d", g.UnixID)}})
attrs = append(attrs, &ldap.EntryAttribute{"objectClass", []string{"posixGroup"}})
attrs = append(attrs, &ldap.EntryAttribute{"uniqueMember", h.getGroupMembers(g.UnixID)})
attrs = append(attrs, &ldap.EntryAttribute{"memberUid", h.getGroupMemberIDs(g.UnixID)})
dn := fmt.Sprintf("cn=%s,ou=groups,%s", g.Name, h.cfg.Backend.BaseDN)
entries = append(entries, &ldap.Entry{dn, attrs})
}
case "posixaccount", "":
for _, u := range h.cfg.Users {
attrs := []*ldap.EntryAttribute{}
attrs = append(attrs, &ldap.EntryAttribute{"cn", []string{u.Name}})
attrs = append(attrs, &ldap.EntryAttribute{"uid", []string{u.Name}})
if len(u.GivenName) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{"givenName", []string{u.GivenName}})
}
if len(u.SN) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{"sn", []string{u.SN}})
}
attrs = append(attrs, &ldap.EntryAttribute{"ou", []string{h.getGroupName(u.PrimaryGroup)}})
attrs = append(attrs, &ldap.EntryAttribute{"uidNumber", []string{fmt.Sprintf("%d", u.UnixID)}})
if u.Disabled {
attrs = append(attrs, &ldap.EntryAttribute{"accountStatus", []string{"inactive"}})
} else {
attrs = append(attrs, &ldap.EntryAttribute{"accountStatus", []string{"active"}})
}
if len(u.Mail) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{"mail", []string{u.Mail}})
}
attrs = append(attrs, &ldap.EntryAttribute{"objectClass", []string{"posixAccount"}})
if len(u.LoginShell) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{"loginShell", []string{u.LoginShell}})
} else {
attrs = append(attrs, &ldap.EntryAttribute{"loginShell", []string{"/bin/bash"}})
}
if len(u.Homedir) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{"homeDirectory", []string{u.Homedir}})
} else {
attrs = append(attrs, &ldap.EntryAttribute{"homeDirectory", []string{"/home/" + u.Name}})
}
attrs = append(attrs, &ldap.EntryAttribute{"description", []string{fmt.Sprintf("%s via LDAP", u.Name)}})
attrs = append(attrs, &ldap.EntryAttribute{"gecos", []string{fmt.Sprintf("%s via LDAP", u.Name)}})
attrs = append(attrs, &ldap.EntryAttribute{"gidNumber", []string{fmt.Sprintf("%d", u.PrimaryGroup)}})
attrs = append(attrs, &ldap.EntryAttribute{"memberOf", h.getGroupDNs(append(u.OtherGroups, u.PrimaryGroup))})
if len(u.SSHKeys) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{"sshPublicKey", u.SSHKeys})
}
dn := fmt.Sprintf("cn=%s,ou=%s,%s", u.Name, h.getGroupName(u.PrimaryGroup), h.cfg.Backend.BaseDN)
entries = append(entries, &ldap.Entry{dn, attrs})
}
}
stats_frontend.Add("search_successes", 1)
log.Debug("AP: Search OK: %s", searchReq.Filter)
return ldap.ServerSearchResult{entries, []string{}, []ldap.Control{}, ldap.LDAPResultSuccess}, nil
}
//
func (h configHandler) Close(boundDn string, conn net.Conn) error {
stats_frontend.Add("closes", 1)
return nil
}
//
func (h configHandler) getGroupMembers(gid int) []string {
members := make(map[string]bool)
for _, u := range h.cfg.Users {
if u.PrimaryGroup == gid {
dn := fmt.Sprintf("cn=%s,ou=%s,%s", u.Name, h.getGroupName(u.PrimaryGroup), h.cfg.Backend.BaseDN)
members[dn] = true
} else {
for _, othergid := range u.OtherGroups {
if othergid == gid {
dn := fmt.Sprintf("cn=%s,ou=%s,%s", u.Name, h.getGroupName(u.PrimaryGroup), h.cfg.Backend.BaseDN)
members[dn] = true
}
}
}
}
for _, g := range h.cfg.Groups {
if gid == g.UnixID {
for _, includegroupid := range g.IncludeGroups {
if includegroupid != gid {
includegroupmembers := h.getGroupMembers(includegroupid)
for _, includegroupmember := range includegroupmembers {
members[includegroupmember] = true
}
}
}
}
}
m := []string{}
for k, _ := range members {
m = append(m, k)
}
sort.Strings(m)
return m
}
//
func (h configHandler) getGroupMemberIDs(gid int) []string {
members := make(map[string]bool)
for _, u := range h.cfg.Users {
if u.PrimaryGroup == gid {
members[u.Name] = true
} else {
for _, othergid := range u.OtherGroups {
if othergid == gid {
members[u.Name] = true
}
}
}
}
for _, g := range h.cfg.Groups {
if gid == g.UnixID {
for _, includegroupid := range g.IncludeGroups {
if includegroupid == gid {
log.Warning(fmt.Sprintf("Group: %d - Ignoring myself as included group", includegroupid))
} else {
includegroupmemberids := h.getGroupMemberIDs(includegroupid)
for _, includegroupmemberid := range includegroupmemberids {
members[includegroupmemberid] = true
}
}
}
}
}
m := []string{}
for k, _ := range members {
m = append(m, k)
}
sort.Strings(m)
return m
}
// Converts an array of GUIDs into an array of DNs
func (h configHandler) getGroupDNs(gids []int) []string {
groups := make(map[string]bool)
for _, gid := range gids {
for _, g := range h.cfg.Groups {
if g.UnixID == gid {
dn := fmt.Sprintf("cn=%s,ou=groups,%s", g.Name, h.cfg.Backend.BaseDN)
groups[dn] = true
}
for _, includegroupid := range g.IncludeGroups {
if includegroupid == gid && g.UnixID != gid {
includegroupdns := h.getGroupDNs([]int{g.UnixID})
for _, includegroupdn := range includegroupdns {
groups[includegroupdn] = true
}
}
}
}
}
g := []string{}
for k, _ := range groups {
g = append(g, k)
}
sort.Strings(g)
return g
}
//
func (h configHandler) getGroupName(gid int) string {
for _, g := range h.cfg.Groups {
if g.UnixID == gid {
return g.Name
}
}
return ""
}
-304
View File
@@ -1,304 +0,0 @@
package main
import (
"expvar"
"fmt"
"github.com/BurntSushi/toml"
"github.com/GeertJohan/yubigo"
"github.com/docopt/docopt-go"
"github.com/nmcclain/ldap"
"github.com/op/go-logging"
"gopkg.in/amz.v1/aws"
"gopkg.in/amz.v1/s3"
"os"
"strings"
)
// Set with buildtime vars
var LastGitTag string
var BuildTime string
var GitCommit string
var GitClean string
var GitBranch string
var GitTagIsCommit string
const programName = "glauth"
var usage = `glauth: securely expose your LDAP for external auth
Usage:
glauth [options] -c <file|s3url>
glauth -h --help
glauth --version
Options:
-c, --config <file> Config file.
-K <aws_key_id> AWS Key ID.
-S <aws_secret_key> AWS Secret Key.
-r <aws_region> AWS Region [default: us-east-1].
-h, --help Show this screen.
--version Show version.
`
// exposed expvar variables
var stats_frontend = expvar.NewMap("proxy_frontend")
var stats_backend = expvar.NewMap("proxy_backend")
var stats_general = expvar.NewMap("proxy")
// interface for backend handler
type Backend interface {
ldap.Binder
ldap.Searcher
ldap.Closer
}
// config file
type configBackend struct {
BaseDN string
Datastore string
Insecure bool // For LDAP backend only
Servers []string // For LDAP backend only
}
type configFrontend struct {
AllowedBaseDNs []string // For LDAP backend only
Cert string
Key string
Listen string
TLS bool
}
type configAPI struct {
Cert string
Enabled bool
Key string
Listen string
SecretToken string
TLS bool
}
type configUser struct {
Name string
OtherGroups []int
PassSHA256 string
PrimaryGroup int
SSHKeys []string
OTPSecret string
Yubikey string
Disabled bool
UnixID int
Mail string
LoginShell string
GivenName string
SN string
Homedir string
}
type configGroup struct {
Name string
UnixID int
IncludeGroups []int
}
type config struct {
API configAPI
Backend configBackend
Debug bool
YubikeyClientID string
YubikeySecret string
Frontend configFrontend
Groups []configGroup
Syslog bool
Users []configUser
ConfigFile string
AwsAccessKeyId string
AwsSecretAccessKey string
AwsRegion string
}
var log = logging.MustGetLogger(programName)
// Reads builtime vars and returns a full string containing info about
// the currently running version of the software. Primarily used by the
// --version flag at runtime.
func getVersionString() string {
var versionstr string
versionstr = "GLauth"
// Notate the git context of the build
switch {
// If a release, use the tag
case GitClean == "1" && GitTagIsCommit == "1":
versionstr += " " + LastGitTag + "\n\n"
// If this branch had a tag before, mention the branch and the tag to give a rough idea of the base version
case len(GitBranch) > 1 && len(LastGitTag) > 1:
versionstr += "\nNon-release build from branch " + GitBranch + ", based on tag " + LastGitTag + "\n\n"
// If no previous tag specified, just mention the branch
case len(GitBranch) > 1:
versionstr += "\nNon-release build from branch " + GitBranch + "\n\n"
// Fallback message, if all else fails
default:
versionstr += "\nNon-release build\n\n"
}
// Include build time
if len(BuildTime) > 1 {
versionstr += "Build time: " + BuildTime + "\n"
}
// Add commit hash
if GitClean == "1" && len(GitCommit) > 1 {
versionstr += "Commit: " + GitCommit + "\n"
}
return versionstr
}
func main() {
stderr := initLogging()
log.Debug("AP start")
cfg, err := doConfig()
if err != nil {
log.Fatalf("Configuration file error: %s", err.Error())
}
if cfg.Syslog {
enableSyslog(stderr)
}
// stats
stats_general.Set("version", stringer(LastGitTag))
// web API
if cfg.API.Enabled {
log.Debug("Web API enabled")
go RunAPI(cfg)
}
yubiAuth := (*yubigo.YubiAuth)(nil)
if len(cfg.YubikeyClientID) > 0 && len(cfg.YubikeySecret) > 0 {
yubiAuth, err = yubigo.NewYubiAuth(cfg.YubikeyClientID, cfg.YubikeySecret)
if err != nil {
log.Fatalf("Yubikey Auth failed")
}
}
// configure the backend
s := ldap.NewServer()
s.EnforceLDAP = true
var handler Backend
switch cfg.Backend.Datastore {
case "ldap":
handler = newLdapHandler(cfg)
case "config":
handler = newConfigHandler(cfg, yubiAuth)
default:
log.Fatalf("Unsupported backend %s - must be 'config' or 'ldap'.", cfg.Backend.Datastore)
}
log.Notice(fmt.Sprintf("Using %s backend", cfg.Backend.Datastore))
s.BindFunc("", handler)
s.SearchFunc("", handler)
s.CloseFunc("", handler)
// start the frontend server
if cfg.Frontend.TLS {
log.Notice(fmt.Sprintf("Frontend LDAPS server listening on %s", cfg.Frontend.Listen))
if err := s.ListenAndServeTLS(cfg.Frontend.Listen, cfg.Frontend.Cert, cfg.Frontend.Key); err != nil {
log.Fatalf("LDAP Server Failed: %s", err.Error())
}
} else {
log.Notice(fmt.Sprintf("Frontend LDAP server listening on %s", cfg.Frontend.Listen))
if err := s.ListenAndServe(cfg.Frontend.Listen); err != nil {
log.Fatalf("LDAP Server Failed: %s", err.Error())
}
}
log.Critical("AP exit")
}
// doConfig reads the cli flags and config file
func doConfig() (*config, error) {
cfg := config{}
// setup defaults
cfg.Frontend.TLS = true
// parse the command-line args
args, err := docopt.Parse(usage, nil, true, getVersionString(), false)
if err != nil {
return &cfg, err
}
// parse the config file
if strings.HasPrefix(args["--config"].(string), "s3://") {
if _, present := aws.Regions[args["-r"].(string)]; present == false {
return &cfg, fmt.Errorf("Invalid AWS region: %s", args["-r"])
}
region := aws.Regions[args["-r"].(string)]
auth, err := aws.EnvAuth()
if err != nil {
if args["-K"] == nil || args["-S"] == nil {
return &cfg, fmt.Errorf("AWS credentials not found: must use -K and -S flags, or set these env vars:\n\texport AWS_ACCESS_KEY_ID=\"AAA...\"\n\texport AWS_SECRET_ACCESS_KEY=\"BBBB...\"\n")
}
auth = aws.Auth{
AccessKey: args["-K"].(string),
SecretKey: args["-S"].(string),
}
}
// parse S3 url
s3url := strings.TrimPrefix(args["--config"].(string), "s3://")
parts := strings.SplitN(s3url, "/", 2)
if len(parts) != 2 {
return &cfg, fmt.Errorf("Invalid S3 URL: %s", s3url)
}
b := s3.New(auth, region).Bucket(parts[0])
tomlData, err := b.Get(parts[1])
if err != nil {
return &cfg, err
}
if _, err := toml.Decode(string(tomlData), &cfg); err != nil {
return &cfg, err
}
} else { // local config file
if _, err := toml.DecodeFile(args["--config"].(string), &cfg); err != nil {
return &cfg, err
}
}
// enable features
if cfg.Debug {
logging.SetLevel(logging.DEBUG, programName)
log.Debug("Debugging enabled")
}
switch cfg.Backend.Datastore {
case "":
cfg.Backend.Datastore = "config"
case "config":
case "ldap":
default:
return &cfg, fmt.Errorf("Invalid backend %s - must be 'config' or 'ldap'.", cfg.Backend.Datastore)
}
return &cfg, nil
}
// initLogging sets up logging to stderr
func initLogging() *logging.LogBackend {
format := "%{color}%{time:15:04:05.000000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}"
logBackend := logging.NewLogBackend(os.Stderr, "", 0)
logging.SetBackend(logBackend)
logging.SetLevel(logging.NOTICE, programName)
logging.SetFormatter(logging.MustStringFormatter(format))
return logBackend
}
// enableSyslog turns on syslog and turns off color
func enableSyslog(stderrBackend *logging.LogBackend) {
format := "%{time:15:04:05.000000} %{shortfunc} ▶ %{level:.4s} %{id:03x} %{message}"
logging.SetFormatter(logging.MustStringFormatter(format))
syslogBackend, err := logging.NewSyslogBackend("")
if err != nil {
log.Fatal(err)
}
logging.SetBackend(stderrBackend, syslogBackend)
log.Debug("Syslog enabled")
}
-32
View File
@@ -1,32 +0,0 @@
package main
// Just the beginning of testing - more to be added
import (
"testing"
)
func TestGetVersionString(t *testing.T) {
// Setup config and check output
LastGitTag = "7"
BuildTime = "20180616_042045Z"
GitCommit = "a9837f3112fc049fd1e9faca6f56d8be1bec91bd"
GitClean = "1"
GitBranch = "dev"
GitTagIsCommit = "0"
versionOutput := getVersionString()
if versionOutput != "7" {
t.Errorf("Finish writing this test, output was: %s", versionOutput)
}
// TODO: write these cases
// Cases to test:
// 1) if GitTagIsCommit=1 - check for release-based strings (this build is release)
// 2) if GitClean=1 - check for "Commit: " as well as value of commit str
// 3) if GitClean=0 - Ensure "Commit: " is NOT there, as well ensure value of commit str is not there
// 4) do additional checks for release behavior, as well as branch name
}
+3
View File
@@ -0,0 +1,3 @@
go 1.19
use ./v2
-294
View File
@@ -1,294 +0,0 @@
package main
import (
"crypto/sha256"
"crypto/tls"
"encoding/json"
"fmt"
"github.com/kr/pretty"
"github.com/nmcclain/ldap"
"net"
"net/url"
"strconv"
"strings"
"sync"
"time"
)
type ldapHandler struct {
sessions map[string]ldapSession
servers []ldapBackend
lock sync.Mutex // for sessions and servers
doPing chan bool
cfg *config
}
type ldapSession struct {
id string
c net.Conn
ldap *ldap.Conn
}
type ldapBackendStatus int
const (
Down ldapBackendStatus = iota
Up
)
type ldapBackend struct {
Scheme string
Hostname string
Port int
Status ldapBackendStatus
Ping time.Duration
}
func newLdapHandler(cfg *config) Backend {
handler := ldapHandler{ // set non-zero-value defaults here
sessions: make(map[string]ldapSession),
doPing: make(chan bool),
cfg: cfg,
}
// parse LDAP URLs
for _, ldapurl := range cfg.Backend.Servers {
l, err := parseURL(ldapurl)
if err != nil {
log.Fatal(err)
}
handler.servers = append(handler.servers, l)
}
// test server connectivity before listening, then keep it updated
handler.monitorServers()
return handler
}
//
func (h ldapHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resultCode ldap.LDAPResultCode, err error) {
log.Debug("Bind request as %s from %s", bindDN, conn.RemoteAddr().String())
stats_frontend.Add("bind_reqs", 1)
s, err := h.getSession(conn)
if err != nil {
stats_frontend.Add("bind_ldapSession_errors", 1)
log.Debug("Bind ops error as %s from %s == %s", bindDN, conn.RemoteAddr().String(), err.Error())
return ldap.LDAPResultOperationsError, err
}
if err := s.ldap.Bind(bindDN, bindSimplePw); err != nil {
stats_frontend.Add("bind_errors", 1)
log.Debug("Bind invalid creds as %s from %s", bindDN, conn.RemoteAddr().String())
return ldap.LDAPResultInvalidCredentials, nil
}
stats_frontend.Add("bind_successes", 1)
log.Debug("Bind success as %s from %s", bindDN, conn.RemoteAddr().String())
return ldap.LDAPResultSuccess, nil
}
//
func (h ldapHandler) Search(boundDN string, searchReq ldap.SearchRequest, conn net.Conn) (result ldap.ServerSearchResult, err error) {
log.Debug("Search request as %s from %s for %s", boundDN, conn.RemoteAddr().String(), searchReq.Filter)
stats_frontend.Add("search_reqs", 1)
s, err := h.getSession(conn)
if err != nil {
stats_frontend.Add("search_ldapSession_errors", 1)
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, nil
}
search := ldap.NewSearchRequest(
searchReq.BaseDN,
searchReq.Scope,
searchReq.DerefAliases,
searchReq.SizeLimit,
searchReq.TimeLimit,
searchReq.TypesOnly,
searchReq.Filter,
searchReq.Attributes,
searchReq.Controls,
)
log.Debug("Search req to backend: %# v", pretty.Formatter(search))
sr, err := s.ldap.Search(search)
log.Debug("Backend Search result: %# v", pretty.Formatter(sr))
ssr := ldap.ServerSearchResult{
Entries: sr.Entries,
Referrals: sr.Referrals,
Controls: sr.Controls,
}
log.Debug("Frontend Search result: %# v", pretty.Formatter(ssr))
if err != nil {
e := err.(*ldap.Error)
log.Debug("Search Err: %# v", pretty.Formatter(err))
stats_frontend.Add("search_errors", 1)
ssr.ResultCode = ldap.LDAPResultCode(e.ResultCode)
return ssr, err
}
stats_frontend.Add("search_successes", 1)
log.Debug("AP: Search OK: %s -> num of entries = %d\n", search.Filter, len(ssr.Entries))
return ssr, nil
}
func (h ldapHandler) Close(boundDn string, conn net.Conn) error {
conn.Close() // close connection to the server when then client is closed
h.lock.Lock()
defer h.lock.Unlock()
delete(h.sessions, connID(conn))
stats_frontend.Add("closes", 1)
stats_backend.Add("closes", 1)
return nil
}
// monitorServers tests server connectivity before listening, then keeps it updated
func (h *ldapHandler) monitorServers() {
err := h.ping()
if err != nil {
log.Fatal(err)
}
go func() {
for {
select {
case <-h.doPing:
log.Notice("doPing requested due to server failure")
err = h.ping()
if err != nil {
log.Fatal(err)
}
case <-time.NewTimer(60 * time.Second).C:
log.Debug("doPing after timeout")
err = h.ping()
if err != nil {
log.Fatal(err)
}
}
}
}()
}
//
func (h ldapHandler) getSession(conn net.Conn) (ldapSession, error) {
id := connID(conn)
h.lock.Lock()
s, ok := h.sessions[id] // use server connection if it exists
h.lock.Unlock()
if !ok { // open a new server connection if not
var l *ldap.Conn
server, err := h.getBestServer() // pick the best server
if err != nil {
return ldapSession{}, err
}
dest := fmt.Sprintf("%s:%d", server.Hostname, server.Port)
if server.Scheme == "ldaps" {
tlsCfg := &tls.Config{}
if h.cfg.Backend.Insecure {
tlsCfg.InsecureSkipVerify = true
}
l, err = ldap.DialTLS("tcp", dest, tlsCfg)
} else if server.Scheme == "ldap" {
l, err = ldap.Dial("tcp", dest)
}
if err != nil {
select {
case h.doPing <- true: // non-blocking send
default:
}
return ldapSession{}, err
}
s = ldapSession{id: id, c: conn, ldap: l}
h.lock.Lock()
h.sessions[s.id] = s
h.lock.Unlock()
}
return s, nil
}
//
func (h ldapHandler) ping() error {
healthy := false
for k, s := range h.servers {
var l *ldap.Conn
var err error
dest := fmt.Sprintf("%s:%d", s.Hostname, s.Port)
start := time.Now()
if h.servers[0].Scheme == "ldaps" {
tlsCfg := &tls.Config{}
if h.cfg.Backend.Insecure {
tlsCfg.InsecureSkipVerify = true
}
l, err = ldap.DialTLS("tcp", dest, tlsCfg)
} else if h.servers[0].Scheme == "ldap" {
l, err = ldap.Dial("tcp", dest)
}
elapsed := time.Since(start)
h.lock.Lock()
if err != nil || l == nil {
log.Error(fmt.Sprintf("Server %s:%d ping failed: %s", s.Hostname, s.Port, err.Error()))
h.servers[k].Ping = 0
h.servers[k].Status = Down
} else {
healthy = true
h.servers[k].Ping = elapsed
h.servers[k].Status = Up
l.Close() // prank caller
}
h.lock.Unlock()
}
log.Debug("Server health: %# v", pretty.Formatter(h.servers))
b, err := json.Marshal(h.servers)
if err != nil {
log.Error(fmt.Sprintf("Error encoding tail data: %s", err.Error()))
}
stats_backend.Set("servers", stringer(string(b)))
if healthy == false {
return fmt.Errorf("No healthy servers")
}
return nil
}
//
func (h ldapHandler) getBestServer() (ldapBackend, error) {
favorite := ldapBackend{}
forever, err := time.ParseDuration("30m")
if err != nil {
return ldapBackend{}, err
}
bestping := forever
for _, s := range h.servers {
if s.Status == Up && s.Ping < bestping {
favorite = s
bestping = s.Ping
}
}
if bestping == forever {
return ldapBackend{}, fmt.Errorf("No healthy servers found")
}
log.Debug("Best server: %# v", pretty.Formatter(favorite))
return favorite, nil
}
// helper functions
func connID(conn net.Conn) string {
h := sha256.New()
h.Write([]byte(conn.LocalAddr().String() + conn.RemoteAddr().String()))
sha := fmt.Sprintf("% x", h.Sum(nil))
return string(sha)
}
func parseURL(ldapurl string) (ldapBackend, error) {
u, err := url.Parse(ldapurl)
if err != nil {
return ldapBackend{}, err
}
var port int
if u.Scheme == "ldaps" {
port = 636
} else if u.Scheme == "ldap" {
port = 389
} else {
return ldapBackend{}, fmt.Errorf("Unknown LDAP scheme: %s", u.Scheme)
}
parts := strings.Split(u.Host, ":")
hostname := parts[0]
if len(parts) > 1 {
port, err = strconv.Atoi(parts[1])
if err != nil {
return ldapBackend{}, err
}
}
return ldapBackend{Scheme: u.Scheme, Hostname: hostname, Port: port}, nil
}
-83
View File
@@ -1,83 +0,0 @@
#################
# glauth.conf
#################
# General configuration.
debug = true
#syslog = true
# yubikeyclientid = "yubi-api-clientid"
# yubikeysecret = "yubi-api-secret"
#################
# The frontend section controls how clients connect to the proxy.
[frontend]
tls = false # enable TLS for production!!
listen = "localhost:3893"
cert = "cert.pem"
key = "key.pem"
#################
# The backend section controls the data store.
[backend]
datastore = "config"
baseDN = "dc=glauth,dc=com"
#################
# The users section contains a hardcoded list of valid users.
# to create a passSHA256: echo -n "mysecret" | openssl dgst -sha256
[[users]]
name = "hackers"
unixid = 5001
primarygroup = 5501
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
# This user record shows all of the possible fields available
[[users]]
name = "johndoe"
givenname="John"
sn="Doe"
mail = "jdoe@example.com"
unixid = 5002
primarygroup = 5501
loginShell = "/bin/sh"
homeDir = "/root"
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
sshkeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwVw3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4hC/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLVIfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XBYnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+txSuhw== rsa-key-20160209"]
[[users]]
name = "serviceuser"
unixid = 5003
primarygroup = 5502
passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret
# Test user showing 2 factor auth authentication
[[users]]
name = "otpuser"
unixid = 5004
primarygroup = 5501
passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret
otpsecret = "3hnvnk4ycv44glzigd6s25j4dougs3rk"
yubikey = "vvjrcfalhlaa"
#################
# The groups section contains a hardcoded list of valid users.
[[groups]]
name = "superheros"
unixid = 5501
[[groups]]
name = "svcaccts"
unixid = 5502
[[groups]]
name = "vpn"
unixid = 5503
includegroups = [ 5501 ]
#################
# Enable and configure the optional REST API here.
[api]
enabled = true
tls = false # enable TLS for production!!
listen = "localhost:5555"
cert = "cert.pem"
key = "key.pem"
-70
View File
@@ -1,70 +0,0 @@
#################
# glauth.conf
#################
# General configuration.
debug = true
#syslog = true
#################
# The frontend section controls how clients connect to the proxy.
[frontend]
tls = false # enable TLS for production!!
listen = "localhost:3893"
cert = "cert.pem"
key = "key.pem"
#################
# The backend section controls the data store.
[backend]
datastore = "config"
baseDN = "dc=glauth,dc=com"
#################
# The users section contains a hardcoded list of valid users.
# to create a passSHA256: echo -n "mysecret" | openssl dgst -sha256
[[users]]
name = "hackers"
unixid = 5001
primarygroup = 5501
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
# This user record shows all of the possible fields available
[[users]]
name = "johndoe"
givenname="John"
sn="Doe"
mail = "jdoe@example.com"
unixid = 5002
primarygroup = 5501
loginShell = "/bin/sh"
homeDir = "/root"
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
sshkeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwVw3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4hC/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLVIfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XBYnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+txSuhw== rsa-key-20160209"]
[[users]]
name = "serviceuser"
unixid = 5003
primarygroup = 5502
passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret
#################
# The groups section contains a hardcoded list of valid users.
[[groups]]
name = "superheros"
unixid = 5501
[[groups]]
name = "svcaccts"
unixid = 5502
#################
# Enable and configure the optional REST API here.
[api]
enabled = true
tls = false # enable TLS for production!!
listen = "localhost:5555"
cert = "cert.pem"
key = "key.pem"
-13
View File
@@ -1,13 +0,0 @@
#!/bin/sh
# Copy config file if it doesn't exist so that the app can start
if [ ! -f /app/config/config.cfg ] ; then
cp /app/docker/default-config.cfg /app/config/config.cfg;
fi
# Output version string to logs
/app/glauth --version
# Run app
/app/glauth -c /app/config/config.cfg
-9
View File
@@ -1,9 +0,0 @@
#!/bin/bash
img="localhost/glauth-dev"
sudo docker rmi -f "$img"
sudo docker build -f Dockerfile -t "$img" .
sudo docker run -P "$img"
@@ -1,104 +0,0 @@
dn: cn=hackers,ou=superheros,dc=glauth,dc=com
cn: hackers
uid: hackers
ou: superheros
uidNumber: 5001
accountStatus: active
objectClass: posixAccount
loginShell: /bin/bash
homeDirectory: /home/hackers
description: hackers via LDAP
gecos: hackers via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
dn: cn=johndoe,ou=superheros,dc=glauth,dc=com
cn: johndoe
uid: johndoe
givenName: John
sn: Doe
ou: superheros
uidNumber: 5002
accountStatus: active
mail: jdoe@example.com
objectClass: posixAccount
loginShell: /bin/sh
homeDirectory: /root
description: johndoe via LDAP
gecos: johndoe via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwV
w3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4h
C/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLV
IfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XB
YnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+
txSuhw== rsa-key-20160209
dn: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
cn: jamesdoe
uid: jamesdoe
givenName: James
sn: Doe
ou: superheros
uidNumber: 5003
accountStatus: active
mail: jdoe2@example.com
objectClass: posixAccount
loginShell: /bin/sh
homeDirectory: /home/jamesdoe
description: jamesdoe via LDAP
gecos: jamesdoe via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwV
w3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4h
C/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLV
IfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XB
YnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+
txSuhw== rsa-key-20160209
dn: cn=alexdoe,ou=superheros,dc=glauth,dc=com
cn: alexdoe
uid: alexdoe
givenName: Alex
sn: Doe
ou: superheros
uidNumber: 5004
accountStatus: active
mail: adoe@example.com
objectClass: posixAccount
loginShell: /bin/sh
homeDirectory: /home/alexdoe
description: alexdoe via LDAP
gecos: alexdoe via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
dn: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
cn: serviceuser
uid: serviceuser
ou: svcaccts
uidNumber: 5003
accountStatus: active
objectClass: posixAccount
loginShell: /bin/bash
homeDirectory: /home/serviceuser
description: serviceuser via LDAP
gecos: serviceuser via LDAP
gidNumber: 5502
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=svcaccts,ou=groups,dc=glauth,dc=com
@@ -1,78 +0,0 @@
dn: cn=superheros,ou=groups,dc=glauth,dc=com
cn: superheros
description: superheros via LDAP
gidNumber: 5501
objectClass: posixGroup
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=hackers,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=johndoe,ou=superheros,dc=glauth,dc=com
memberUid: alexdoe
memberUid: hackers
memberUid: jamesdoe
memberUid: johndoe
dn: cn=svcaccts,ou=groups,dc=glauth,dc=com
cn: svcaccts
description: svcaccts via LDAP
gidNumber: 5502
objectClass: posixGroup
uniqueMember: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
memberUid: serviceuser
dn: cn=vpnaccess,ou=groups,dc=glauth,dc=com
cn: vpnaccess
description: vpnaccess via LDAP
gidNumber: 5503
objectClass: posixGroup
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=hackers,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=johndoe,ou=superheros,dc=glauth,dc=com
memberUid: alexdoe
memberUid: hackers
memberUid: jamesdoe
memberUid: johndoe
dn: cn=allaccs,ou=groups,dc=glauth,dc=com
cn: allaccs
description: allaccs via LDAP
gidNumber: 5504
objectClass: posixGroup
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=hackers,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=johndoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
memberUid: alexdoe
memberUid: hackers
memberUid: jamesdoe
memberUid: johndoe
memberUid: serviceuser
dn: cn=mailadmin,ou=groups,dc=glauth,dc=com
cn: mailadmin
description: mailadmin via LDAP
gidNumber: 5505
objectClass: posixGroup
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
memberUid: alexdoe
memberUid: jamesdoe
dn: cn=webmail,ou=groups,dc=glauth,dc=com
cn: webmail
description: webmail via LDAP
gidNumber: 5506
objectClass: posixGroup
dn: cn=fulltime,ou=groups,dc=glauth,dc=com
cn: fulltime
description: fulltime via LDAP
gidNumber: 5507
objectClass: posixGroup
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
memberUid: alexdoe
memberUid: jamesdoe
@@ -1,16 +0,0 @@
dn: cn=hackers,ou=superheros,dc=glauth,dc=com
cn: hackers
uid: hackers
ou: superheros
uidNumber: 5001
accountStatus: active
objectClass: posixAccount
loginShell: /bin/bash
homeDirectory: /home/hackers
description: hackers via LDAP
gecos: hackers via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
@@ -1,15 +0,0 @@
dn: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
cn: serviceuser
uid: serviceuser
ou: svcaccts
uidNumber: 5003
accountStatus: active
objectClass: posixAccount
loginShell: /bin/bash
homeDirectory: /home/serviceuser
description: serviceuser via LDAP
gecos: serviceuser via LDAP
gidNumber: 5502
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=svcaccts,ou=groups,dc=glauth,dc=com
@@ -1,21 +0,0 @@
dn: cn=alexdoe,ou=superheros,dc=glauth,dc=com
cn: alexdoe
uid: alexdoe
givenName: Alex
sn: Doe
ou: superheros
uidNumber: 5004
accountStatus: active
mail: adoe@example.com
objectClass: posixAccount
loginShell: /bin/sh
homeDirectory: /home/alexdoe
description: alexdoe via LDAP
gecos: alexdoe via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
+22
View File
@@ -0,0 +1,22 @@
# Changelog
## [2.3.0](https://github.com/glauth/glauth/compare/v2.2.0...v2.3.0) (2023-10-02)
### ⚠ BREAKING CHANGES
* **plugins:** Rename the groups table to ldapgroups ([#326](https://github.com/glauth/glauth/issues/326))
### Features
* Update migration code to support table names ([#339](https://github.com/glauth/glauth/issues/339)) ([349431c](https://github.com/glauth/glauth/commit/349431c6caa0388d17ab987621eb9be5f019155e))
### Bug Fixes
* **plugins:** Rename the groups table to ldapgroups ([#326](https://github.com/glauth/glauth/issues/326)) ([675b236](https://github.com/glauth/glauth/commit/675b236328a21a65daa7876a1a3c6900b85b1964))
### Miscellaneous Chores
* release 2.3.0 ([0c11325](https://github.com/glauth/glauth/commit/0c11325a2482d5067c805a4c7ed948a5e337b8f8))
+5
View File
@@ -0,0 +1,5 @@
# Contributor Code of Conduct
This project adheres to No Code of Conduct. We are all adults. We accept anyone's contributions. Nothing else matters.
For more information please visit the [No Code of Conduct](https://github.com/domgetter/NCoC) homepage.
+239
View File
@@ -0,0 +1,239 @@
.DEFAULT_GOAL := help
VERSION=$(shell bin/linuxamd64/glauth --version)
GIT_COMMIT=$(shell git rev-list -1 HEAD )
BUILD_TIME=$(shell date -u +%Y%m%d_%H%M%SZ)
GIT_CLEAN=$(shell git status | grep -E "working (tree|directory) clean" | wc -l | sed 's/^[ ]*//')
# Last git tag
LAST_GIT_TAG=$(shell git describe --abbrev=0 --tags 2> /dev/null)
# this=1 if the current commit is the tagged commit (ie, if this is a release build)
GIT_IS_TAG_COMMIT=$(shell git describe --abbrev=0 --tags > /dev/null 2> /dev/null && echo "1" || echo "0")
# Used when a tag isn't available
GIT_BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
# Build variables
BUILD_VARS?=-s -w
BUILD_FILES=.
TRIM_FLAGS>?=-trimpath
# Targets
MAIN_TARGETS=linux/amd64,linux/386,linux/arm64,linux/arm-7,darwin/amd64,darwin/arm64,windows/amd64,windows/386
PLUGIN_TARGETS=linux/amd64,linux/386,linux/arm64,linux/arm-7,darwin/amd64,darwin/arm64
# For release process
GO_RELEASE_V=$(shell go version | { read _ _ v _; echo $${v#go}; })
# Build
GOOS?=linux
GOARCH?=amd64
# Plugins
-include pkg/plugins/*/Makefile
#####################
# High level commands
#####################
#help: @ List available tasks on this project
help:
@grep -E '[a-zA-Z\.\-]+:.*?@ .*$$' $(MAKEFILE_LIST)| tr -d '#' | sed -E 's/Makefile.//' | awk 'BEGIN {FS = ":.*?@ "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
#run: @ build and run - used for development
run: setup devrun
mocks:
go install go.uber.org/mock/mockgen@v0.3.0
# generate gomocks
go generate ./...
.PHONY: mocks
vet:
-go vet ./...
.PHONY: vet
#test: @ runs the integration test on linuxamd64 (eventually allow the binary to be set)
test: mocks vet
# also run unit tests for packages, skip glauth_test.go for now
go test -v -cover -coverprofile coverage.out ./pkg/... ./internal/...
$(MAKE) runtest
#all: @ run build process for all binaries
all: setup binaries verify
#fast: @ run build process for only linuxamd64
fast: setup linuxamd64
#binaries: @ list of binary formats to build
binaries: linux386 linuxamd64 linuxarm linuxarm64 darwinamd64 darwinarm64 win386 winamd64
#setup: @ setup commands to always run
setup: getdeps format
#####################
# Subcommands
#####################
# Run integration test
runtest:
./scripts/ci/integration-test.sh cleanup
# Get all dependencies
getdeps:
go get -d ./...
updatetest:
./scripts/ci/integration-test.sh
format:
go fmt
devrun:
go run ${BUILD_FILES} -c sample-simple.cfg
mkbindir:
@echo "create directory bin/$(GOOS)$(GOARCH)"
@mkdir -p bin/$(GOOS)$(GOARCH)
.PHONY: mkbindir
build: mkbindir
@go build ${TRIM_FLAGS} -ldflags "${BUILD_VARS}" -o bin/$(GOOS)$(GOARCH)/glauth -buildvcs=false .
$(MAKE) sha256
.PHONY: build
sha256:
@sha256sum bin/$(GOOS)$(GOARCH)/glauth > bin/$(GOOS)$(GOARCH)/glauth.sha256
.PHONY: sha256
linux386:
GOOS=linux GOARCH=386 $(MAKE) build
linuxamd64:
GOOS=linux GOARCH=amd64 $(MAKE) build
linuxarm:
GOOS=linux GOARCH=arm $(MAKE) build
linuxarm64:
GOOS=linux GOARCH=arm64 $(MAKE) build
darwinamd64:
GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 $(MAKE) build
darwinarm64:
GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 $(MAKE) build
win386:
GOOS=windows GOARCH=386 $(MAKE) build
winamd64:
GOOS=windows GOARCH=amd64 $(MAKE) build
verify:
@for binary in linux386 linuxamd64 linuxarm linuxarm64 darwinamd64 darwinarm64 win386 winamd64; do cd bin/$$binary && sha256sum glauth.sha256 -c && cd ../..; done
pull-plugin:
@git submodule add $U $M && \
(for pkg in $$(cat $$M/go.mod | awk '/^require [a-z]/{print $$2} /^\)/{rblock=0} rblock{if($$4!="indirect" && $$1!~"glauth"){print $$1}} /^require \(/{rblock=1}'); do if [ "$$(grep $$pkg go.mod &>/dev/null)" = "" ]; then go get $$pkg; fi; done)
#pull-base-plugins: @ pull plugins for local building
pull-base-plugins:
@U=https://github.com/glauth/glauth-sqlite M=pkg/plugins/glauth-sqlite make pull-plugin && \
U=https://github.com/glauth/glauth-mysql M=pkg/plugins/glauth-mysql make pull-plugin && \
U=https://github.com/glauth/glauth-postgres M=pkg/plugins/glauth-postgres make pull-plugin && \
U=https://github.com/glauth/glauth-pam M=pkg/plugins/glauth-pam make pull-plugin
forget-plugin:
rm -rf $M ../.git/modules/v2/$M && \
git config --remove-section submodule.v2/$M && \
git rm --cache $M && \
go mod tidy
#forget-plugin: @ remove plugins, restoring original file structure
forget-plugins:
for pkg in pkg/plugins/*/go.mod; do\
P=$$(echo $$pkg | cut -d'/' -f 3) M=$$(dirname $$pkg) make forget-plugin;\
done
#releasemain: @ build main binaries for distribution
releasemain:
@xgo -v -ldflags="${BUILD_VARS}" -trimpath -go ${GO_RELEASE_V} -out glauth -dest bin -buildvcs=false --targets="${MAIN_TARGETS}" .
releaseplugin:
@xgo -v -ldflags="${BUILD_VARS}" -trimpath -go ${GO_RELEASE_V} -out $P -dest bin -buildvcs=false -buildmode=plugin --targets="${PLUGIN_TARGETS}" --pkg $M/$P.go . && \
(cd bin && for lib in $$(ls $$P-*); do sudo mv $$lib $$lib.so; done)
#releaseplugins: @ build base plugins for distribution
releaseplugins:
@for pkg in pkg/plugins/*/go.mod; do\
M=pkg/plugins/$$(echo $$pkg | cut -d'/' -f 3) && P=$$(echo $$M | grep -oP 'glauth-\K\w+') && M=$$M P=$$P make releaseplugin;\
done
releasedockermain:
$(if $(TAG),,$(error Must set TAG))
$(if $(REPO),,$(error Must set REPO - glauth or other))
@mkdir -p docker/assets/linux/amd64 docker/assets/linux/arm64 docker/assets/linux/arm/v7 && \
cp -f bin/glauth-linux-amd64 docker/assets/linux/amd64/glauth && \
cp -f bin/glauth-linux-arm64 docker/assets/linux/arm64/glauth && \
cp -f bin/glauth-linux-arm-7 docker/assets/linux/arm/v7/glauth && \
docker buildx build --tag $$REPO/glauth:$$TAG -t $$REPO/glauth:latest -f docker/Dockerfile-standalone --platform linux/amd64,linux/arm64,linux/arm/v7 --push docker
releasedockerplugins:
$(if $(TAG),,$(error Must set TAG))
$(if $(REPO),,$(error Must set REPO - glauth or other))
@mkdir -p docker/assets/linux/amd64 docker/assets/linux/arm64 docker/assets/linux/arm/v7 && \
cp -f bin/sqlite-linux-amd64.so docker/assets/linux/amd64/sqlite.so && \
cp -f bin/sqlite-linux-arm64.so docker/assets/linux/arm64/sqlite.so && \
cp -f bin/sqlite-linux-arm-7.so docker/assets/linux/arm/v7/sqlite.so && \
cp -f bin/mysql-linux-amd64.so docker/assets/linux/amd64/mysql.so && \
cp -f bin/mysql-linux-arm64.so docker/assets/linux/arm64/mysql.so && \
cp -f bin/mysql-linux-arm-7.so docker/assets/linux/arm/v7/mysql.so && \
cp -f bin/postgres-linux-amd64.so docker/assets/linux/amd64/postgres.so && \
cp -f bin/postgres-linux-arm64.so docker/assets/linux/arm64/postgres.so && \
cp -f bin/postgres-linux-arm-7.so docker/assets/linux/arm/v7/postgres.so && \
docker buildx build --tag $$REPO/glauth-plugins:$$TAG -t $$REPO/glauth-plugins:latest -f docker/Dockerfile-plugins --platform linux/amd64,linux/arm64,linux/arm/v7 --push docker
releasedocker: releasedockermain releasedockerplugins
#testdocker: @ run integration test using docker
testdocker:
$(if $(REPO),,$(error Must set REPO - glauth or other))
@echo "==> Cleaning up any existing image to be on the safe side..." && \
(for image in $$(docker image ls -q "$$REPO/glauth*"); do \
for container in $$(docker container ls -a -q -f ancestor=$$image); do \
docker stop $$container; \
while [ "$$(docker container ls -q -f ancestor=$$image)" != "" ]; do sleep 1; done; \
docker rm $$container; \
done; \
docker rmi $$image; \
done) && \
echo "==> Running glauth main container..." && \
docker run -d --name glauth-test -p 3893:3893 $$REPO/glauth:latest && \
sleep 5 && \
if [ "$$(ldapsearch -LLL -H ldap://localhost:3893 -D cn=serviceuser,ou=svcaccts,dc=glauth,dc=com -w mysecret -x -bdc=glauth,dc=com cn=hackers | grep posixAccount)" != "" ]; then \
echo "Checked: Glauth is responding properly to ldapsearch query."; \
else \
echo "glauth check did not pass. Aborting."; \
exit 1; \
fi && \
echo "==> Stopping glauth main container..." && \
docker stop glauth-test && \
while [ "$$(docker ps -q -f name=glauth-test)" != "" ]; do sleep 1; done; \
docker rm glauth-test && \
echo "==> Running glauth plugins container..." && \
docker run -d --name glauth-test -p 3893:3893 $$REPO/glauth-plugins:latest && \
sleep 5 && \
if [ "$$(ldapsearch -LLL -H ldap://localhost:3893 -D cn=serviceuser,ou=svcaccts,dc=glauth,dc=com -w mysecret -x -bdc=glauth,dc=com cn=hackers | grep posixAccount)" != "" ]; then \
echo "Checked: Glauth is responding properly to ldapsearch query."; \
else \
echo "glauth check did not pass. Aborting."; \
exit 1; \
fi && \
echo "==> Stopping glauth plugins container..." && \
docker stop glauth-test && \
while [ "$$(docker ps -q -f name=glauth-test)" != "" ]; do sleep 1; done; \
docker rm glauth-test && \
echo "==> Testing complete."
.PHONY: all run test fast binaries setup getdeps runtest updatetest format devrun linux386 linuxamd64 linuxarm linuxarm64 darwinamd64 darwinarm64 win386 winamd64 verify pull-plugin pull-base-plugins forget-plugin forget-plugins releasemain releaseplugin release releaseplugins releasedockermain releaserdockerplugins releasedocker testdocker
+35
View File
@@ -0,0 +1,35 @@
#################
# Build Step
#################
FROM alpine:latest as build
RUN apk add --no-cache git make busybox-static dumb-init
#################
# Run Step
#################
FROM gcr.io/distroless/base-debian11 as run
LABEL maintainers="Ben Yanke <ben@benyanke.com>, Jörn Friedrich Dreyer <jfd@butonic.de>, Chris F Ravenscroft <chris@voilaweb.com>"
ARG TARGETPLATFORM
# Copy docker specific scripts from build container
COPY assets/start-plugins.sh /app/docker/start-plugins.sh
COPY assets/default-config-plugins.cfg /app/docker/default-config-plugins.cfg
COPY assets/gl.db /app/docker/gl.db
COPY assets/$TARGETPLATFORM/glauth /app/glauth
COPY assets/$TARGETPLATFORM/*.so /app/
# Just what we need
COPY --from=build /usr/bin/dumb-init /usr/bin/dumb-init
COPY --from=build /bin/busybox.static /bin/sh
COPY --from=build /bin/busybox.static /bin/ln
COPY --from=build /bin/busybox.static /bin/rm
RUN ln /bin/sh /usr/bin/cp && ln /bin/sh /usr/bin/mkdir && rm /bin/ln /bin/rm
# Expose web and LDAP ports
EXPOSE 389 636 5555
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/bin/sh", "/app/docker/start-plugins.sh"]
+33
View File
@@ -0,0 +1,33 @@
#################
# Build Step
#################
FROM alpine:latest as build
RUN apk add --no-cache git make busybox-static dumb-init
#################
# Run Step
#################
FROM gcr.io/distroless/base-debian11 as run
LABEL maintainers="Ben Yanke <ben@benyanke.com>, Jörn Friedrich Dreyer <jfd@butonic.de>, Chris F Ravenscroft <chris@voilaweb.com>"
ARG TARGETPLATFORM
# Copy docker specific scripts from build container
COPY assets/start-standalone.sh /app/docker/start-standalone.sh
COPY assets/default-config-standalone.cfg /app/docker/default-config-standalone.cfg
COPY assets/$TARGETPLATFORM/glauth /app/glauth
# Just what we need
COPY --from=build /usr/bin/dumb-init /usr/bin/dumb-init
COPY --from=build /bin/busybox.static /bin/sh
COPY --from=build /bin/busybox.static /bin/ln
COPY --from=build /bin/busybox.static /bin/rm
RUN ln /bin/sh /usr/bin/cp && ln /bin/sh /usr/bin/mkdir && rm /bin/ln /bin/rm
# Expose web and LDAP ports
EXPOSE 389 636 5555
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/bin/sh", "/app/docker/start-standalone.sh"]
@@ -0,0 +1,38 @@
#################
# glauth.conf
#################
# General configuration.
debug = true
#syslog = true
#################
[ldap]
enabled = true
listen = "0.0.0.0:3893"
[ldaps]
enabled = false
listen = "0.0.0.0:3894"
cert = "cert.pem"
key = "key.pem"
#################
# The backend section controls the data store.
[backend]
datastore = "plugin"
# If "plugin," uncomment the line below
plugin = "/app/sqlite.so"
pluginhandler = "NewSQLiteHandler"
baseDN = "dc=glauth,dc=com"
database = "/app/config/gl.db"
#################
# Enable and configure the optional REST API here.
[api]
enabled = true
tls = false # enable TLS for production!!
listen = "localhost:5555"
cert = "cert.pem"
key = "key.pem"
@@ -0,0 +1,150 @@
#################
# glauth.conf
#################
# General configuration.
debug = true
# syslog = true
#
# Enable hot-reload of configuration on changes
# - does NOT work [ldap], [ldaps], [backend] or [api] sections
# watchconfig = true
#################
# yubikeyclientid = "yubi-api-clientid"
# yubikeysecret = "yubi-api-secret"
#################
# Server configuration.
[ldap]
enabled = true
# run on a non privileged port
listen = "0.0.0.0:3893"
[ldaps]
# to enable ldaps genrerate a certificate, eg. with:
# openssl req -x509 -newkey rsa:4096 -keyout glauth.key -out glauth.crt -days 365 -nodes -subj '/CN=`hostname`'
enabled = false
listen = "0.0.0.0:3894"
cert = "glauth.crt"
key = "glauth.key"
#################
# The backend section controls the data store.
[backend]
datastore = "config"
baseDN = "dc=glauth,dc=com"
## Configure dn format to use structures like
## "uid=serviceuser,cn=svcaccts,$BASEDN" instead of "cn=serviceuser,ou=svcaccts,$BASEDN"
## to help ease migrations from other LDAP systems
# nameformat = "uid"
# groupformat = "cn"
## Configure ssh-key attribute name, default is 'sshPublicKey'
# sshkeyattr = "ipaSshPubKey"
[behaviors]
# Ignore all capabilities restrictions, for instance allowing every user to perform a search
IgnoreCapabilities = false
# Enable a "fail2ban" type backoff mechanism temporarily banning repeated failed login attempts
LimitFailedBinds = true
# How many failed login attempts are allowed before a ban is imposed
NumberOfFailedBinds = 3
# How long (in seconds) is the window for failed login attempts
PeriodOfFailedBinds = 10
# How long (in seconds) is the ban duration
BlockFailedBindsFor = 60
# Clean learnt IP addresses every N seconds
PruneSourceTableEvery = 600
# Clean learnt IP addresses not seen in N seconds
PruneSourcesOlderThan = 600
#################
# The users section contains a hardcoded list of valid users.
# to create a passSHA256: echo -n "mysecret" | openssl dgst -sha256
[[users]]
name = "hackers"
uidnumber = 5001
primarygroup = 5501
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
[[users.capabilities]]
action = "search"
object = "ou=superheros,dc=glauth,dc=com"
# This user record shows all of the possible fields available
[[users]]
name = "johndoe"
givenname="John"
sn="Doe"
mail = "jdoe@example.com"
uidnumber = 5002
primarygroup = 5501
loginShell = "/bin/sh"
homeDir = "/root"
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
sshkeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwVw3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4hC/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLVIfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XBYnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+txSuhw== rsa-key-20160209"]
passappsha256 = [
"c32255dbf6fd6b64883ec8801f793bccfa2a860f2b1ae1315cd95cdac1338efa", # TestAppPw1
"c9853d5f2599e90497e9f8cc671bd2022b0fb5d1bd7cfff92f079e8f8f02b8d3", # TestAppPw2
"4939efa7c87095dacb5e7e8b8cfb3a660fa1f5edcc9108f6d7ec20ea4d6b3a88", # TestAppPw3
]
[[users]]
name = "serviceuser"
mail = "serviceuser@example.com"
uidnumber = 5003
primarygroup = 5502
passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret
[[users.capabilities]]
action = "search"
object = "*"
# Test user showing 2 factor auth authentication
[[users]]
name = "otpuser"
uidnumber = 5004
primarygroup = 5501
passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret
otpsecret = "3hnvnk4ycv44glzigd6s25j4dougs3rk"
yubikey = "vvjrcfalhlaa"
[[users.capabilities]]
action = "search"
object = "ou=superheros,dc=glauth,dc=com"
[[users]]
name = "uberhackers"
uidnumber = 5005
primarygroup = 5501
# bcrypt format: hex($2y$2^<number of rounds>$<salt>$<hash>)
passappbcrypt = [
"243261243130244B62463462656F7265504F762E794F324957746D656541326B4B46596275674A79336A476845764B616D65446169784E41384F4432" # dogood
]
# uncomment and comment out above array to test password with otp code
# passappbcrypt = "243261243130244B62463462656F7265504F762E794F324957746D656541326B4B46596275674A79336A476845764B616D65446169784E41384F4432" # dogood
otpsecret = "3hnvnk4ycv44glzigd6s25j4dougs3rk"
#################
# The groups section contains a hardcoded list of valid users.
[[groups]]
name = "superheros"
gidnumber = 5501
[[groups]]
name = "svcaccts"
gidnumber = 5502
[[groups]]
name = "vpn"
gidnumber = 5503
includegroups = [ 5501 ]
#################
# Enable and configure the optional REST API here.
[api]
enabled = true
tls = false # enable TLS for production!!
listen = "0.0.0.0:5555"
cert = "cert.pem"
key = "key.pem"
Binary file not shown.
+32
View File
@@ -0,0 +1,32 @@
#!/bin/sh
# Copy config file if it doesn't exist so that the app can start
if [ ! -f /app/config/config.cfg ] ; then
echo "Config file not found at /app/config/config.cfg"
echo "Copying example configuration file to run."
mkdir -p /app/config
cp /app/docker/default-config-plugins.cfg /app/config/config.cfg || exit 1
# This file will be ignored when switching away from the default (sqlite)
cp /app/docker/gl.db /app/config/gl.db || exit 1
fi
echo "";
echo "Version and build information:";
echo "";
# Output version string to logs
/app/glauth --version
echo "";
echo "Starting GLauth now.";
echo "";
# Run app
cd /app
/app/glauth -c /app/config/config.cfg
echo ""
echo "GLauth has exited."
echo "Exiting."
+29
View File
@@ -0,0 +1,29 @@
#!/bin/sh
# Copy config file if it doesn't exist so that the app can start
if [ ! -f /app/config/config.cfg ] ; then
echo "Config file not found at /app/config/config.cfg"
echo "Copying example configuration file to run."
mkdir -p /app/config
cp /app/docker/default-config-standalone.cfg /app/config/config.cfg || exit 1
fi
echo "";
echo "Version and build information:";
echo "";
# Output version string to logs
/app/glauth --version
echo "";
echo "Starting GLauth now.";
echo "";
# Run app
/app/glauth -c /app/config/config.cfg
echo ""
echo "GLauth has exited."
echo "Exiting."
+263
View File
@@ -0,0 +1,263 @@
package main
import (
"crypto/tls"
"fmt"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/GeertJohan/yubigo"
"github.com/arl/statsviz"
docopt "github.com/docopt/docopt-go"
"github.com/fsnotify/fsnotify"
"github.com/glauth/glauth/v2/internal/monitoring"
_tls "github.com/glauth/glauth/v2/internal/tls"
"github.com/glauth/glauth/v2/internal/toml"
"github.com/glauth/glauth/v2/internal/version"
"github.com/glauth/glauth/v2/pkg/config"
"github.com/glauth/glauth/v2/pkg/frontend"
"github.com/glauth/glauth/v2/pkg/logging"
"github.com/glauth/glauth/v2/pkg/server"
"github.com/glauth/glauth/v2/pkg/stats"
"github.com/jinzhu/copier"
"github.com/rs/zerolog"
)
const programName = "glauth"
var usage = `glauth: securely expose your LDAP for external auth
Usage:
glauth [options] -c <file|s3 url>
glauth -h --help
glauth --version
Options:
-c, --config <file> Config file.
-K <aws_key_id> AWS Key ID.
-S <aws_secret_key> AWS Secret Key.
-r <aws_region> AWS Region [default: us-east-1].
--aws_endpoint_url <url> Custom S3 endpoint.
--ldap <address> Listen address for the LDAP server.
--ldaps <address> Listen address for the LDAPS server.
--ldaps-cert <cert-file> Path to cert file for the LDAPS server.
--ldaps-key <key-file> Path to key file for the LDAPS server.
--check-config Check configuration file and exit.
-h, --help Show this screen.
--version Show version.
`
var (
log zerolog.Logger
args map[string]interface{}
yubiAuth *yubigo.YubiAuth
activeConfig = &config.Config{}
)
func main() {
if err := parseArgs(); err != nil {
fmt.Println("Could not parse command-line arguments")
fmt.Println(err)
os.Exit(1)
}
checkConfig := false
if cc, ok := args["--check-config"]; ok {
if cc == true {
checkConfig = true
}
}
cfg, err := toml.NewConfig(checkConfig, getConfigLocation(), args)
if err != nil {
fmt.Println("Configuration file error")
fmt.Println(err)
os.Exit(1)
}
if checkConfig {
fmt.Println("Config file seems ok (but I am not checking much at this time)")
return
}
if err := copier.Copy(activeConfig, cfg); err != nil {
log.Info().Err(err).Msg("Could not save reloaded config. Holding on to old config")
}
log = logging.InitLogging(activeConfig.Debug, activeConfig.Syslog, activeConfig.StructuredLog)
if !checkConfig {
if cfg.Debug {
log.Info().Msg("Debugging enabled")
}
if cfg.Syslog {
log.Info().Msg("Syslog enabled")
}
}
log.Info().Msg("AP start")
startService()
}
func startService() {
// stats
stats.General.Set("version", stats.Stringer(version.Version))
// web API
if activeConfig.API.Enabled {
log.Info().Msg("Web API enabled")
if activeConfig.API.Internals {
statsviz.Register(
http.DefaultServeMux,
statsviz.Root("/internals"),
statsviz.SendFrequency(1000*time.Millisecond),
)
}
go frontend.RunAPI(
frontend.Logger(log),
frontend.Config(&activeConfig.API),
)
}
monitor := monitoring.NewMonitor(&log)
startConfigWatcher()
var err error
var tlsConfig *tls.Config
if c := activeConfig.LDAP; c.Enabled && c.TLS {
// TODO check if tls params are string or bytes and change config accordingly
tlsConfig, err = _tls.MakeTLS([]byte(c.TLSCert), []byte(c.TLSKey))
if err != nil {
log.Warn().Err(err).Msg("unable to configure TLS, proceeding without....StartTLS won't be supported")
}
}
s, err := server.NewServer(
server.Logger(log),
server.Config(activeConfig),
server.Monitor(monitor),
server.TLSConfig(tlsConfig),
)
if err != nil {
log.Error().Err(err).Msg("could not create server")
os.Exit(1)
}
if activeConfig.LDAP.Enabled {
go func() {
if err := s.ListenAndServe(); err != nil {
log.Error().Err(err).Msg("could not start LDAP server")
os.Exit(1)
}
}()
}
if activeConfig.LDAPS.Enabled {
go func() {
if err := s.ListenAndServeTLS(); err != nil {
log.Error().Err(err).Msg("could not start LDAPS server")
os.Exit(1)
}
}()
}
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
// Block until we receive our signal.
<-c
// Doesn't block if no connections, but will otherwise wait
// until the timeout deadline.
s.Shutdown()
// Optionally, you could run srv.Shutdown in a goroutine and block on
// <-ctx.Done() if your application should wait for other services
// to finalize based on context cancellation.
log.Info().Msg("AP exit")
os.Exit(0)
}
func startConfigWatcher() {
configFileLocation := getConfigLocation()
if !activeConfig.WatchConfig || strings.HasPrefix(configFileLocation, "s3://") {
return
}
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Error().Err(err).Msg("could not start config-watcher")
return
}
ticker := time.NewTicker(1 * time.Second)
go func() {
isChanged, isRemoved := false, false
for {
select {
case event := <-watcher.Events:
log.Info().Str("e", event.Op.String()).Msg("watcher got event")
if event.Op&fsnotify.Write == fsnotify.Write {
isChanged = true
} else if event.Op&fsnotify.Remove == fsnotify.Remove { // vim edit file with rename/remove
isChanged, isRemoved = true, true
}
case err := <-watcher.Errors:
log.Error().Err(err).Msg("watcher error")
case <-ticker.C:
// wakeup, try finding removed config
}
if _, err := os.Stat(configFileLocation); !os.IsNotExist(err) && (isRemoved || isChanged) {
if isRemoved {
log.Info().Str("file", configFileLocation).Msg("rewatching config")
watcher.Add(configFileLocation) // overwrite
isChanged, isRemoved = true, false
}
if isChanged {
cfg, err := toml.NewConfig(false, configFileLocation, args)
if err != nil {
log.Info().Err(err).Msg("Could not reload config. Holding on to old config")
} else {
log.Info().Msg("Config was reloaded")
if err := copier.Copy(activeConfig, cfg); err != nil {
log.Info().Err(err).Msg("Could not save reloaded config. Holding on to old config")
}
}
isChanged = false
}
}
}
}()
watcher.Add(configFileLocation)
}
func parseArgs() error {
var err error
if args, err = docopt.Parse(usage, nil, true, version.GetVersion(), false); err != nil {
return err
}
return nil
}
func getConfigLocation() string {
return args["--config"].(string)
}
+252
View File
@@ -0,0 +1,252 @@
package main
import (
"os"
"os/exec"
"strings"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
type testEnv struct {
checkanonymousrootDSE bool
checkTOTP bool
checkbindUPN bool
svcdn string
svcdnnogroup string
otpdn string
expectedinfo string
expectedaccount string
scopedaccount string
expectedfirstaccount string
expectedgroup string
checkemployeetype string
}
func TestIntegerStuff(t *testing.T) {
Convey("Preflight: Testing pre-requisites", t, func() {
Convey("bin/glauth should exist", func() {
info, err := os.Stat("bin/glauth")
So(err, ShouldEqual, nil)
Convey("bin/glauth should be executable", func() {
mode := uint32(info.Mode())
So(mode&0b001001001, ShouldNotEqual, 0)
Convey("Testing sample-simple local file-based LDAP server", func() {
svc := startSvc(SD, "bin/glauth", "-c", "sample-simple.cfg")
batteryOfTests(
t,
svc, testEnv{
checkanonymousrootDSE: true,
checkTOTP: true,
checkbindUPN: true,
expectedinfo: "supportedLDAPVersion: 3",
svcdn: "cn=serviceuser,ou=svcaccts,dc=glauth,dc=com",
svcdnnogroup: "cn=serviceuser,dc=glauth,dc=com",
otpdn: "cn=otpuser,ou=superheros,dc=glauth,dc=com",
expectedaccount: "dn: cn=hackers,ou=superheros,ou=users,dc=glauth,dc=com",
scopedaccount: "dn: cn=hackers,ou=superheros,dc=glauth,dc=com",
expectedfirstaccount: "dn: cn=hackers,ou=superheros,ou=users,dc=glauth,dc=com",
expectedgroup: "dn: ou=superheros,ou=users,dc=glauth,dc=com",
checkemployeetype: "cn=hackers",
})
stopSvc(svc)
})
matchingLibrary := doRunGetFirst(RD, "ls", "bin/sqlite.so")
if matchingLibrary == "bin/sqlite.so" {
Convey("Testing sample-database LDAP server", func() {
svc := startSvc(SD, "bin/glauth", "-c", "pkg/plugins/sample-database.cfg")
batteryOfTests(
t,
svc, testEnv{
checkanonymousrootDSE: true,
checkTOTP: false,
checkbindUPN: true,
expectedinfo: "supportedLDAPVersion: 3",
svcdn: "cn=serviceuser,ou=svcaccts,dc=glauth,dc=com",
svcdnnogroup: "cn=serviceuser,dc=glauth,dc=com",
otpdn: "cn=otpuser,ou=superheros,dc=glauth,dc=com",
expectedaccount: "dn: cn=hackers,ou=superheros,ou=users,dc=glauth,dc=com",
scopedaccount: "dn: cn=hackers,ou=superheros,dc=glauth,dc=com",
expectedfirstaccount: "dn: cn=hackers,ou=superheros,ou=users,dc=glauth,dc=com",
expectedgroup: "dn: ou=superheros,ou=users,dc=glauth,dc=com",
checkemployeetype: "",
})
stopSvc(svc)
})
}
})
})
})
matchingContainers := doRunGetFirst(RD, "sh", "-c", "docker ps | grep openldap-service | wc -l")
if matchingContainers == "1" {
Convey("Testing sample-simple local LDAP server", t, func() {
svc := startSvc(SD, "bin/glauth", "-c", "sample-ldap-injection.cfg")
batteryOfTests(
t,
svc, testEnv{
checkanonymousrootDSE: false,
checkTOTP: true,
checkbindUPN: false,
expectedinfo: "objectClass: top",
svcdn: "cn=serviceuser,cn=svcaccts,ou=users,dc=glauth,dc=com",
svcdnnogroup: "", // ignore
otpdn: "cn=otpuser,cn=superheros,ou=users,dc=glauth,dc=com",
expectedaccount: "dn: cn=hackers,cn=superheros,ou=users,dc=glauth,dc=com",
scopedaccount: "dn: cn=hackers,cn=superheros,dc=glauth,dc=com",
expectedfirstaccount: "dn: cn=johndoe,cn=superheros,ou=users,dc=glauth,dc=com",
expectedgroup: "dn: ou=superheros,ou=users,dc=glauth,dc=com",
checkemployeetype: "",
})
stopSvc(svc)
})
}
}
func batteryOfTests(t *testing.T, svc *exec.Cmd, env testEnv) {
Convey("When searching for the 'hackers' user", func() {
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "cn=hackers")
Convey("We should find them in the 'superheros' group", func() {
So(out, ShouldEqual, env.expectedaccount)
})
})
if env.svcdnnogroup != "" {
Convey("When searching for the 'hackers' user without binding with a group", func() {
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdnnogroup, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "cn=hackers")
Convey("We should find them in the 'superheros' group", func() {
So(out, ShouldEqual, env.expectedaccount)
})
})
}
if env.checkbindUPN {
Convey("When searching for the 'hackers' user after binding using the account's UPN", func() {
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", "serviceuser@example.com", "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "cn=hackers")
Convey("We should find them in the 'superheros' group", func() {
So(out, ShouldEqual, env.expectedaccount)
})
})
}
Convey("When querying the root SDE", func() {
out := doRunGetSecond(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-s", "base", "(objectclass=*)")
Convey("We should get some meta information", func() {
So(out, ShouldEqual, env.expectedinfo)
})
})
if env.checkanonymousrootDSE {
Convey("When querying the root SDE anonymously without authorizing in config file", func() {
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-x", "-s", "base", "(objectclass=*)")
Convey("We should get error 50", func() {
So(out, ShouldEqual, "exit status 50")
})
})
}
Convey("When enumerating posix groups", func() {
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "(objectclass=posixgroup)")
Convey("We should get a list starting with the 'superheros' group", func() {
So(out, ShouldEqual, env.expectedgroup)
})
})
Convey("When searching for members of the 'superheros' group", func() {
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "(memberOf=ou=superheros,ou=groups,dc=glauth,dc=com)")
Convey("We should get a list starting with the 'hackers' user", func() {
So(out, ShouldEqual, env.expectedfirstaccount)
})
})
Convey("When performing a complex search for members of 'superheros' group", func() {
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "(&(objectClass=*)(memberOf=ou=superheros,ou=groups,dc=glauth,dc=com))")
Convey("We should get a list starting with the 'hackers' user", func() {
So(out, ShouldEqual, env.expectedfirstaccount)
})
})
if env.checkTOTP {
Convey("When searching for the 'hacker' user using a TOTP-enabled account", func() {
otpvalue := doRunGetFirst(RD, "oathtool", "--totp", "-b", "-d", "6", "3hnvnk4ycv44glzigd6s25j4dougs3rk")
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.otpdn, "-w", "mysecret"+otpvalue, "-x", "-bou=superheros,dc=glauth,dc=com", "cn=hackers")
Convey("We should find them in in the 'superheros' group", func() {
So(out, ShouldEqual, env.scopedaccount)
})
})
Convey("When searching for the 'hacker' user using a TOTP-enabled account and no value", func() {
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.otpdn, "-w", "mysecret", "-x", "-bou=superheros,dc=glauth,dc=com", "cn=hackers")
Convey("We should get 'Invalid credentials(49)'", func() {
So(out, ShouldEqual, "exit status 49")
})
})
Convey("When searching for the 'hacker' user using a TOTP-enabled account and the wrong value", func() {
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.otpdn, "-w", "mysecret123456", "-x", "-bou=superheros,dc=glauth,dc=com", "cn=hackers")
Convey("We should get 'Invalid credentials(49)'", func() {
So(out, ShouldEqual, "exit status 49")
})
})
}
if env.checkemployeetype != "" {
Convey("When searching for the 'hacker' user", func() {
out := doRunGetSecond(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", env.checkemployeetype, "employeetype")
Convey("Employee type should be 'Intern'", func() {
So(out, ShouldEqual, "employeetype: Intern")
})
})
}
}
// -----=============================================================================----
const SD = 3 // Start Delay
const RD = 2 // Response Delay
func startSvc(delay time.Duration, name string, arg ...string) *exec.Cmd {
cmd := exec.Command(name, arg...)
cmd.Start()
if delay != 0 {
time.Sleep(time.Second * delay)
}
return cmd
}
func stopSvc(svc *exec.Cmd) {
svc.Process.Kill()
}
func doRunGetFirst(delay time.Duration, name string, arg ...string) string {
out := strings.SplitN(doRun(delay, name, arg...), "\n", 2)
if out == nil || len(out) < 1 {
return "*fail*"
}
return out[0]
}
func doRunGetSecond(delay time.Duration, name string, arg ...string) string {
out := strings.SplitN(doRun(delay, name, arg...), "\n", 3)
if out == nil || len(out) < 2 {
return "*fail*"
}
return out[1]
}
func doRun(delay time.Duration, name string, arg ...string) string {
out, err := exec.Command(name, arg...).Output()
if err != nil {
return err.Error()
}
if delay != 0 {
time.Sleep(time.Second * delay)
}
return strings.TrimSpace(string(out))
}
+48
View File
@@ -0,0 +1,48 @@
module github.com/glauth/glauth/v2
go 1.19
require (
github.com/BurntSushi/toml v1.3.2
github.com/GeertJohan/yubigo v0.0.0-20190917122436-175bc097e60e
github.com/arl/statsviz v0.6.0
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
github.com/fsnotify/fsnotify v1.6.0
github.com/glauth/ldap v0.0.0-20231210225823-b9bf4d1baf6e
github.com/jinzhu/copier v0.4.0
github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.17.0
github.com/prometheus/client_model v0.5.0
github.com/rs/zerolog v1.31.0
github.com/smartystreets/goconvey v1.8.1
github.com/yaegashi/msgraph.go v0.1.4
go.uber.org/mock v0.3.0
golang.org/x/crypto v0.14.0
gopkg.in/amz.v3 v3.0.0-20201001071545-24fc1eceb27b
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/kr/text v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rickb777/date v1.20.5 // indirect
github.com/rickb777/plural v1.4.1 // indirect
github.com/smarty/assertions v1.15.0 // indirect
golang.org/x/sys v0.13.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)
replace github.com/nmcclain/ldap => github.com/shipperizer/ldap v0.0.0-20231117095347-c1d6afe484ca
// replace github.com/nmcclain/ldap => ../../../ldap
+140
View File
@@ -0,0 +1,140 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/GeertJohan/yubigo v0.0.0-20190917122436-175bc097e60e h1:Bqtt5C+uVk+vH/t5dmB47uDCTwxw16EYHqvJnmY2aQc=
github.com/GeertJohan/yubigo v0.0.0-20190917122436-175bc097e60e/go.mod h1:njRCDrl+1RQ/A/+KVU8Ho2EWAxUSkohOWczdW3dzDG0=
github.com/arl/statsviz v0.6.0 h1:jbW1QJkEYQkufd//4NDYRSNBpwJNrdzPahF7ZmoGdyE=
github.com/arl/statsviz v0.6.0/go.mod h1:0toboo+YGSUXDaS4g1D5TVS4dXs7S7YYT5J/qnW2h8s=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/glauth/ldap v0.0.0-20231210225823-b9bf4d1baf6e h1:ohe1O2DUo198delHsQvA8O+CgAPP8wv1SmSxLPlO9ao=
github.com/glauth/ldap v0.0.0-20231210225823-b9bf4d1baf6e/go.mod h1:EHMcFuVSIs0huelHOhaGtj9IKMB/Dmxo+jDheipjtYA=
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rickb777/date v1.12.4/go.mod h1:xP0eo/I5qmUt97yRGClHZfyLZ3ikMw6v6SU5MOGZTE0=
github.com/rickb777/date v1.20.5 h1:Ybjz7J7ga9ui4VJizQpil0l330r6wkn6CicaoattIxQ=
github.com/rickb777/date v1.20.5/go.mod h1:6BPrm3/aQI0I8jvlD1fAlm/86k5eSeTQ2mR5FEmTnSw=
github.com/rickb777/plural v1.2.0/go.mod h1:UdpyWFCGbo3mvK3f/PfZOAOrkjzJlYN/sD46XNWJ+Es=
github.com/rickb777/plural v1.4.1 h1:5MMLcbIaapLFmvDGRT5iPk8877hpTPt8Y9cdSKRw9sU=
github.com/rickb777/plural v1.4.1/go.mod h1:kdmXUpmKBJTS0FtG/TFumd//VBWsNTD7zOw7x4umxNw=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/shipperizer/ldap v0.0.0-20231117095347-c1d6afe484ca h1:KSpZYIDb2ei4d+AxsFmC9KJaTNlmSiONSl6hLd5Qmfg=
github.com/shipperizer/ldap v0.0.0-20231117095347-c1d6afe484ca/go.mod h1:/KinL0zHM54Sk4gMgvODL26YGG7u4SFBOAKNpJmsJvc=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/yaegashi/msgraph.go v0.1.4 h1:leDXSczAbwBpYFSmmZrdByTiPoUw8dbTfNMetAjJvbw=
github.com/yaegashi/msgraph.go v0.1.4/go.mod h1:vgeYhHa5skJt/3lTyjGXThTZhwbhRnGo6uUxzoJIGME=
github.com/yaegashi/wtz.go v0.0.2/go.mod h1:nOLA5QXsmdkRxBkP5tljhua13ADHCKirLBrzPf4PEJc=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200529172331-a64b76657301/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/amz.v3 v3.0.0-20201001071545-24fc1eceb27b h1:tdSyAzD5FHJmZAZ13d5WexAo7YJkiyGphhzFfInMnDw=
gopkg.in/amz.v3 v3.0.0-20201001071545-24fc1eceb27b/go.mod h1:cE0dCGx2UfBTjLFlzEx4EXJUmoX6BXBoX9GjKOvqha4=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+28
View File
@@ -0,0 +1,28 @@
package monitoring
import (
"net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/zerolog"
)
type API struct {
logger zerolog.Logger
}
func (a *API) RegisterEndpoints(router *http.ServeMux) {
router.HandleFunc("/metrics", a.prometheusHTTP)
}
func (a *API) prometheusHTTP(w http.ResponseWriter, r *http.Request) {
promhttp.Handler().ServeHTTP(w, r)
}
func NewAPI(logger zerolog.Logger) *API {
a := new(API)
a.logger = logger
return a
}
+13
View File
@@ -0,0 +1,13 @@
package monitoring
import "github.com/glauth/ldap"
type MonitorInterface interface {
SetResponseTimeMetric(map[string]string, float64) error
SetLDAPMetric(map[string]string, float64) error
}
type LDAPServerInterface interface {
SetStats(bool)
GetStats() ldap.Stats
}
+60
View File
@@ -0,0 +1,60 @@
package monitoring
import (
"time"
"github.com/rs/zerolog"
)
type LDAPMonitorWatcher struct {
syncTicker *time.Ticker
ldap LDAPServerInterface
monitor MonitorInterface
logger *zerolog.Logger
}
func (m *LDAPMonitorWatcher) sync() {
for {
select {
case tick := <-m.syncTicker.C:
m.logger.Debug().Time("value", tick).Msg("Tick")
m.storeMetrics()
default:
continue
}
}
}
func (m *LDAPMonitorWatcher) storeMetrics() {
stats := m.ldap.GetStats()
if err := m.monitor.SetLDAPMetric(map[string]string{"type": "conns"}, float64(stats.Conns)); err != nil {
m.logger.Error().Err(err).Msg("failed to set metric")
}
if err := m.monitor.SetLDAPMetric(map[string]string{"type": "binds"}, float64(stats.Binds)); err != nil {
m.logger.Error().Err(err).Msg("failed to set metric")
}
if err := m.monitor.SetLDAPMetric(map[string]string{"type": "unbinds"}, float64(stats.Unbinds)); err != nil {
m.logger.Error().Err(err).Msg("failed to set metric")
}
if err := m.monitor.SetLDAPMetric(map[string]string{"type": "searches"}, float64(stats.Searches)); err != nil {
m.logger.Error().Err(err).Msg("failed to set metric")
}
}
func NewLDAPMonitorWatcher(ldap LDAPServerInterface, monitor MonitorInterface, logger *zerolog.Logger) *LDAPMonitorWatcher {
m := new(LDAPMonitorWatcher)
m.syncTicker = time.NewTicker(15 * time.Second)
m.ldap = ldap
m.monitor = monitor
m.logger = logger
m.ldap.SetStats(true)
go m.sync()
return m
}
+36
View File
@@ -0,0 +1,36 @@
package monitoring
import (
"testing"
"time"
"github.com/glauth/ldap"
"github.com/rs/zerolog"
"go.uber.org/mock/gomock"
)
//go:generate mockgen -build_flags=--mod=mod -package monitoring -destination ./mock_interfaces.go -source=./interfaces.go
func TestNewLDAPMonitorWatcherRunsOnASchedule(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockMonitor := NewMockMonitorInterface(ctrl)
mockLDAPServer := NewMockLDAPServerInterface(ctrl)
stats := ldap.Stats{}
mockLDAPServer.EXPECT().SetStats(true).Times(1)
mockLDAPServer.EXPECT().GetStats().MinTimes(1).Return(stats)
mockMonitor.EXPECT().SetLDAPMetric(map[string]string{"type": "conns"}, float64(0)).MinTimes(1)
mockMonitor.EXPECT().SetLDAPMetric(map[string]string{"type": "binds"}, float64(0)).MinTimes(1)
mockMonitor.EXPECT().SetLDAPMetric(map[string]string{"type": "unbinds"}, float64(0)).MinTimes(1)
mockMonitor.EXPECT().SetLDAPMetric(map[string]string{"type": "searches"}, float64(0)).MinTimes(1)
logger := zerolog.Nop()
m := NewLDAPMonitorWatcher(mockLDAPServer, mockMonitor, &logger)
m.syncTicker = time.NewTicker(5 * time.Microsecond)
// allow goroutine to start and ticker to tick
time.Sleep(10 * time.Millisecond)
}
+139
View File
@@ -0,0 +1,139 @@
package monitoring
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"regexp"
"strings"
"github.com/prometheus/client_golang/prometheus"
)
// The code below liberally borrows from https://github.com/albertito/prometheus-expvar-exporter
// and is adapted (just a little) for our purpose.
type Collector struct {
url string
names map[string]string
helps map[string]string
labelName map[string]string
}
func (c *Collector) Describe(ch chan<- *prometheus.Desc) {
return
}
func (c *Collector) Collect(ch chan<- prometheus.Metric) {
resp, err := http.Get(c.url)
if err != nil {
log.Printf("Error scraping %q: %v", c.url, err)
return
}
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("Error reading body of %q: %v", c.url, err)
return
}
// Replace "\xNN" with "?" because the default parser doesn't handle them
// well.
re := regexp.MustCompile(`\\x..`)
body = re.ReplaceAllFunc(body, func(s []byte) []byte {
return []byte("?")
})
var vs map[string]interface{}
err = json.Unmarshal(body, &vs)
if err != nil {
log.Printf("Error unmarshalling json from %q: %v", c.url, err)
return
}
for k, v := range vs {
name := strings.ReplaceAll(k, "/", "_")
if n, ok := c.names[k]; ok {
name = n
}
help := fmt.Sprintf("expvar %q", k)
if h, ok := c.helps[k]; ok {
help = h
}
lnames := []string{}
if ln, ok := c.labelName[k]; ok {
lnames = append(lnames, ln)
}
desc := prometheus.NewDesc(name, help, lnames, nil)
switch v := v.(type) {
case float64:
ch <- prometheus.MustNewConstMetric(desc, prometheus.UntypedValue, v)
case bool:
ch <- prometheus.MustNewConstMetric(desc, prometheus.UntypedValue,
valToFloat(v))
case map[string]interface{}:
// We only support explicitly written label names.
if len(lnames) != 1 {
continue
}
for lk, lv := range v {
ch <- prometheus.MustNewConstMetric(desc, prometheus.UntypedValue,
valToFloat(lv), lk)
}
case string:
// Not supported by Prometheus.
continue
case []interface{}:
// Not supported by Prometheus.
continue
default:
continue
}
}
}
func NewCollector(url string) *Collector {
c := new(Collector)
c.url = url
c.names = map[string]string{
"proxy": "glauth_proxy",
"proxy_frontend": "glauth_proxy_frontend",
"proxy_backend": "glauth_proxy_backend",
}
c.helps = map[string]string{
"proxy": "General Metrics",
"proxy_frontend": "Frontend Metrics",
"proxy_backend": "Backend Metrics",
}
c.labelName = map[string]string{
"proxy": "metric",
"proxy_frontend": "metric",
"proxy_backend": "metric",
}
prometheus.MustRegister(c)
return c
}
func valToFloat(v interface{}) float64 {
switch v := v.(type) {
case float64:
return v
case bool:
if v {
return 1.0
}
return 0.0
case string:
return 0.0
}
log.Printf("unexpected value type: %#v", v)
return 0.0
}
+109
View File
@@ -0,0 +1,109 @@
package monitoring
import (
"fmt"
"github.com/prometheus/client_golang/prometheus"
"github.com/rs/zerolog"
)
type Monitor struct {
responseTime *prometheus.HistogramVec
ldapMetric *prometheus.GaugeVec
logger *zerolog.Logger
}
func (m *Monitor) SetResponseTimeMetric(tags map[string]string, value float64) error {
if m.responseTime == nil {
return fmt.Errorf("metric not instantiated")
}
m.responseTime.With(tags).Observe(value)
return nil
}
func (m *Monitor) SetLDAPMetric(tags map[string]string, value float64) error {
if m.ldapMetric == nil {
return fmt.Errorf("metric not instantiated")
}
m.ldapMetric.With(tags).Set(value)
return nil
}
func (m *Monitor) constLabels() map[string]string {
return map[string]string{
"library": "github.com/glauth/glauth",
}
}
func (m *Monitor) registerHistograms() {
histograms := make([]*prometheus.HistogramVec, 0)
m.responseTime = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "tcp_response_time_seconds",
Help: "tcp_response_time_seconds",
ConstLabels: m.constLabels(),
},
[]string{"operation", "status"},
)
histograms = append(histograms, m.responseTime)
for _, histogram := range histograms {
err := prometheus.Register(histogram)
switch err.(type) {
case nil:
return
case prometheus.AlreadyRegisteredError:
m.logger.Debug().Interface("metric", histogram).Msg("metric already registered")
default:
m.logger.Error().Interface("metric", histogram).Msg("metric could not be registered")
}
}
}
func (m *Monitor) registerGauges() {
gauges := make([]*prometheus.GaugeVec, 0)
m.ldapMetric = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "ldap_metric",
Help: "ldap_metric",
ConstLabels: m.constLabels(),
},
[]string{"type"},
)
gauges = append(gauges, m.ldapMetric)
for _, gauge := range gauges {
err := prometheus.Register(gauge)
switch err.(type) {
case nil:
return
case prometheus.AlreadyRegisteredError:
m.logger.Debug().Interface("metric", gauge).Msg("metric already registered")
default:
m.logger.Error().Interface("metric", gauge).Msg("metric could not be registered")
}
}
}
func NewMonitor(logger *zerolog.Logger) *Monitor {
m := new(Monitor)
m.logger = logger
m.registerHistograms()
m.registerGauges()
return m
}
+62
View File
@@ -0,0 +1,62 @@
package monitoring
import (
"reflect"
"testing"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"github.com/rs/zerolog"
)
func TestNewMonitorImplementsInterface(t *testing.T) {
logger := zerolog.Nop()
m := NewMonitor(&logger)
i := reflect.TypeOf((*MonitorInterface)(nil)).Elem()
if !reflect.TypeOf(m).Implements(i) {
t.Fatal("Monitor doesn't implement MonitorInterface")
}
}
func TestMonitorSetLDAPMetricSucceeds(t *testing.T) {
logger := zerolog.Nop()
m := NewMonitor(&logger)
labels := map[string]string{"type": "test"}
m.SetLDAPMetric(labels, float64(10))
mLDAPMetric := dto.Metric{}
m.ldapMetric.With(labels).Write(&mLDAPMetric)
if mLDAPMetric.GetGauge().GetValue() != float64(10) {
t.Fatalf("metric value should have been set to %v", float64(10))
}
}
func TestMonitorSetResponseTimeMetricSucceeds(t *testing.T) {
logger := zerolog.Nop()
m := NewMonitor(&logger)
labels := map[string]string{"operation": "test", "status": "0"}
m.SetResponseTimeMetric(labels, float64(10))
mResponseTimeMetric := dto.Metric{}
m.responseTime.With(labels).(prometheus.Metric).Write(&mResponseTimeMetric)
if mResponseTimeMetric.GetHistogram().GetSampleSum() != float64(10) {
t.Fatalf("metric value should have been set to %v", float64(10))
}
for _, bucket := range mResponseTimeMetric.GetHistogram().GetBucket() {
if bucket.GetUpperBound() < float64(10) && bucket.GetCumulativeCount() != 0 {
t.Fatal("there should be no count for this metric bucket")
}
if bucket.GetUpperBound() >= float64(10) && bucket.GetCumulativeCount() != 1 {
t.Fatal("there should be one entry into this metric bucket")
}
}
}
+81
View File
@@ -0,0 +1,81 @@
package tls
import (
tls "crypto/tls"
"crypto/x509"
"encoding/pem"
"github.com/rs/zerolog"
)
var (
log zerolog.Logger
)
// MakeTLS generates a tls.Config
func MakeTLS(clientCert, key []byte) (*tls.Config, error) {
if clientCert == nil && key == nil {
return new(tls.Config), nil
}
var err error
cert, err := tls.X509KeyPair(clientCert, key)
if err != nil {
return nil, err
}
log.Debug().Str("key", string(key))
log.Debug().Str("client.certificate", string(clientCert))
log.Debug().Interface("certificate", cert)
if err != nil {
return nil, err
}
// Get the SystemCertPool, continue with an empty pool on error
rootCAs, err := x509.SystemCertPool()
if rootCAs == nil {
rootCAs = x509.NewCertPool()
log.Warn().Err(err).Msg("Using empty cert-pool")
} else {
log.Info().Msg("Using system cert-pool")
}
for _, cert := range DecodePEM(clientCert).Certificate {
x509Cert, err := x509.ParseCertificate(cert)
if err != nil {
log.Error().Err(err).Msg("issue parsing cert PEM")
}
rootCAs.AddCert(x509Cert)
}
log.Debug().Interface("root.ca", rootCAs)
log.Debug().Interface("certificates", []tls.Certificate{cert})
return &tls.Config{
RootCAs: rootCAs,
MinVersion: tls.VersionTLS10,
MaxVersion: tls.VersionTLS13,
Certificates: []tls.Certificate{cert},
}, nil
}
// DecodePEM builds a PEM certificate object
func DecodePEM(certPEM []byte) tls.Certificate {
var cert tls.Certificate
var certDER *pem.Block
for {
certDER, certPEM = pem.Decode(certPEM)
if certDER == nil {
break
}
if certDER.Type == "CERTIFICATE" {
cert.Certificate = append(cert.Certificate, certDER.Bytes)
}
}
return cert
}
+453
View File
@@ -0,0 +1,453 @@
package toml
import (
"bytes"
"fmt"
"os"
"path/filepath"
"reflect"
"strings"
"github.com/BurntSushi/toml"
"github.com/GeertJohan/yubigo"
"github.com/glauth/glauth/v2/pkg/config"
"github.com/rs/zerolog/log"
"gopkg.in/amz.v3/aws"
"gopkg.in/amz.v3/s3"
)
type Config struct {
Users []toml.Primitive
}
type User struct {
Name string
CustomAttributes []toml.Primitive
}
// NewConfig reads the cli flags and config file
func NewConfig(checkConfig bool, location string, args map[string]interface{}) (*config.Config, error) {
// Parse config-file into config{} struct
cfg, err := parseConfigFile(location, args)
if err != nil {
return nil, err
}
// Handle parsed flags
cfg, err = handleArgs(cfg, args)
if err != nil {
return nil, err
}
// Handle parsing of legacy [frontend] section into [ldap] and/or [ldaps] sections
cfg, err = handleLegacyConfig(cfg)
if err != nil {
return nil, err
}
cfg, err = validateConfig(cfg)
if err != nil {
return nil, err
}
// TODO @shipperizer reinstate this
// // Before greenlighting new config entirely, lets make sure the yubiauth works - in case they changed
if _, err := yubigo.NewYubiAuth(cfg.YubikeyClientID, cfg.YubikeySecret); err != nil && len(cfg.YubikeyClientID) > 0 && len(cfg.YubikeySecret) > 0 {
return nil, err
}
return cfg, nil
}
func parseConfigFile(configFileLocation string, args map[string]interface{}) (*config.Config, error) {
cfg := new(config.Config)
// setup defaults
cfg.LDAP.Enabled = false
cfg.LDAPS.Enabled = true
// parse the config file
if strings.HasPrefix(configFileLocation, "s3://") {
region, present := aws.Regions[args["-r"].(string)]
if !present {
return cfg, fmt.Errorf("invalid AWS region: %s", args["-r"])
}
if args["--aws_endpoint_url"] != nil {
region = aws.Region{
Name: "User defined",
S3Endpoint: args["--aws_endpoint_url"].(string),
}
present = true
}
auth, err := aws.EnvAuth()
if err != nil {
if args["-K"] == nil || args["-S"] == nil {
return cfg, fmt.Errorf("AWS credentials not found: must use -K and -S flags, or set these env vars:\n\texport AWS_ACCESS_KEY_ID=\"AAA...\"\n\texport AWS_SECRET_ACCESS_KEY=\"BBBB...\"\n")
}
auth = aws.Auth{
AccessKey: args["-K"].(string),
SecretKey: args["-S"].(string),
}
}
// parse S3 url
s3url := strings.TrimPrefix(configFileLocation, "s3://")
parts := strings.SplitN(s3url, "/", 2)
if len(parts) != 2 {
return cfg, fmt.Errorf("invalid S3 URL: %s", s3url)
}
b, err := s3.New(auth, region).Bucket(parts[0])
if err != nil {
return cfg, err
}
tomlData, err := b.Get(parts[1])
if err != nil {
return cfg, err
}
if _, err := toml.Decode(string(tomlData), cfg); err != nil {
return cfg, err
}
} else { // local config file
fInfo, err := os.Stat(configFileLocation)
if err != nil {
return cfg, fmt.Errorf("non-existent config path: %s", configFileLocation)
}
if fInfo.IsDir() { // multiple files in a directory
rawCfgStruct := make(map[string]interface{})
// To keep things simple, we are not going to use the default values built in Cfg
// so far (LDAP.Enabled, LDAPS.Enabled, etc) so do not forget to specify them!
/*
sourcebuf := new(bytes.Buffer)
err = toml.NewEncoder(sourcebuf).Encode(cfg)
if err != nil {
return cfg, err
}
var initialRawCfgStruct interface{}
if err := toml.Unmarshal(sourcebuf.Bytes(), &initialRawCfgStruct); err != nil {
return cfg, err
}
if err = mergeConfigs(&rawCfgStruct, initialRawCfgStruct); err != nil {
return cfg, err
}
*/
files, _ := os.ReadDir(configFileLocation)
for _, f := range files {
canonicalName := filepath.Join(configFileLocation, f.Name())
bs, _ := os.ReadFile(canonicalName)
var curRawCfgStruct interface{}
if err := toml.Unmarshal(bs, &curRawCfgStruct); err != nil {
return cfg, err
}
if err = mergeConfigs(&rawCfgStruct, curRawCfgStruct); err != nil {
return cfg, err
}
}
destbuf := new(bytes.Buffer)
err = toml.NewEncoder(destbuf).Encode(rawCfgStruct)
if err != nil {
return cfg, err
}
fmt.Println(destbuf.String())
merged := config.Config{}
if _, err = toml.Decode(destbuf.String(), &merged); err != nil {
return cfg, err
}
cfg = &merged
} else {
_, err = toml.DecodeFile(configFileLocation, cfg)
if err != nil {
return cfg, err
}
}
usersCustomAttributes(configFileLocation, cfg)
}
// Backward Compability
if cfg.Backend.Datastore != "" {
if cfg.Backends != nil {
return cfg, fmt.Errorf("you cannot specify both [Backend] and [[Backends]] directives in the same configuration ")
} else {
cfg.Backends = append(cfg.Backends, cfg.Backend)
}
}
// Patch with default values where not specified
for i := range cfg.Backends {
if cfg.Backends[i].NameFormat == "" {
cfg.Backends[i].NameFormat = "cn"
}
if cfg.Backends[i].GroupFormat == "" {
cfg.Backends[i].GroupFormat = "ou"
}
if cfg.Backends[i].SSHKeyAttr == "" {
cfg.Backends[i].SSHKeyAttr = "sshPublicKey"
}
}
//
return cfg, nil
}
// usersCustomAttributes changes config passed in by adding extra information coming from the custom attributes
func usersCustomAttributes(location string, config *config.Config) {
// TODO @shipperizer deal with multiple files like in line #126
c := new(Config)
md, err := toml.DecodeFile(location, c)
if err != nil {
log.Error().Err(err).Msg("issues parsing users...keep going")
return
}
for _, u := range c.Users {
user := new(User)
md.PrimitiveDecode(u, user)
if user.CustomAttributes == nil {
continue
}
for idx, cUser := range config.Users {
if cUser.Name != user.Name {
continue
}
x := make(map[string]interface{})
for _, attribute := range user.CustomAttributes {
err := md.PrimitiveDecode(attribute, x)
fmt.Println(err, x)
for k, v := range x {
if config.Users[idx].CustomAttrs == nil {
config.Users[idx].CustomAttrs = make(map[string]interface{})
}
config.Users[idx].CustomAttrs[k] = v
}
}
}
}
}
func mergeConfigs(config1 interface{}, config2 interface{}) error {
var merger func(int, string, interface{}, interface{}) error
merger = func(depth int, keyName string, cfg1 interface{}, cfg2 interface{}) error {
//fmt.Println(strings.Repeat(" ", depth), "Handling element: ", keyName, " for: ", cfg2)
switch element2 := cfg2.(type) {
case map[string]interface{}:
//fmt.Println(strings.Repeat(" ", depth), " - A map")
element2, ok := cfg2.(map[string]interface{})
if !ok {
return fmt.Errorf("config source: %s is not a map", keyName)
}
element1, ok := cfg1.(*map[string]interface{})
if !ok {
return fmt.Errorf("config dest: %s is not a map", keyName)
}
for k, _ := range element2 {
//fmt.Println(strings.Repeat(" ", depth), " - key: ", k)
_, ok := (*element1)[k]
if !ok {
(*element1)[k] = element2[k]
} else {
//fmt.Println(strings.Repeat(" ", depth), " - merging: ", element2[k])
asanarrayptr, ok := (*element1)[k].([]map[string]interface{})
if ok {
if err := merger(depth+1, k, &asanarrayptr, element2[k]); err != nil {
return err
}
(*element1)[k] = asanarrayptr
} else {
asamapptr, ok := (*element1)[k].(map[string]interface{})
if ok {
if err := merger(depth+1, k, &asamapptr, element2[k]); err != nil {
return err
}
(*element1)[k] = asamapptr
} else {
return fmt.Errorf("config dest: %s does not make a valid map/array ptr", keyName)
}
}
}
}
case []map[string]interface{}:
//fmt.Println(strings.Repeat(" ", depth), " - An array")
element2, ok := cfg2.([]map[string]interface{})
if !ok {
return fmt.Errorf("config source: %s is not a map array", keyName)
}
//fmt.Println(strings.Repeat(" ", depth), " - element2: ", element2)
element1, ok := cfg1.(*[]map[string]interface{})
if !ok {
return fmt.Errorf("config dest: %s is not a map array", keyName)
}
//fmt.Println(strings.Repeat(" ", depth), " - element1: ", element1)
for index, _ := range element2 {
*element1 = append(*element1, element2[index])
}
case string:
//fmt.Println(strings.Repeat(" ", depth), " - A string")
element2, ok := cfg2.(string)
if !ok {
return fmt.Errorf("config: %s is not a string", keyName)
}
case bool:
//fmt.Println(strings.Repeat(" ", depth), " - A boolean")
element2, ok := cfg2.(bool)
if !ok {
return fmt.Errorf("config: %s is not a boolean value", keyName)
}
case float64:
//fmt.Println(strings.Repeat(" ", depth), " - A float64")
element2, ok := cfg2.(float64)
if !ok {
return fmt.Errorf("config: %s is not a float64 value", keyName)
}
case nil:
//fmt.Println(strings.Repeat(" ", depth), " - Nil")
default:
log.Info().Str("type", reflect.TypeOf(element2).String()).Msg("Unknown element type found in configuration file. Ignoring.")
}
return nil
}
err := merger(0, "TOP", config1, config2)
if err != nil {
return err
}
return nil
}
func handleArgs(cfg *config.Config, args map[string]interface{}) (*config.Config, error) {
// LDAP flags
if ldap, ok := args["--ldap"].(string); ok && ldap != "" {
cfg.LDAP.Enabled = true
cfg.LDAP.Listen = ldap
}
// LDAPS flags
if ldaps, ok := args["--ldaps"].(string); ok && ldaps != "" {
cfg.LDAPS.Enabled = true
cfg.LDAPS.Listen = ldaps
}
if ldapsCert, ok := args["--ldaps-cert"].(string); ok && ldapsCert != "" {
cfg.LDAPS.Cert = ldapsCert
}
if ldapsKey, ok := args["--ldaps-key"].(string); ok && ldapsKey != "" {
cfg.LDAPS.Key = ldapsKey
}
return cfg, nil
}
func handleLegacyConfig(cfg *config.Config) (*config.Config, error) {
if len(cfg.Frontend.Listen) > 0 && (len(cfg.LDAP.Listen) > 0 || len(cfg.LDAPS.Listen) > 0) {
// Both old server-config and new - dont allow
return cfg, fmt.Errorf("both old and new server-config in use - please remove old format ([frontend]) and migrate to new format ([ldap], [ldaps])")
}
if len(cfg.Frontend.Listen) > 0 {
// We're going with old format - parse it into new
log.Info().Msg("Config [frontend] is deprecated - please move to [ldap] and [ldaps] as-per documentation")
cfg.LDAP.Enabled = !cfg.Frontend.TLS
cfg.LDAPS.Enabled = cfg.Frontend.TLS
if cfg.Frontend.TLS {
cfg.LDAPS.Listen = cfg.Frontend.Listen
} else {
cfg.LDAP.Listen = cfg.Frontend.Listen
}
if len(cfg.Frontend.Cert) > 0 {
cfg.LDAPS.Cert = cfg.Frontend.Cert
}
if len(cfg.Frontend.Key) > 0 {
cfg.LDAPS.Key = cfg.Frontend.Key
}
}
return cfg, nil
}
func validateConfig(cfg *config.Config) (*config.Config, error) {
if !cfg.LDAP.Enabled && !cfg.LDAPS.Enabled {
return cfg, fmt.Errorf("no server configuration found: please provide either LDAP or LDAPS configuration")
}
if cfg.LDAPS.Enabled {
// LDAPS enabled - verify requirements (cert, key, listen)
if len(cfg.LDAPS.Cert) == 0 || len(cfg.LDAPS.Key) == 0 {
return cfg, fmt.Errorf("LDAPS was enabled but no certificate or key were specified: please disable LDAPS or use the 'cert' and 'key' options")
}
if len(cfg.LDAPS.Listen) == 0 {
return cfg, fmt.Errorf("no LDAPS bind address was specified: please disable LDAPS or use the 'listen' option")
}
}
if cfg.LDAP.Enabled {
// LDAP enabled - verify listen
if len(cfg.LDAP.Listen) == 0 {
return cfg, fmt.Errorf("no LDAP bind address was specified: please disable LDAP or use the 'listen' option")
}
if cfg.LDAP.TLS && cfg.LDAP.TLSCert == "" && cfg.LDAP.TLSCertPath != "" {
byteData, err := os.ReadFile(cfg.LDAP.TLSCertPath)
cfg.LDAP.TLSCert = string(byteData)
if err != nil {
return cfg, fmt.Errorf("unable to read TLS certificate file")
}
}
if cfg.LDAP.TLS && cfg.LDAP.TLSKey == "" && cfg.LDAP.TLSKeyPath != "" {
byteData, err := os.ReadFile(cfg.LDAP.TLSKeyPath)
cfg.LDAP.TLSKey = string(byteData)
if err != nil {
return cfg, fmt.Errorf("unable to read TLS key file")
}
}
}
//spew.Dump(cfg)
for i := range cfg.Backends {
switch cfg.Backends[i].Datastore {
case "":
cfg.Backends[i].Datastore = "config"
case "config":
case "ldap":
case "owncloud":
case "plugin":
default:
return cfg, fmt.Errorf("invalid backend %s - must be 'config', 'ldap', 'owncloud' or 'plugin'", cfg.Backends[i].Datastore)
}
}
// TODO: remove after deprecating UnixID on User and Group
for _, user := range cfg.Users {
if user.UnixID != 0 {
user.UIDNumber = user.UnixID
log.Info().Msg(fmt.Sprintf("User '%s': 'unixid' is deprecated - please move to 'uidnumber' as per documentation", user.Name))
}
}
for _, group := range cfg.Groups {
if group.UnixID != 0 {
group.GIDNumber = group.UnixID
log.Info().Msg(fmt.Sprintf("Group '%s': 'unixid' is deprecated - please move to 'gidnumber' as per documentation", group.Name))
}
}
return cfg, nil
}
+5
View File
@@ -0,0 +1,5 @@
package version
// Version is the tag of the latest release, it gets changed by the ci
// process when a release is happening
const Version = "v2.3.0" // x-release-please-version
+68
View File
@@ -0,0 +1,68 @@
package version
import (
"fmt"
"runtime/debug"
)
type gitInfo struct {
BuildTime string
Commit string
Dirty bool
}
// GetVersion reads builtime vars and returns a full string containing info about
// the currently running version of the software. Primarily used by the
// --version flag at runtime.
func GetVersion() string {
// TODO @shipperizer see if it's worth replacing with info.Main.Path
// aka github.com/glauth/glauth/v2 - not great but worth checking
name := "GLauth"
info, ok := debug.ReadBuildInfo()
if !ok {
return fmt.Sprintf(`
%s
Non-release build`,
name)
}
gitInfo := vcsInfo(info.Settings)
// If a release, use the tag
if !gitInfo.Dirty {
return fmt.Sprintf(`%s %s
Build time: %s
Commit: %s`, name, Version, gitInfo.BuildTime, gitInfo.Commit)
}
return fmt.Sprintf(`%s
Non-release build based on tag %s
Build time: %s
Commit: %s`, name, Version, gitInfo.BuildTime, gitInfo.Commit)
}
func vcsInfo(settings []debug.BuildSetting) *gitInfo {
info := new(gitInfo)
info.BuildTime = "unknown"
info.Commit = "unknown"
info.Dirty = false
for _, v := range settings {
switch v.Key {
case "vcs.revision":
info.Commit = v.Value
case "vcs.modified":
info.Dirty = v.Value == "true"
case "vcs.time":
info.BuildTime = v.Value
}
}
return info
}
+23
View File
@@ -0,0 +1,23 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 e5ffe9fd
dn: cn=config
objectClass: olcGlobal
cn: config
olcArgsFile: /var/run/slapd/slapd.args
olcLogLevel: none
olcPidFile: /var/run/slapd/slapd.pid
olcToolThreads: 1
structuralObjectClass: olcGlobal
entryUUID: 5c21e0e8-7ddc-103b-9ebb-c740e015b81b
creatorsName: cn=config
createTimestamp: 20210720192809Z
olcTLSCipherSuite: SECURE256:+SECURE128:-VERS-TLS-ALL:+VERS-TLS1.2:-RSA:-DHE
-DSS:-CAMELLIA-128-CBC:-CAMELLIA-256-CBC
olcTLSCACertificateFile: /container/service/slapd/assets/certs/ca.crt
olcTLSCertificateFile: /container/service/slapd/assets/certs/ldap.crt
olcTLSCertificateKeyFile: /container/service/slapd/assets/certs/ldap.key
olcTLSDHParamFile: /container/service/slapd/assets/certs/dhparam.pem
olcTLSVerifyClient: demand
entryCSN: 20210812002157.868917Z#000000#000#000000
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
modifyTimestamp: 20210812002157Z
@@ -0,0 +1,16 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 2aabc521
dn: cn=module{0}
objectClass: olcModuleList
cn: module{0}
olcModulePath: /usr/lib/ldap
olcModuleLoad: {0}back_mdb
olcModuleLoad: {1}memberof
olcModuleLoad: {2}refint
structuralObjectClass: olcModuleList
entryUUID: 5c2b1fb4-7ddc-103b-9ec3-c740e015b81b
creatorsName: cn=admin,cn=config
createTimestamp: 20210720192809Z
entryCSN: 20210720192809.856380Z#000000#000#000000
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
modifyTimestamp: 20210720192809Z
@@ -0,0 +1,12 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 bc37faae
dn: cn=schema
objectClass: olcSchemaConfig
cn: schema
structuralObjectClass: olcSchemaConfig
entryUUID: 5c2570a0-7ddc-103b-9ebe-c740e015b81b
creatorsName: cn=admin,cn=config
createTimestamp: 20210720192809Z
entryCSN: 20210720192809.316331Z#000000#000#000000
modifiersName: cn=admin,cn=config
modifyTimestamp: 20210720192809Z
@@ -0,0 +1,249 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 57d21f8c
dn: cn={0}core
objectClass: olcSchemaConfig
cn: {0}core
olcAttributeTypes: {0}( 2.5.4.2 NAME 'knowledgeInformation' DESC 'RFC2256: k
nowledge information' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.
121.1.15{32768} )
olcAttributeTypes: {1}( 2.5.4.4 NAME ( 'sn' 'surname' ) DESC 'RFC2256: last
(family) name(s) for which the entity is known by' SUP name )
olcAttributeTypes: {2}( 2.5.4.5 NAME 'serialNumber' DESC 'RFC2256: serial nu
mber of the entity' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMat
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44{64} )
olcAttributeTypes: {3}( 2.5.4.6 NAME ( 'c' 'countryName' ) DESC 'RFC4519: tw
o-letter ISO-3166 country code' SUP name SYNTAX 1.3.6.1.4.1.1466.115.121.1.
11 SINGLE-VALUE )
olcAttributeTypes: {4}( 2.5.4.7 NAME ( 'l' 'localityName' ) DESC 'RFC2256: l
ocality which this object resides in' SUP name )
olcAttributeTypes: {5}( 2.5.4.8 NAME ( 'st' 'stateOrProvinceName' ) DESC 'RF
C2256: state or province which this object resides in' SUP name )
olcAttributeTypes: {6}( 2.5.4.9 NAME ( 'street' 'streetAddress' ) DESC 'RFC2
256: street address of this object' EQUALITY caseIgnoreMatch SUBSTR caseIgn
oreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
olcAttributeTypes: {7}( 2.5.4.10 NAME ( 'o' 'organizationName' ) DESC 'RFC22
56: organization this object belongs to' SUP name )
olcAttributeTypes: {8}( 2.5.4.11 NAME ( 'ou' 'organizationalUnitName' ) DESC
'RFC2256: organizational unit this object belongs to' SUP name )
olcAttributeTypes: {9}( 2.5.4.12 NAME 'title' DESC 'RFC2256: title associate
d with the entity' SUP name )
olcAttributeTypes: {10}( 2.5.4.14 NAME 'searchGuide' DESC 'RFC2256: search g
uide, deprecated by enhancedSearchGuide' SYNTAX 1.3.6.1.4.1.1466.115.121.1.
25 )
olcAttributeTypes: {11}( 2.5.4.15 NAME 'businessCategory' DESC 'RFC2256: bus
iness category' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch S
YNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
olcAttributeTypes: {12}( 2.5.4.16 NAME 'postalAddress' DESC 'RFC2256: postal
address' EQUALITY caseIgnoreListMatch SUBSTR caseIgnoreListSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
olcAttributeTypes: {13}( 2.5.4.17 NAME 'postalCode' DESC 'RFC2256: postal co
de' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.
1.4.1.1466.115.121.1.15{40} )
olcAttributeTypes: {14}( 2.5.4.18 NAME 'postOfficeBox' DESC 'RFC2256: Post O
ffice Box' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX
1.3.6.1.4.1.1466.115.121.1.15{40} )
olcAttributeTypes: {15}( 2.5.4.19 NAME 'physicalDeliveryOfficeName' DESC 'RF
C2256: Physical Delivery Office Name' EQUALITY caseIgnoreMatch SUBSTR caseI
gnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
olcAttributeTypes: {16}( 2.5.4.20 NAME 'telephoneNumber' DESC 'RFC2256: Tele
phone Number' EQUALITY telephoneNumberMatch SUBSTR telephoneNumberSubstring
sMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.50{32} )
olcAttributeTypes: {17}( 2.5.4.21 NAME 'telexNumber' DESC 'RFC2256: Telex Nu
mber' SYNTAX 1.3.6.1.4.1.1466.115.121.1.52 )
olcAttributeTypes: {18}( 2.5.4.22 NAME 'teletexTerminalIdentifier' DESC 'RFC
2256: Teletex Terminal Identifier' SYNTAX 1.3.6.1.4.1.1466.115.121.1.51 )
olcAttributeTypes: {19}( 2.5.4.23 NAME ( 'facsimileTelephoneNumber' 'fax' )
DESC 'RFC2256: Facsimile (Fax) Telephone Number' SYNTAX 1.3.6.1.4.1.1466.11
5.121.1.22 )
olcAttributeTypes: {20}( 2.5.4.24 NAME 'x121Address' DESC 'RFC2256: X.121 Ad
dress' EQUALITY numericStringMatch SUBSTR numericStringSubstringsMatch SYNT
AX 1.3.6.1.4.1.1466.115.121.1.36{15} )
olcAttributeTypes: {21}( 2.5.4.25 NAME 'internationaliSDNNumber' DESC 'RFC22
56: international ISDN number' EQUALITY numericStringMatch SUBSTR numericSt
ringSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.36{16} )
olcAttributeTypes: {22}( 2.5.4.26 NAME 'registeredAddress' DESC 'RFC2256: re
gistered postal address' SUP postalAddress SYNTAX 1.3.6.1.4.1.1466.115.121.
1.41 )
olcAttributeTypes: {23}( 2.5.4.27 NAME 'destinationIndicator' DESC 'RFC2256:
destination indicator' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstring
sMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44{128} )
olcAttributeTypes: {24}( 2.5.4.28 NAME 'preferredDeliveryMethod' DESC 'RFC22
56: preferred delivery method' SYNTAX 1.3.6.1.4.1.1466.115.121.1.14 SINGLE-
VALUE )
olcAttributeTypes: {25}( 2.5.4.29 NAME 'presentationAddress' DESC 'RFC2256:
presentation address' EQUALITY presentationAddressMatch SYNTAX 1.3.6.1.4.1.
1466.115.121.1.43 SINGLE-VALUE )
olcAttributeTypes: {26}( 2.5.4.30 NAME 'supportedApplicationContext' DESC 'R
FC2256: supported application context' EQUALITY objectIdentifierMatch SYNTA
X 1.3.6.1.4.1.1466.115.121.1.38 )
olcAttributeTypes: {27}( 2.5.4.31 NAME 'member' DESC 'RFC2256: member of a g
roup' SUP distinguishedName )
olcAttributeTypes: {28}( 2.5.4.32 NAME 'owner' DESC 'RFC2256: owner (of the
object)' SUP distinguishedName )
olcAttributeTypes: {29}( 2.5.4.33 NAME 'roleOccupant' DESC 'RFC2256: occupan
t of role' SUP distinguishedName )
olcAttributeTypes: {30}( 2.5.4.36 NAME 'userCertificate' DESC 'RFC2256: X.50
9 user certificate, use ;binary' EQUALITY certificateExactMatch SYNTAX 1.3.
6.1.4.1.1466.115.121.1.8 )
olcAttributeTypes: {31}( 2.5.4.37 NAME 'cACertificate' DESC 'RFC2256: X.509
CA certificate, use ;binary' EQUALITY certificateExactMatch SYNTAX 1.3.6.1.
4.1.1466.115.121.1.8 )
olcAttributeTypes: {32}( 2.5.4.38 NAME 'authorityRevocationList' DESC 'RFC22
56: X.509 authority revocation list, use ;binary' SYNTAX 1.3.6.1.4.1.1466.1
15.121.1.9 )
olcAttributeTypes: {33}( 2.5.4.39 NAME 'certificateRevocationList' DESC 'RFC
2256: X.509 certificate revocation list, use ;binary' SYNTAX 1.3.6.1.4.1.14
66.115.121.1.9 )
olcAttributeTypes: {34}( 2.5.4.40 NAME 'crossCertificatePair' DESC 'RFC2256:
X.509 cross certificate pair, use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121
.1.10 )
olcAttributeTypes: {35}( 2.5.4.42 NAME ( 'givenName' 'gn' ) DESC 'RFC2256: f
irst name(s) for which the entity is known by' SUP name )
olcAttributeTypes: {36}( 2.5.4.43 NAME 'initials' DESC 'RFC2256: initials of
some or all of names, but not the surname(s).' SUP name )
olcAttributeTypes: {37}( 2.5.4.44 NAME 'generationQualifier' DESC 'RFC2256:
name qualifier indicating a generation' SUP name )
olcAttributeTypes: {38}( 2.5.4.45 NAME 'x500UniqueIdentifier' DESC 'RFC2256:
X.500 unique identifier' EQUALITY bitStringMatch SYNTAX 1.3.6.1.4.1.1466.1
15.121.1.6 )
olcAttributeTypes: {39}( 2.5.4.46 NAME 'dnQualifier' DESC 'RFC2256: DN quali
fier' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR case
IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
olcAttributeTypes: {40}( 2.5.4.47 NAME 'enhancedSearchGuide' DESC 'RFC2256:
enhanced search guide' SYNTAX 1.3.6.1.4.1.1466.115.121.1.21 )
olcAttributeTypes: {41}( 2.5.4.48 NAME 'protocolInformation' DESC 'RFC2256:
protocol information' EQUALITY protocolInformationMatch SYNTAX 1.3.6.1.4.1.
1466.115.121.1.42 )
olcAttributeTypes: {42}( 2.5.4.50 NAME 'uniqueMember' DESC 'RFC2256: unique
member of a group' EQUALITY uniqueMemberMatch SYNTAX 1.3.6.1.4.1.1466.115.1
21.1.34 )
olcAttributeTypes: {43}( 2.5.4.51 NAME 'houseIdentifier' DESC 'RFC2256: hous
e identifier' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYN
TAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
olcAttributeTypes: {44}( 2.5.4.52 NAME 'supportedAlgorithms' DESC 'RFC2256:
supported algorithms' SYNTAX 1.3.6.1.4.1.1466.115.121.1.49 )
olcAttributeTypes: {45}( 2.5.4.53 NAME 'deltaRevocationList' DESC 'RFC2256:
delta revocation list; use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 )
olcAttributeTypes: {46}( 2.5.4.54 NAME 'dmdName' DESC 'RFC2256: name of DMD'
SUP name )
olcAttributeTypes: {47}( 2.5.4.65 NAME 'pseudonym' DESC 'X.520(4th): pseudon
ym for the object' SUP name )
olcAttributeTypes: {48}( 0.9.2342.19200300.100.1.3 NAME ( 'mail' 'rfc822Mail
box' ) DESC 'RFC1274: RFC822 Mailbox' EQUALITY caseIgnoreIA5Match SUBST
R caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256}
)
olcAttributeTypes: {49}( 0.9.2342.19200300.100.1.25 NAME ( 'dc' 'domainCompo
nent' ) DESC 'RFC1274/2247: domain component' EQUALITY caseIgnoreIA5Match S
UBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SIN
GLE-VALUE )
olcAttributeTypes: {50}( 0.9.2342.19200300.100.1.37 NAME 'associatedDomain'
DESC 'RFC1274: domain associated with object' EQUALITY caseIgnoreIA5Match S
UBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {51}( 1.2.840.113549.1.9.1 NAME ( 'email' 'emailAddress'
'pkcs9email' ) DESC 'RFC3280: legacy attribute for email addresses in DNs'
EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.
6.1.4.1.1466.115.121.1.26{128} )
olcObjectClasses: {0}( 2.5.6.2 NAME 'country' DESC 'RFC2256: a country' SUP
top STRUCTURAL MUST c MAY ( searchGuide $ description ) )
olcObjectClasses: {1}( 2.5.6.3 NAME 'locality' DESC 'RFC2256: a locality' SU
P top STRUCTURAL MAY ( street $ seeAlso $ searchGuide $ st $ l $ descriptio
n ) )
olcObjectClasses: {2}( 2.5.6.4 NAME 'organization' DESC 'RFC2256: an organiz
ation' SUP top STRUCTURAL MUST o MAY ( userPassword $ searchGuide $ seeAlso
$ businessCategory $ x121Address $ registeredAddress $ destinationIndicato
r $ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ tel
ephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $ street
$ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName
$ st $ l $ description ) )
olcObjectClasses: {3}( 2.5.6.5 NAME 'organizationalUnit' DESC 'RFC2256: an o
rganizational unit' SUP top STRUCTURAL MUST ou MAY ( userPassword $ searchG
uide $ seeAlso $ businessCategory $ x121Address $ registeredAddress $ desti
nationIndicator $ preferredDeliveryMethod $ telexNumber $ teletexTerminalId
entifier $ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNu
mber $ street $ postOfficeBox $ postalCode $ postalAddress $ physicalDelive
ryOfficeName $ st $ l $ description ) )
olcObjectClasses: {4}( 2.5.6.6 NAME 'person' DESC 'RFC2256: a person' SUP to
p STRUCTURAL MUST ( sn $ cn ) MAY ( userPassword $ telephoneNumber $ seeAls
o $ description ) )
olcObjectClasses: {5}( 2.5.6.7 NAME 'organizationalPerson' DESC 'RFC2256: an
organizational person' SUP person STRUCTURAL MAY ( title $ x121Address $ r
egisteredAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNu
mber $ teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumbe
r $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $ posta
lAddress $ physicalDeliveryOfficeName $ ou $ st $ l ) )
olcObjectClasses: {6}( 2.5.6.8 NAME 'organizationalRole' DESC 'RFC2256: an o
rganizational role' SUP top STRUCTURAL MUST cn MAY ( x121Address $ register
edAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNumber $
teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ fac
simileTelephoneNumber $ seeAlso $ roleOccupant $ preferredDeliveryMethod $
street $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOffic
eName $ ou $ st $ l $ description ) )
olcObjectClasses: {7}( 2.5.6.9 NAME 'groupOfNames' DESC 'RFC2256: a group of
names (DNs)' SUP top STRUCTURAL MUST ( member $ cn ) MAY ( businessCategor
y $ seeAlso $ owner $ ou $ o $ description ) )
olcObjectClasses: {8}( 2.5.6.10 NAME 'residentialPerson' DESC 'RFC2256: an r
esidential person' SUP person STRUCTURAL MUST l MAY ( businessCategory $ x1
21Address $ registeredAddress $ destinationIndicator $ preferredDeliveryMet
hod $ telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internati
onaliSDNNumber $ facsimileTelephoneNumber $ preferredDeliveryMethod $ stree
t $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName
$ st $ l ) )
olcObjectClasses: {9}( 2.5.6.11 NAME 'applicationProcess' DESC 'RFC2256: an
application process' SUP top STRUCTURAL MUST cn MAY ( seeAlso $ ou $ l $ de
scription ) )
olcObjectClasses: {10}( 2.5.6.12 NAME 'applicationEntity' DESC 'RFC2256: an
application entity' SUP top STRUCTURAL MUST ( presentationAddress $ cn ) MA
Y ( supportedApplicationContext $ seeAlso $ ou $ o $ l $ description ) )
olcObjectClasses: {11}( 2.5.6.13 NAME 'dSA' DESC 'RFC2256: a directory syste
m agent (a server)' SUP applicationEntity STRUCTURAL MAY knowledgeInformati
on )
olcObjectClasses: {12}( 2.5.6.14 NAME 'device' DESC 'RFC2256: a device' SUP
top STRUCTURAL MUST cn MAY ( serialNumber $ seeAlso $ owner $ ou $ o $ l $
description ) )
olcObjectClasses: {13}( 2.5.6.15 NAME 'strongAuthenticationUser' DESC 'RFC22
56: a strong authentication user' SUP top AUXILIARY MUST userCertificate )
olcObjectClasses: {14}( 2.5.6.16 NAME 'certificationAuthority' DESC 'RFC2256
: a certificate authority' SUP top AUXILIARY MUST ( authorityRevocationList
$ certificateRevocationList $ cACertificate ) MAY crossCertificatePair )
olcObjectClasses: {15}( 2.5.6.17 NAME 'groupOfUniqueNames' DESC 'RFC2256: a
group of unique names (DN and Unique Identifier)' SUP top STRUCTURAL MUST (
uniqueMember $ cn ) MAY ( businessCategory $ seeAlso $ owner $ ou $ o $ de
scription ) )
olcObjectClasses: {16}( 2.5.6.18 NAME 'userSecurityInformation' DESC 'RFC225
6: a user security information' SUP top AUXILIARY MAY ( supportedAlgorithms
) )
olcObjectClasses: {17}( 2.5.6.16.2 NAME 'certificationAuthority-V2' SUP cert
ificationAuthority AUXILIARY MAY ( deltaRevocationList ) )
olcObjectClasses: {18}( 2.5.6.19 NAME 'cRLDistributionPoint' SUP top STRUCTU
RAL MUST ( cn ) MAY ( certificateRevocationList $ authorityRevocationList $
deltaRevocationList ) )
olcObjectClasses: {19}( 2.5.6.20 NAME 'dmd' SUP top STRUCTURAL MUST ( dmdNam
e ) MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $ x121Add
ress $ registeredAddress $ destinationIndicator $ preferredDeliveryMethod $
telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internationali
SDNNumber $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode
$ postalAddress $ physicalDeliveryOfficeName $ st $ l $ description ) )
olcObjectClasses: {20}( 2.5.6.21 NAME 'pkiUser' DESC 'RFC2587: a PKI user' S
UP top AUXILIARY MAY userCertificate )
olcObjectClasses: {21}( 2.5.6.22 NAME 'pkiCA' DESC 'RFC2587: PKI certificate
authority' SUP top AUXILIARY MAY ( authorityRevocationList $ certificateRe
vocationList $ cACertificate $ crossCertificatePair ) )
olcObjectClasses: {22}( 2.5.6.23 NAME 'deltaCRL' DESC 'RFC2587: PKI user' SU
P top AUXILIARY MAY deltaRevocationList )
olcObjectClasses: {23}( 1.3.6.1.4.1.250.3.15 NAME 'labeledURIObject' DESC 'R
FC2079: object that contains the URI attribute type' MAY ( labeledURI ) SUP
top AUXILIARY )
olcObjectClasses: {24}( 0.9.2342.19200300.100.4.19 NAME 'simpleSecurityObjec
t' DESC 'RFC1274: simple security object' SUP top AUXILIARY MUST userPasswo
rd )
olcObjectClasses: {25}( 1.3.6.1.4.1.1466.344 NAME 'dcObject' DESC 'RFC2247:
domain component object' SUP top AUXILIARY MUST dc )
olcObjectClasses: {26}( 1.3.6.1.1.3.1 NAME 'uidObject' DESC 'RFC2377: uid ob
ject' SUP top AUXILIARY MUST uid )
structuralObjectClass: olcSchemaConfig
entryUUID: 5c26a290-7ddc-103b-9ebf-c740e015b81b
creatorsName: cn=admin,cn=config
createTimestamp: 20210720192809Z
entryCSN: 20210720192809.324162Z#000000#000#000000
modifiersName: cn=admin,cn=config
modifyTimestamp: 20210720192809Z
@@ -0,0 +1,178 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 3133677b
dn: cn={1}cosine
objectClass: olcSchemaConfig
cn: {1}cosine
olcAttributeTypes: {0}( 0.9.2342.19200300.100.1.2 NAME 'textEncodedORAddress
' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.
4.1.1466.115.121.1.15{256} )
olcAttributeTypes: {1}( 0.9.2342.19200300.100.1.4 NAME 'info' DESC 'RFC1274:
general information' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsM
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{2048} )
olcAttributeTypes: {2}( 0.9.2342.19200300.100.1.5 NAME ( 'drink' 'favouriteD
rink' ) DESC 'RFC1274: favorite drink' EQUALITY caseIgnoreMatch SUBSTR case
IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
olcAttributeTypes: {3}( 0.9.2342.19200300.100.1.6 NAME 'roomNumber' DESC 'RF
C1274: room number' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMat
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
olcAttributeTypes: {4}( 0.9.2342.19200300.100.1.7 NAME 'photo' DESC 'RFC1274
: photo (G3 fax)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.23{25000} )
olcAttributeTypes: {5}( 0.9.2342.19200300.100.1.8 NAME 'userClass' DESC 'RFC
1274: category of user' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstring
sMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
olcAttributeTypes: {6}( 0.9.2342.19200300.100.1.9 NAME 'host' DESC 'RFC1274:
host computer' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch S
YNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
olcAttributeTypes: {7}( 0.9.2342.19200300.100.1.10 NAME 'manager' DESC 'RFC1
274: DN of manager' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466
.115.121.1.12 )
olcAttributeTypes: {8}( 0.9.2342.19200300.100.1.11 NAME 'documentIdentifier'
DESC 'RFC1274: unique identifier of document' EQUALITY caseIgnoreMatch SUB
STR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
olcAttributeTypes: {9}( 0.9.2342.19200300.100.1.12 NAME 'documentTitle' DESC
'RFC1274: title of document' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSub
stringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
olcAttributeTypes: {10}( 0.9.2342.19200300.100.1.13 NAME 'documentVersion' D
ESC 'RFC1274: version of document' EQUALITY caseIgnoreMatch SUBSTR caseIgno
reSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
olcAttributeTypes: {11}( 0.9.2342.19200300.100.1.14 NAME 'documentAuthor' DE
SC 'RFC1274: DN of author of document' EQUALITY distinguishedNameMatch SYNT
AX 1.3.6.1.4.1.1466.115.121.1.12 )
olcAttributeTypes: {12}( 0.9.2342.19200300.100.1.15 NAME 'documentLocation'
DESC 'RFC1274: location of document original' EQUALITY caseIgnoreMatch SUBS
TR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
olcAttributeTypes: {13}( 0.9.2342.19200300.100.1.20 NAME ( 'homePhone' 'home
TelephoneNumber' ) DESC 'RFC1274: home telephone number' EQUALITY telephone
NumberMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.1
15.121.1.50 )
olcAttributeTypes: {14}( 0.9.2342.19200300.100.1.21 NAME 'secretary' DESC 'R
FC1274: DN of secretary' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1
.1466.115.121.1.12 )
olcAttributeTypes: {15}( 0.9.2342.19200300.100.1.22 NAME 'otherMailbox' SYNT
AX 1.3.6.1.4.1.1466.115.121.1.39 )
olcAttributeTypes: {16}( 0.9.2342.19200300.100.1.26 NAME 'aRecord' EQUALITY
caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {17}( 0.9.2342.19200300.100.1.27 NAME 'mDRecord' EQUALITY
caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {18}( 0.9.2342.19200300.100.1.28 NAME 'mXRecord' EQUALITY
caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {19}( 0.9.2342.19200300.100.1.29 NAME 'nSRecord' EQUALITY
caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {20}( 0.9.2342.19200300.100.1.30 NAME 'sOARecord' EQUALIT
Y caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {21}( 0.9.2342.19200300.100.1.31 NAME 'cNAMERecord' EQUAL
ITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {22}( 0.9.2342.19200300.100.1.38 NAME 'associatedName' DE
SC 'RFC1274: DN of entry associated with domain' EQUALITY distinguishedName
Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
olcAttributeTypes: {23}( 0.9.2342.19200300.100.1.39 NAME 'homePostalAddress'
DESC 'RFC1274: home postal address' EQUALITY caseIgnoreListMatch SUBSTR ca
seIgnoreListSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
olcAttributeTypes: {24}( 0.9.2342.19200300.100.1.40 NAME 'personalTitle' DES
C 'RFC1274: personal title' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubst
ringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
olcAttributeTypes: {25}( 0.9.2342.19200300.100.1.41 NAME ( 'mobile' 'mobileT
elephoneNumber' ) DESC 'RFC1274: mobile telephone number' EQUALITY telephon
eNumberMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.
115.121.1.50 )
olcAttributeTypes: {26}( 0.9.2342.19200300.100.1.42 NAME ( 'pager' 'pagerTel
ephoneNumber' ) DESC 'RFC1274: pager telephone number' EQUALITY telephoneNu
mberMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115
.121.1.50 )
olcAttributeTypes: {27}( 0.9.2342.19200300.100.1.43 NAME ( 'co' 'friendlyCou
ntryName' ) DESC 'RFC1274: friendly country name' EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
olcAttributeTypes: {28}( 0.9.2342.19200300.100.1.44 NAME 'uniqueIdentifier'
DESC 'RFC1274: unique identifer' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.
1.1466.115.121.1.15{256} )
olcAttributeTypes: {29}( 0.9.2342.19200300.100.1.45 NAME 'organizationalStat
us' DESC 'RFC1274: organizational status' EQUALITY caseIgnoreMatch SUBSTR c
aseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
olcAttributeTypes: {30}( 0.9.2342.19200300.100.1.46 NAME 'janetMailbox' DESC
'RFC1274: Janet mailbox' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5S
ubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
olcAttributeTypes: {31}( 0.9.2342.19200300.100.1.47 NAME 'mailPreferenceOpti
on' DESC 'RFC1274: mail preference option' SYNTAX 1.3.6.1.4.1.1466.115.121.
1.27 )
olcAttributeTypes: {32}( 0.9.2342.19200300.100.1.48 NAME 'buildingName' DESC
'RFC1274: name of building' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubs
tringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
olcAttributeTypes: {33}( 0.9.2342.19200300.100.1.49 NAME 'dSAQuality' DESC '
RFC1274: DSA Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.19 SINGLE-VALUE )
olcAttributeTypes: {34}( 0.9.2342.19200300.100.1.50 NAME 'singleLevelQuality
' DESC 'RFC1274: Single Level Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.13
SINGLE-VALUE )
olcAttributeTypes: {35}( 0.9.2342.19200300.100.1.51 NAME 'subtreeMinimumQual
ity' DESC 'RFC1274: Subtree Mininum Quality' SYNTAX 1.3.6.1.4.1.1466.115.12
1.1.13 SINGLE-VALUE )
olcAttributeTypes: {36}( 0.9.2342.19200300.100.1.52 NAME 'subtreeMaximumQual
ity' DESC 'RFC1274: Subtree Maximun Quality' SYNTAX 1.3.6.1.4.1.1466.115.12
1.1.13 SINGLE-VALUE )
olcAttributeTypes: {37}( 0.9.2342.19200300.100.1.53 NAME 'personalSignature'
DESC 'RFC1274: Personal Signature (G3 fax)' SYNTAX 1.3.6.1.4.1.1466.115.12
1.1.23 )
olcAttributeTypes: {38}( 0.9.2342.19200300.100.1.54 NAME 'dITRedirect' DESC
'RFC1274: DIT Redirect' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.
1466.115.121.1.12 )
olcAttributeTypes: {39}( 0.9.2342.19200300.100.1.55 NAME 'audio' DESC 'RFC12
74: audio (u-law)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.4{25000} )
olcAttributeTypes: {40}( 0.9.2342.19200300.100.1.56 NAME 'documentPublisher'
DESC 'RFC1274: publisher of document' EQUALITY caseIgnoreMatch SUBSTR case
IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
olcObjectClasses: {0}( 0.9.2342.19200300.100.4.4 NAME ( 'pilotPerson' 'newPi
lotPerson' ) SUP person STRUCTURAL MAY ( userid $ textEncodedORAddress $ rf
c822Mailbox $ favouriteDrink $ roomNumber $ userClass $ homeTelephoneNumber
$ homePostalAddress $ secretary $ personalTitle $ preferredDeliveryMethod
$ businessCategory $ janetMailbox $ otherMailbox $ mobileTelephoneNumber $
pagerTelephoneNumber $ organizationalStatus $ mailPreferenceOption $ person
alSignature ) )
olcObjectClasses: {1}( 0.9.2342.19200300.100.4.5 NAME 'account' SUP top STRU
CTURAL MUST userid MAY ( description $ seeAlso $ localityName $ organizatio
nName $ organizationalUnitName $ host ) )
olcObjectClasses: {2}( 0.9.2342.19200300.100.4.6 NAME 'document' SUP top STR
UCTURAL MUST documentIdentifier MAY ( commonName $ description $ seeAlso $
localityName $ organizationName $ organizationalUnitName $ documentTitle $
documentVersion $ documentAuthor $ documentLocation $ documentPublisher ) )
olcObjectClasses: {3}( 0.9.2342.19200300.100.4.7 NAME 'room' SUP top STRUCTU
RAL MUST commonName MAY ( roomNumber $ description $ seeAlso $ telephoneNum
ber ) )
olcObjectClasses: {4}( 0.9.2342.19200300.100.4.9 NAME 'documentSeries' SUP t
op STRUCTURAL MUST commonName MAY ( description $ seeAlso $ telephonenumber
$ localityName $ organizationName $ organizationalUnitName ) )
olcObjectClasses: {5}( 0.9.2342.19200300.100.4.13 NAME 'domain' SUP top STRU
CTURAL MUST domainComponent MAY ( associatedName $ organizationName $ descr
iption $ businessCategory $ seeAlso $ searchGuide $ userPassword $ locality
Name $ stateOrProvinceName $ streetAddress $ physicalDeliveryOfficeName $ p
ostalAddress $ postalCode $ postOfficeBox $ streetAddress $ facsimileTeleph
oneNumber $ internationalISDNNumber $ telephoneNumber $ teletexTerminalIden
tifier $ telexNumber $ preferredDeliveryMethod $ destinationIndicator $ reg
isteredAddress $ x121Address ) )
olcObjectClasses: {6}( 0.9.2342.19200300.100.4.14 NAME 'RFC822localPart' SUP
domain STRUCTURAL MAY ( commonName $ surname $ description $ seeAlso $ tel
ephoneNumber $ physicalDeliveryOfficeName $ postalAddress $ postalCode $ po
stOfficeBox $ streetAddress $ facsimileTelephoneNumber $ internationalISDNN
umber $ telephoneNumber $ teletexTerminalIdentifier $ telexNumber $ preferr
edDeliveryMethod $ destinationIndicator $ registeredAddress $ x121Address )
)
olcObjectClasses: {7}( 0.9.2342.19200300.100.4.15 NAME 'dNSDomain' SUP domai
n STRUCTURAL MAY ( ARecord $ MDRecord $ MXRecord $ NSRecord $ SOARecord $ C
NAMERecord ) )
olcObjectClasses: {8}( 0.9.2342.19200300.100.4.17 NAME 'domainRelatedObject'
DESC 'RFC1274: an object related to an domain' SUP top AUXILIARY MUST asso
ciatedDomain )
olcObjectClasses: {9}( 0.9.2342.19200300.100.4.18 NAME 'friendlyCountry' SUP
country STRUCTURAL MUST friendlyCountryName )
olcObjectClasses: {10}( 0.9.2342.19200300.100.4.20 NAME 'pilotOrganization'
SUP ( organization $ organizationalUnit ) STRUCTURAL MAY buildingName )
olcObjectClasses: {11}( 0.9.2342.19200300.100.4.21 NAME 'pilotDSA' SUP dsa S
TRUCTURAL MAY dSAQuality )
olcObjectClasses: {12}( 0.9.2342.19200300.100.4.22 NAME 'qualityLabelledData
' SUP top AUXILIARY MUST dsaQuality MAY ( subtreeMinimumQuality $ subtreeMa
ximumQuality ) )
structuralObjectClass: olcSchemaConfig
entryUUID: 5c286ac6-7ddc-103b-9ec0-c740e015b81b
creatorsName: cn=admin,cn=config
createTimestamp: 20210720192809Z
entryCSN: 20210720192809.335841Z#000000#000#000000
modifiersName: cn=admin,cn=config
modifyTimestamp: 20210720192809Z
@@ -0,0 +1,108 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 8c7d43da
dn: cn={2}nis
objectClass: olcSchemaConfig
cn: {2}nis
olcAttributeTypes: {0}( 1.3.6.1.1.1.1.2 NAME 'gecos' DESC 'The GECOS field;
the common name' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5Substrings
Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {1}( 1.3.6.1.1.1.1.3 NAME 'homeDirectory' DESC 'The absol
ute path to the home directory' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4
.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {2}( 1.3.6.1.1.1.1.4 NAME 'loginShell' DESC 'The path to
the login shell' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121
.1.26 SINGLE-VALUE )
olcAttributeTypes: {3}( 1.3.6.1.1.1.1.5 NAME 'shadowLastChange' EQUALITY int
egerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {4}( 1.3.6.1.1.1.1.6 NAME 'shadowMin' EQUALITY integerMat
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {5}( 1.3.6.1.1.1.1.7 NAME 'shadowMax' EQUALITY integerMat
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {6}( 1.3.6.1.1.1.1.8 NAME 'shadowWarning' EQUALITY intege
rMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {7}( 1.3.6.1.1.1.1.9 NAME 'shadowInactive' EQUALITY integ
erMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {8}( 1.3.6.1.1.1.1.10 NAME 'shadowExpire' EQUALITY intege
rMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {9}( 1.3.6.1.1.1.1.11 NAME 'shadowFlag' EQUALITY integerM
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {10}( 1.3.6.1.1.1.1.12 NAME 'memberUid' EQUALITY caseExac
tIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.12
1.1.26 )
olcAttributeTypes: {11}( 1.3.6.1.1.1.1.13 NAME 'memberNisNetgroup' EQUALITY
caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.146
6.115.121.1.26 )
olcAttributeTypes: {12}( 1.3.6.1.1.1.1.14 NAME 'nisNetgroupTriple' DESC 'Net
group triple' SYNTAX 1.3.6.1.1.1.0.0 )
olcAttributeTypes: {13}( 1.3.6.1.1.1.1.15 NAME 'ipServicePort' EQUALITY inte
gerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {14}( 1.3.6.1.1.1.1.16 NAME 'ipServiceProtocol' SUP name
)
olcAttributeTypes: {15}( 1.3.6.1.1.1.1.17 NAME 'ipProtocolNumber' EQUALITY i
ntegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {16}( 1.3.6.1.1.1.1.18 NAME 'oncRpcNumber' EQUALITY integ
erMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {17}( 1.3.6.1.1.1.1.19 NAME 'ipHostNumber' DESC 'IP addre
ss' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
olcAttributeTypes: {18}( 1.3.6.1.1.1.1.20 NAME 'ipNetworkNumber' DESC 'IP ne
twork' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128
} SINGLE-VALUE )
olcAttributeTypes: {19}( 1.3.6.1.1.1.1.21 NAME 'ipNetmaskNumber' DESC 'IP ne
tmask' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128
} SINGLE-VALUE )
olcAttributeTypes: {20}( 1.3.6.1.1.1.1.22 NAME 'macAddress' DESC 'MAC addres
s' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
olcAttributeTypes: {21}( 1.3.6.1.1.1.1.23 NAME 'bootParameter' DESC 'rpc.boo
tparamd parameter' SYNTAX 1.3.6.1.1.1.0.1 )
olcAttributeTypes: {22}( 1.3.6.1.1.1.1.24 NAME 'bootFile' DESC 'Boot image n
ame' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {23}( 1.3.6.1.1.1.1.26 NAME 'nisMapName' SUP name )
olcAttributeTypes: {24}( 1.3.6.1.1.1.1.27 NAME 'nisMapEntry' EQUALITY caseEx
actIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.
121.1.26{1024} SINGLE-VALUE )
olcObjectClasses: {0}( 1.3.6.1.1.1.2.0 NAME 'posixAccount' DESC 'Abstraction
of an account with POSIX attributes' SUP top AUXILIARY MUST ( cn $ uid $ u
idNumber $ gidNumber $ homeDirectory ) MAY ( userPassword $ loginShell $ ge
cos $ description ) )
olcObjectClasses: {1}( 1.3.6.1.1.1.2.1 NAME 'shadowAccount' DESC 'Additional
attributes for shadow passwords' SUP top AUXILIARY MUST uid MAY ( userPass
word $ shadowLastChange $ shadowMin $ shadowMax $ shadowWarning $ shadowIna
ctive $ shadowExpire $ shadowFlag $ description ) )
olcObjectClasses: {2}( 1.3.6.1.1.1.2.2 NAME 'posixGroup' DESC 'Abstraction o
f a group of accounts' SUP top STRUCTURAL MUST ( cn $ gidNumber ) MAY ( use
rPassword $ memberUid $ description ) )
olcObjectClasses: {3}( 1.3.6.1.1.1.2.3 NAME 'ipService' DESC 'Abstraction an
Internet Protocol service' SUP top STRUCTURAL MUST ( cn $ ipServicePort $
ipServiceProtocol ) MAY description )
olcObjectClasses: {4}( 1.3.6.1.1.1.2.4 NAME 'ipProtocol' DESC 'Abstraction o
f an IP protocol' SUP top STRUCTURAL MUST ( cn $ ipProtocolNumber $ descrip
tion ) MAY description )
olcObjectClasses: {5}( 1.3.6.1.1.1.2.5 NAME 'oncRpc' DESC 'Abstraction of an
ONC/RPC binding' SUP top STRUCTURAL MUST ( cn $ oncRpcNumber $ description
) MAY description )
olcObjectClasses: {6}( 1.3.6.1.1.1.2.6 NAME 'ipHost' DESC 'Abstraction of a
host, an IP device' SUP top AUXILIARY MUST ( cn $ ipHostNumber ) MAY ( l $
description $ manager ) )
olcObjectClasses: {7}( 1.3.6.1.1.1.2.7 NAME 'ipNetwork' DESC 'Abstraction of
an IP network' SUP top STRUCTURAL MUST ( cn $ ipNetworkNumber ) MAY ( ipNe
tmaskNumber $ l $ description $ manager ) )
olcObjectClasses: {8}( 1.3.6.1.1.1.2.8 NAME 'nisNetgroup' DESC 'Abstraction
of a netgroup' SUP top STRUCTURAL MUST cn MAY ( nisNetgroupTriple $ memberN
isNetgroup $ description ) )
olcObjectClasses: {9}( 1.3.6.1.1.1.2.9 NAME 'nisMap' DESC 'A generic abstrac
tion of a NIS map' SUP top STRUCTURAL MUST nisMapName MAY description )
olcObjectClasses: {10}( 1.3.6.1.1.1.2.10 NAME 'nisObject' DESC 'An entry in
a NIS map' SUP top STRUCTURAL MUST ( cn $ nisMapEntry $ nisMapName ) MAY de
scription )
olcObjectClasses: {11}( 1.3.6.1.1.1.2.11 NAME 'ieee802Device' DESC 'A device
with a MAC address' SUP top AUXILIARY MAY macAddress )
olcObjectClasses: {12}( 1.3.6.1.1.1.2.12 NAME 'bootableDevice' DESC 'A devic
e with boot parameters' SUP top AUXILIARY MAY ( bootFile $ bootParameter )
)
structuralObjectClass: olcSchemaConfig
entryUUID: 5c2966ce-7ddc-103b-9ec1-c740e015b81b
creatorsName: cn=admin,cn=config
createTimestamp: 20210720192809Z
entryCSN: 20210720192809.342294Z#000000#000#000000
modifiersName: cn=admin,cn=config
modifyTimestamp: 20210720192809Z
@@ -0,0 +1,49 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 b409a0c2
dn: cn={3}inetorgperson
objectClass: olcSchemaConfig
cn: {3}inetorgperson
olcAttributeTypes: {0}( 2.16.840.1.113730.3.1.1 NAME 'carLicense' DESC 'RFC2
798: vehicle license or registration plate' EQUALITY caseIgnoreMatch SUBSTR
caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
olcAttributeTypes: {1}( 2.16.840.1.113730.3.1.2 NAME 'departmentNumber' DESC
'RFC2798: identifies a department within an organization' EQUALITY caseIgn
oreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1
.15 )
olcAttributeTypes: {2}( 2.16.840.1.113730.3.1.241 NAME 'displayName' DESC 'R
FC2798: preferred name to be used when displaying entries' EQUALITY caseIgn
oreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1
.15 SINGLE-VALUE )
olcAttributeTypes: {3}( 2.16.840.1.113730.3.1.3 NAME 'employeeNumber' DESC '
RFC2798: numerically identifies an employee within an organization' EQUALIT
Y caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.
115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {4}( 2.16.840.1.113730.3.1.4 NAME 'employeeType' DESC 'RF
C2798: type of employment for a person' EQUALITY caseIgnoreMatch SUBSTR cas
eIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
olcAttributeTypes: {5}( 0.9.2342.19200300.100.1.60 NAME 'jpegPhoto' DESC 'RF
C2798: a JPEG image' SYNTAX 1.3.6.1.4.1.1466.115.121.1.28 )
olcAttributeTypes: {6}( 2.16.840.1.113730.3.1.39 NAME 'preferredLanguage' DE
SC 'RFC2798: preferred written or spoken language for a person' EQUALITY ca
seIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.
121.1.15 SINGLE-VALUE )
olcAttributeTypes: {7}( 2.16.840.1.113730.3.1.40 NAME 'userSMIMECertificate'
DESC 'RFC2798: PKCS#7 SignedData used to support S/MIME' SYNTAX 1.3.6.1.4.
1.1466.115.121.1.5 )
olcAttributeTypes: {8}( 2.16.840.1.113730.3.1.216 NAME 'userPKCS12' DESC 'RF
C2798: personal identity information, a PKCS #12 PFX' SYNTAX 1.3.6.1.4.1.14
66.115.121.1.5 )
olcObjectClasses: {0}( 2.16.840.1.113730.3.2.2 NAME 'inetOrgPerson' DESC 'RF
C2798: Internet Organizational Person' SUP organizationalPerson STRUCTURAL
MAY ( audio $ businessCategory $ carLicense $ departmentNumber $ displayNam
e $ employeeNumber $ employeeType $ givenName $ homePhone $ homePostalAddre
ss $ initials $ jpegPhoto $ labeledURI $ mail $ manager $ mobile $ o $ page
r $ photo $ roomNumber $ secretary $ uid $ userCertificate $ x500uniqueIden
tifier $ preferredLanguage $ userSMIMECertificate $ userPKCS12 ) )
structuralObjectClass: olcSchemaConfig
entryUUID: 5c2a5016-7ddc-103b-9ec2-c740e015b81b
creatorsName: cn=admin,cn=config
createTimestamp: 20210720192809Z
entryCSN: 20210720192809.348266Z#000000#000#000000
modifiersName: cn=admin,cn=config
modifyTimestamp: 20210720192809Z
@@ -0,0 +1,65 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 cded21ef
dn: cn={4}ppolicy
objectClass: olcSchemaConfig
cn: {4}ppolicy
olcAttributeTypes: {0}( 1.3.6.1.4.1.42.2.27.8.1.1 NAME 'pwdAttribute' EQUALI
TY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
olcAttributeTypes: {1}( 1.3.6.1.4.1.42.2.27.8.1.2 NAME 'pwdMinAge' EQUALITY
integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.
1.27 SINGLE-VALUE )
olcAttributeTypes: {2}( 1.3.6.1.4.1.42.2.27.8.1.3 NAME 'pwdMaxAge' EQUALITY
integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.
1.27 SINGLE-VALUE )
olcAttributeTypes: {3}( 1.3.6.1.4.1.42.2.27.8.1.4 NAME 'pwdInHistory' EQUALI
TY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.1
21.1.27 SINGLE-VALUE )
olcAttributeTypes: {4}( 1.3.6.1.4.1.42.2.27.8.1.5 NAME 'pwdCheckQuality' EQU
ALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.11
5.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {5}( 1.3.6.1.4.1.42.2.27.8.1.6 NAME 'pwdMinLength' EQUALI
TY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.
121.1.27 SINGLE-VALUE )
olcAttributeTypes: {6}( 1.3.6.1.4.1.42.2.27.8.1.7 NAME 'pwdExpireWarning' EQ
UALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.
115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {7}( 1.3.6.1.4.1.42.2.27.8.1.8 NAME 'pwdGraceAuthNLimit'
EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.146
6.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {8}( 1.3.6.1.4.1.42.2.27.8.1.9 NAME 'pwdLockout' EQUALITY
booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
olcAttributeTypes: {9}( 1.3.6.1.4.1.42.2.27.8.1.10 NAME 'pwdLockoutDuration'
EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.14
66.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {10}( 1.3.6.1.4.1.42.2.27.8.1.11 NAME 'pwdMaxFailure' EQU
ALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.1
15.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {11}( 1.3.6.1.4.1.42.2.27.8.1.12 NAME 'pwdFailureCountInt
erval' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.
4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {12}( 1.3.6.1.4.1.42.2.27.8.1.13 NAME 'pwdMustChange' EQU
ALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
olcAttributeTypes: {13}( 1.3.6.1.4.1.42.2.27.8.1.14 NAME 'pwdAllowUserChange
' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
olcAttributeTypes: {14}( 1.3.6.1.4.1.42.2.27.8.1.15 NAME 'pwdSafeModify' EQU
ALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
olcAttributeTypes: {15}( 1.3.6.1.4.1.4754.1.99.1 NAME 'pwdCheckModule' DESC
'Loadable module that instantiates "check_password() function' EQUALITY cas
eExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {16}( 1.3.6.1.4.1.42.2.27.8.1.30 NAME 'pwdMaxRecordedFail
ure' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.
1.1466.115.121.1.27 SINGLE-VALUE )
olcObjectClasses: {0}( 1.3.6.1.4.1.4754.2.99.1 NAME 'pwdPolicyChecker' SUP t
op AUXILIARY MAY pwdCheckModule )
olcObjectClasses: {1}( 1.3.6.1.4.1.42.2.27.8.2.1 NAME 'pwdPolicy' SUP top AU
XILIARY MUST pwdAttribute MAY ( pwdMinAge $ pwdMaxAge $ pwdInHistory $ pwdC
heckQuality $ pwdMinLength $ pwdExpireWarning $ pwdGraceAuthNLimit $ pwdLoc
kout $ pwdLockoutDuration $ pwdMaxFailure $ pwdFailureCountInterval $ pwdMu
stChange $ pwdAllowUserChange $ pwdSafeModify $ pwdMaxRecordedFailure ) )
structuralObjectClass: olcSchemaConfig
entryUUID: 5c51364a-7ddc-103b-8d41-8b35b6446851
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
createTimestamp: 20210720192809Z
entryCSN: 20210720192809.603187Z#000000#000#000000
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
modifyTimestamp: 20210720192809Z
@@ -0,0 +1,175 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 7d21399c
dn: cn={5}kopano
objectClass: olcSchemaConfig
cn: {5}kopano
olcAttributeTypes: {0}( 1.3.6.1.4.1.47732.1.1.1.1 NAME 'kopanoQuotaOverride'
DESC 'KOPANO: Override child quota' EQUALITY integerMatch SYNTAX 1.3.6.1.4
.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {1}( 1.3.6.1.4.1.47732.1.1.1.2 NAME 'kopanoQuotaWarn' DES
C 'KOPANO: Warning quota size in MB' EQUALITY integerMatch SYNTAX 1.3.6.1.4
.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {2}( 1.3.6.1.4.1.47732.1.1.1.3 NAME 'kopanoQuotaSoft' DES
C 'KOPANO: Soft quota size in MB' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.
1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {3}( 1.3.6.1.4.1.47732.1.1.1.4 NAME 'kopanoQuotaHard' DES
C 'KOPANO: Hard quota size in MB' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.
1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {4}( 1.3.6.1.4.1.47732.1.1.1.5 NAME 'kopanoUserDefaultQuo
taOverride' DESC 'KOPANO: Override User default quota for children' EQUALIT
Y integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {5}( 1.3.6.1.4.1.47732.1.1.1.6 NAME 'kopanoUserDefaultQuo
taWarn' DESC 'KOPANO: User default warning quota size in MB' EQUALITY integ
erMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {6}( 1.3.6.1.4.1.47732.1.1.1.7 NAME 'kopanoUserDefaultQuo
taSoft' DESC 'KOPANO: User default soft quota size in MB' EQUALITY integerM
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {7}( 1.3.6.1.4.1.47732.1.1.1.8 NAME 'kopanoUserDefaultQuo
taHard' DESC 'KOPANO: User default hard quota size in MB' EQUALITY integerM
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {8}( 1.3.6.1.4.1.47732.1.1.2.1 NAME 'kopanoAdmin' DESC 'K
OPANO: Administrator of kopano' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.14
66.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {9}( 1.3.6.1.4.1.47732.1.1.2.2 NAME 'kopanoSharedStoreOnl
y' DESC 'KOPANO: is store a shared store' EQUALITY integerMatch SYNTAX 1.3.
6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {10}( 1.3.6.1.4.1.47732.1.1.2.3 NAME 'kopanoAccount' DESC
'KOPANO: entry is a part of kopano' EQUALITY integerMatch SYNTAX 1.3.6.1.4
.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {11}( 1.3.6.1.4.1.47732.1.1.2.4 NAME 'kopanoSendAsPrivile
ge' DESC 'KOPANO: Users may directly send email as this user' EQUALITY case
IgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.12
1.1.15 )
olcAttributeTypes: {12}( 1.3.6.1.4.1.47732.1.1.2.5 NAME 'kopanoMrAccept' DES
C 'KOPANO: user should auto-accept meeting requests' EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {13}( 1.3.6.1.4.1.47732.1.1.2.6 NAME 'kopanoMrDeclineConf
lict' DESC 'KOPANO: user should automatically decline conflicting meeting r
equests' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-
VALUE )
olcAttributeTypes: {14}( 1.3.6.1.4.1.47732.1.1.2.7 NAME 'kopanoMrDeclineRecu
rring' DESC 'KOPANO: user should automatically decline recurring meeting re
quests' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-V
ALUE )
olcAttributeTypes: {15}( 1.3.6.1.4.1.47732.1.1.2.8 NAME 'kopanoId' DESC 'KOP
ANO: Generic unique ID' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.1
15.121.1.40 SINGLE-VALUE )
olcAttributeTypes: {16}( 1.3.6.1.4.1.47732.1.1.2.9 NAME 'kopanoResourceType'
DESC 'KOPANO: for shared stores, resource is type Room or Equipment' EQUAL
ITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.146
6.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {17}( 1.3.6.1.4.1.47732.1.1.2.10 NAME 'kopanoResourceCapa
city' DESC 'KOPANO: number of rooms or equipment available' EQUALITY intege
rMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {18}( 1.3.6.1.4.1.47732.1.1.2.11 NAME 'kopanoHidden' DESC
'KOPANO: This object should be hidden from address book' EQUALITY integerM
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {19}( 1.3.6.1.4.1.47732.1.1.2.13 NAME 'kopanoEnabledFeatu
res' DESC 'KOPANO: This user has these features explicitly enabled' EQUALIT
Y caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
olcAttributeTypes: {20}( 1.3.6.1.4.1.47732.1.1.2.14 NAME 'kopanoDisabledFeat
ures' DESC 'KOPANO: This user has these features explicitly disabled' EQUAL
ITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
olcAttributeTypes: {21}( 1.3.6.1.4.1.47732.1.1.3.1 NAME 'kopanoAliases' DESC
'KOPANO: All other email addresses for this user' EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
olcAttributeTypes: {22}( 1.3.6.1.4.1.47732.1.1.4.1 NAME 'kopanoUserServer' D
ESC 'KOPANO: Home server for the user' EQUALITY caseIgnoreMatch SUBSTR case
IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {23}( 1.3.6.1.4.1.47732.1.1.6.1 NAME 'kopanoUserArchiveSe
rvers' DESC 'KOPANO: List of server names that contain an archive store for
the user' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX
1.3.6.1.4.1.1466.115.121.1.15 )
olcAttributeTypes: {24}( 1.3.6.1.4.1.47732.1.1.6.2 NAME 'kopanoUserArchiveCo
uplings' DESC 'KOPANO: List of username:foldername pairs that specify many-
to-one archive locations' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstri
ngsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
olcAttributeTypes: {25}( 1.3.6.1.4.1.47732.1.2.2.1 NAME 'kopanoSecurityGroup
' DESC 'KOPANO: group has security possibilities' EQUALITY integerMatch SYN
TAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {26}( 1.3.6.1.4.1.47732.1.3.2.4 NAME 'kopanoViewPrivilege
' DESC 'KOPANO: Companies with view privileges over selected company' EQUAL
ITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.146
6.115.121.1.15 )
olcAttributeTypes: {27}( 1.3.6.1.4.1.47732.1.3.2.5 NAME 'kopanoAdminPrivileg
e' DESC 'KOPANO: Users from different companies which are administrator ove
r selected company' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMat
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
olcAttributeTypes: {28}( 1.3.6.1.4.1.47732.1.3.2.6 NAME 'kopanoSystemAdmin'
DESC 'KOPANO: The user who is the system administrator for this company' EQ
UALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.
1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {29}( 1.3.6.1.4.1.47732.1.3.1.5 NAME 'kopanoQuotaUserWarn
ingRecipients' DESC 'KOPANO: Users who will receive a notification email wh
en a user exceeds his quota' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubs
tringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
olcAttributeTypes: {30}( 1.3.6.1.4.1.47732.1.3.1.6 NAME 'kopanoQuotaCompanyW
arningRecipients' DESC 'KOPANO: Users who will receive a notification email
when a company exceeds its quota' EQUALITY caseIgnoreMatch SUBSTR caseIgno
reSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
olcAttributeTypes: {31}( 1.3.6.1.4.1.47732.1.3.4.1 NAME 'kopanoCompanyServer
' DESC 'KOPANO: Home server for the public folders for a company' EQUALITY
caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.11
5.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {32}( 1.3.6.1.4.1.47732.1.4.4.1 NAME 'kopanoHttpPort' DES
C 'KOPANO: Port for the http connection' EQUALITY integerMatch SYNTAX 1.3.6
.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {33}( 1.3.6.1.4.1.47732.1.4.4.2 NAME 'kopanoSslPort' DESC
'KOPANO: Port for the ssl connection' EQUALITY integerMatch SYNTAX 1.3.6.1
.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {34}( 1.3.6.1.4.1.47732.1.4.4.3 NAME 'kopanoFilePath' DES
C 'KOPANO: The Unix socket or named pipe to the server' EQUALITY caseIgnore
Match SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
olcAttributeTypes: {35}( 1.3.6.1.4.1.47732.1.4.4.4 NAME 'kopanoContainsPubli
c' DESC 'KOPANO: This server contains the public store' EQUALITY integerMat
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {36}( 1.3.6.1.4.1.47732.1.4.4.6 NAME 'kopanoProxyURL' DES
C 'KOPANO: Full proxy URL for this server' EQUALITY caseIgnoreMatch SUBSTR
caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
)
olcAttributeTypes: {37}( 1.3.6.1.4.1.47732.1.5.5.1 NAME 'kopanoFilter' DESC
'KOPANO: LDAP Filter to apply' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSu
bstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {38}( 1.3.6.1.4.1.47732.1.5.5.2 NAME 'kopanoBase' DESC 'K
OPANO: LDAP Search base to apply' EQUALITY caseIgnoreMatch SUBSTR caseIgnor
eSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcObjectClasses: {0}( 1.3.6.1.4.1.47732.1.1.0.0 NAME 'kopano-user' DESC 'KO
PANO: an user of Kopano' SUP top AUXILIARY MUST cn MAY ( kopanoQuotaOverrid
e $ kopanoQuotaWarn $ kopanoQuotaSoft $ kopanoSendAsPrivilege $ kopanoQuota
Hard $ kopanoAdmin $ kopanoSharedStoreOnly $ kopanoResourceType $ kopanoRes
ourceCapacity $ kopanoAccount $ kopanoHidden $ kopanoAliases $ kopanoUserSe
rver $ kopanoEnabledFeatures $ kopanoDisabledFeatures $ kopanoUserArchiveSe
rvers $ kopanoUserArchiveCouplings $ uidNumber ) )
olcObjectClasses: {1}( 1.3.6.1.4.1.47732.1.6.0.0 NAME 'kopano-contact' DESC
'KOPANO: a contact of Kopano' SUP top AUXILIARY MUST ( cn $ uidNumber ) MAY
( kopanoSendAsPrivilege $ kopanoHidden $ kopanoAliases $ kopanoAccount ) )
olcObjectClasses: {2}( 1.3.6.1.4.1.47732.1.2.0.0 NAME 'kopano-group' DESC 'K
OPANO: a group of Kopano' SUP top AUXILIARY MUST cn MAY ( kopanoAccount $ k
opanoHidden $ mail $ kopanoAliases $ kopanoSecurityGroup $ kopanoSendAsPriv
ilege $ gidNumber ) )
olcObjectClasses: {3}( 1.3.6.1.4.1.47732.1.3.0.0 NAME 'kopano-company' DESC
'KOPANO: a company of Kopano' SUP top AUXILIARY MUST ou MAY ( kopanoAccount
$ kopanoHidden $ kopanoViewPrivilege $ kopanoAdminPrivilege $ kopanoSystem
Admin $ kopanoQuotaOverride $ kopanoQuotaWarn $ kopanoUserDefaultQuotaOverr
ide $ kopanoUserDefaultQuotaWarn $ kopanoUserDefaultQuotaSoft $ kopanoUserD
efaultQuotaHard $ kopanoQuotaUserWarningRecipients $ kopanoQuotaCompanyWarn
ingRecipients $ kopanoCompanyServer ) )
olcObjectClasses: {4}( 1.3.6.1.4.1.47732.1.4.0.0 NAME 'kopano-server' DESC '
KOPANO: a Kopano server' SUP top AUXILIARY MUST cn MAY ( kopanoAccount $ ko
panoHidden $ kopanoHttpPort $ kopanoSslPort $ kopanoFilePath $ kopanoContai
nsPublic $ kopanoProxyURL ) )
olcObjectClasses: {5}( 1.3.6.1.4.1.47732.1.5.0.0 NAME 'kopano-addresslist' D
ESC 'KOPANO: a Kopano Addresslist' SUP top STRUCTURAL MUST cn MAY ( kopanoA
ccount $ kopanoHidden $ kopanoFilter $ kopanoBase ) )
olcObjectClasses: {6}( 1.3.6.1.4.1.47732.1.7.0.0 NAME 'kopano-dynamicgroup'
DESC 'KOPANO: a Kopano dynamic group' SUP top STRUCTURAL MUST cn MAY ( kopa
noAccount $ kopanoHidden $ mail $ kopanoAliases $ kopanoFilter $ kopanoBase
) )
structuralObjectClass: olcSchemaConfig
entryUUID: 5c5ee420-7ddc-103b-8d42-8b35b6446851
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
createTimestamp: 20210720192809Z
entryCSN: 20210720192809.692846Z#000000#000#000000
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
modifyTimestamp: 20210720192809Z
@@ -0,0 +1,18 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 815bdd09
dn: cn={6}openssh-lpk
objectClass: olcSchemaConfig
cn: {6}openssh-lpk
olcAttributeTypes: {0}( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' D
ESC 'MANDATORY: OpenSSH Public key' EQUALITY octetStringMatch SYNTAX 1.3.6.
1.4.1.1466.115.121.1.40 )
olcObjectClasses: {0}( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' DE
SC 'MANDATORY: OpenSSH LPK objectclass' SUP top AUXILIARY MAY ( sshPublicKe
y $ uid ) )
structuralObjectClass: olcSchemaConfig
entryUUID: 5c6226ee-7ddc-103b-8d43-8b35b6446851
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
createTimestamp: 20210720192809Z
entryCSN: 20210720192809.714216Z#000000#000#000000
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
modifyTimestamp: 20210720192809Z
@@ -0,0 +1,50 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 38be59a6
dn: cn={7}postfix-book
objectClass: olcSchemaConfig
cn: {7}postfix-book
olcAttributeTypes: {0}( 1.3.6.1.4.1.29426.1.10.1 NAME 'mailHomeDirectory' DE
SC 'The absolute path to the mail user home directory' EQUALITY caseExactIA
5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {1}( 1.3.6.1.4.1.29426.1.10.2 NAME 'mailAlias' DESC 'RFC8
22 Mailbox - mail alias' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5Su
bstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
olcAttributeTypes: {2}( 1.3.6.1.4.1.29426.1.10.3 NAME 'mailUidNumber' DESC '
UID required to access the mailbox' EQUALITY integerMatch SYNTAX 1.3.6.1.4.
1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {3}( 1.3.6.1.4.1.29426.1.10.4 NAME 'mailGidNumber' DESC '
GID required to access the mailbox' EQUALITY integerMatch SYNTAX 1.3.6.1.4.
1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {4}( 1.3.6.1.4.1.29426.1.10.5 NAME 'mailEnabled' DESC 'TR
UE to enable, FALSE to disable account' EQUALITY booleanMatch SYNTAX 1.3.6.
1.4.1.1466.115.121.1.7 SINGLE-VALUE )
olcAttributeTypes: {5}( 1.3.6.1.4.1.29426.1.10.6 NAME 'mailGroupMember' DESC
'Name of a mail distribution list' EQUALITY caseExactIA5Match SYNTAX 1.3.6
.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {6}( 1.3.6.1.4.1.29426.1.10.7 NAME 'mailQuota' DESC 'Mail
quota limit in kilobytes' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.14
66.115.121.1.26 )
olcAttributeTypes: {7}( 1.3.6.1.4.1.29426.1.10.8 NAME 'mailStorageDirectory'
DESC 'The absolute path to the mail users mailbox' EQUALITY caseExactIA5Ma
tch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {8}( 1.3.6.1.4.1.29426.1.10.9 NAME 'mailSieveRuleSource'
DESC 'Sun ONE Messaging Server defined attribute' SYNTAX 1.3.6.1.4.1.1466.1
15.121.1.26 X-ORIGIN 'Sun ONE Messaging Server' )
olcAttributeTypes: {9}( 1.3.6.1.4.1.29426.1.10.10 NAME 'mailForwardingAddres
s' DESC 'Address(es) to forward all incoming messages to.' EQUALITY caseIgn
oreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{320} )
olcObjectClasses: {0}( 1.3.6.1.4.1.29426.1.2.2.1 NAME 'PostfixBookMailAccoun
t' DESC 'Mail account used in Postfix Book' SUP top AUXILIARY MUST mail MAY
( mailHomeDirectory $ mailAlias $ mailGroupMember $ mailUidNumber $ mailGi
dNumber $ mailEnabled $ mailQuota $ mailStorageDirectory $ mailSieveRuleSou
rce ) )
olcObjectClasses: {1}( 1.3.6.1.4.1.29426.1.2.2.2 NAME 'PostfixBookMailForwar
d' DESC 'Mail forward used in Postfix Book' SUP top AUXILIARY MUST ( mail $
mailAlias ) MAY mailForwardingAddress )
structuralObjectClass: olcSchemaConfig
entryUUID: 5c658af0-7ddc-103b-8d44-8b35b6446851
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
createTimestamp: 20210720192809Z
entryCSN: 20210720192809.736438Z#000000#000#000000
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
modifyTimestamp: 20210720192809Z
@@ -0,0 +1,235 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 41b81a66
dn: cn={8}samba
objectClass: olcSchemaConfig
cn: {8}samba
olcAttributeTypes: {0}( 1.3.6.1.4.1.7165.2.1.24 NAME 'sambaLMPassword' DESC
'LanManager Password' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.1
15.121.1.26{32} SINGLE-VALUE )
olcAttributeTypes: {1}( 1.3.6.1.4.1.7165.2.1.25 NAME 'sambaNTPassword' DESC
'MD4 hash of the unicode password' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6
.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
olcAttributeTypes: {2}( 1.3.6.1.4.1.7165.2.1.26 NAME 'sambaAcctFlags' DESC '
Account Flags' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.
1.26{16} SINGLE-VALUE )
olcAttributeTypes: {3}( 1.3.6.1.4.1.7165.2.1.27 NAME 'sambaPwdLastSet' DESC
'Timestamp of the last password update' EQUALITY integerMatch SYNTAX 1.3.6.
1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {4}( 1.3.6.1.4.1.7165.2.1.28 NAME 'sambaPwdCanChange' DES
C 'Timestamp of when the user is allowed to update the password' EQUALITY i
ntegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {5}( 1.3.6.1.4.1.7165.2.1.29 NAME 'sambaPwdMustChange' DE
SC 'Timestamp of when the password will expire' EQUALITY integerMatch SYNTA
X 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {6}( 1.3.6.1.4.1.7165.2.1.30 NAME 'sambaLogonTime' DESC '
Timestamp of last logon' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.
121.1.27 SINGLE-VALUE )
olcAttributeTypes: {7}( 1.3.6.1.4.1.7165.2.1.31 NAME 'sambaLogoffTime' DESC
'Timestamp of last logoff' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.11
5.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {8}( 1.3.6.1.4.1.7165.2.1.32 NAME 'sambaKickoffTime' DESC
'Timestamp of when the user will be logged off automatically' EQUALITY int
egerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {9}( 1.3.6.1.4.1.7165.2.1.48 NAME 'sambaBadPasswordCount'
DESC 'Bad password attempt count' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1
.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {10}( 1.3.6.1.4.1.7165.2.1.49 NAME 'sambaBadPasswordTime'
DESC 'Time of the last bad password attempt' EQUALITY integerMatch SYNTAX
1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {11}( 1.3.6.1.4.1.7165.2.1.55 NAME 'sambaLogonHours' DESC
'Logon Hours' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.
1.26{42} SINGLE-VALUE )
olcAttributeTypes: {12}( 1.3.6.1.4.1.7165.2.1.33 NAME 'sambaHomeDrive' DESC
'Driver letter of home directory mapping' EQUALITY caseIgnoreIA5Match SYNTA
X 1.3.6.1.4.1.1466.115.121.1.26{4} SINGLE-VALUE )
olcAttributeTypes: {13}( 1.3.6.1.4.1.7165.2.1.34 NAME 'sambaLogonScript' DES
C 'Logon script path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.
121.1.15{255} SINGLE-VALUE )
olcAttributeTypes: {14}( 1.3.6.1.4.1.7165.2.1.35 NAME 'sambaProfilePath' DES
C 'Roaming profile path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.1
15.121.1.15{255} SINGLE-VALUE )
olcAttributeTypes: {15}( 1.3.6.1.4.1.7165.2.1.36 NAME 'sambaUserWorkstations
' DESC 'List of user workstations the user is allowed to logon to' EQUALITY
caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
olcAttributeTypes: {16}( 1.3.6.1.4.1.7165.2.1.37 NAME 'sambaHomePath' DESC '
Home directory UNC path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.1
15.121.1.15{128} )
olcAttributeTypes: {17}( 1.3.6.1.4.1.7165.2.1.38 NAME 'sambaDomainName' DESC
'Windows NT domain to which the user belongs' EQUALITY caseIgnoreMatch SYN
TAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
olcAttributeTypes: {18}( 1.3.6.1.4.1.7165.2.1.47 NAME 'sambaMungedDial' DESC
'Base64 encoded user parameter string' EQUALITY caseExactMatch SYNTAX 1.3.
6.1.4.1.1466.115.121.1.15{1050} )
olcAttributeTypes: {19}( 1.3.6.1.4.1.7165.2.1.54 NAME 'sambaPasswordHistory'
DESC 'Concatenated MD5 hashes of the salted NT passwords used on this acco
unt' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} )
olcAttributeTypes: {20}( 1.3.6.1.4.1.7165.2.1.20 NAME 'sambaSID' DESC 'Secur
ity ID' EQUALITY caseIgnoreIA5Match SUBSTR caseExactIA5SubstringsMatch SYNT
AX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
olcAttributeTypes: {21}( 1.3.6.1.4.1.7165.2.1.23 NAME 'sambaPrimaryGroupSID'
DESC 'Primary Group Security ID' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.
1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
olcAttributeTypes: {22}( 1.3.6.1.4.1.7165.2.1.51 NAME 'sambaSIDList' DESC 'S
ecurity ID List' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.12
1.1.26{64} )
olcAttributeTypes: {23}( 1.3.6.1.4.1.7165.2.1.19 NAME 'sambaGroupType' DESC
'NT Group Type' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
olcAttributeTypes: {24}( 1.3.6.1.4.1.7165.2.1.21 NAME 'sambaNextUserRid' DES
C 'Next NT rid to give our for users' EQUALITY integerMatch SYNTAX 1.3.6.1.
4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {25}( 1.3.6.1.4.1.7165.2.1.22 NAME 'sambaNextGroupRid' DE
SC 'Next NT rid to give out for groups' EQUALITY integerMatch SYNTAX 1.3.6.
1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {26}( 1.3.6.1.4.1.7165.2.1.39 NAME 'sambaNextRid' DESC 'N
ext NT rid to give out for anything' EQUALITY integerMatch SYNTAX 1.3.6.1.4
.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {27}( 1.3.6.1.4.1.7165.2.1.40 NAME 'sambaAlgorithmicRidBa
se' DESC 'Base at which the samba RID generation algorithm should operate'
EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {28}( 1.3.6.1.4.1.7165.2.1.41 NAME 'sambaShareName' DESC
'Share Name' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE )
olcAttributeTypes: {29}( 1.3.6.1.4.1.7165.2.1.42 NAME 'sambaOptionName' DESC
'Option Name' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SY
NTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
olcAttributeTypes: {30}( 1.3.6.1.4.1.7165.2.1.43 NAME 'sambaBoolOption' DESC
'A boolean option' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1
.7 SINGLE-VALUE )
olcAttributeTypes: {31}( 1.3.6.1.4.1.7165.2.1.44 NAME 'sambaIntegerOption' D
ESC 'An integer option' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.1
21.1.27 SINGLE-VALUE )
olcAttributeTypes: {32}( 1.3.6.1.4.1.7165.2.1.45 NAME 'sambaStringOption' DE
SC 'A string option' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115
.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {33}( 1.3.6.1.4.1.7165.2.1.46 NAME 'sambaStringListOption
' DESC 'A string list option' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1
466.115.121.1.15 )
olcAttributeTypes: {34}( 1.3.6.1.4.1.7165.2.1.53 NAME 'sambaTrustFlags' DESC
'Trust Password Flags' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466
.115.121.1.26 )
olcAttributeTypes: {35}( 1.3.6.1.4.1.7165.2.1.58 NAME 'sambaMinPwdLength' DE
SC 'Minimal password length (default: 5)' EQUALITY integerMatch SYNTAX 1.3.
6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {36}( 1.3.6.1.4.1.7165.2.1.59 NAME 'sambaPwdHistoryLength
' DESC 'Length of Password History Entries (default: 0 => off)' EQUALITY in
tegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {37}( 1.3.6.1.4.1.7165.2.1.60 NAME 'sambaLogonToChgPwd' D
ESC 'Force Users to logon for password change (default: 0 => off, 2 => on)'
EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {38}( 1.3.6.1.4.1.7165.2.1.61 NAME 'sambaMaxPwdAge' DESC
'Maximum password age, in seconds (default: -1 => never expire passwords)'
EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {39}( 1.3.6.1.4.1.7165.2.1.62 NAME 'sambaMinPwdAge' DESC
'Minimum password age, in seconds (default: 0 => allow immediate password c
hange)' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-V
ALUE )
olcAttributeTypes: {40}( 1.3.6.1.4.1.7165.2.1.63 NAME 'sambaLockoutDuration'
DESC 'Lockout duration in minutes (default: 30, -1 => forever)' EQUALITY i
ntegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {41}( 1.3.6.1.4.1.7165.2.1.64 NAME 'sambaLockoutObservati
onWindow' DESC 'Reset time after lockout in minutes (default: 30)' EQUALITY
integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {42}( 1.3.6.1.4.1.7165.2.1.65 NAME 'sambaLockoutThreshold
' DESC 'Lockout users after bad logon attempts (default: 0 => off)' EQUALIT
Y integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {43}( 1.3.6.1.4.1.7165.2.1.66 NAME 'sambaForceLogoff' DES
C 'Disconnect Users outside logon hours (default: -1 => off, 0 => on)' EQUA
LITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {44}( 1.3.6.1.4.1.7165.2.1.67 NAME 'sambaRefuseMachinePwd
Change' DESC 'Allow Machine Password changes (default: 0 => off)' EQUALITY
integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {45}( 1.3.6.1.4.1.7165.2.1.68 NAME 'sambaClearTextPasswor
d' DESC 'Clear text password (used for trusted domain passwords)' EQUALITY
octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
olcAttributeTypes: {46}( 1.3.6.1.4.1.7165.2.1.69 NAME 'sambaPreviousClearTex
tPassword' DESC 'Previous clear text password (used for trusted domain pass
words)' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
olcAttributeTypes: {47}( 1.3.6.1.4.1.7165.2.1.70 NAME 'sambaTrustType' DESC
'Type of trust' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
olcAttributeTypes: {48}( 1.3.6.1.4.1.7165.2.1.71 NAME 'sambaTrustAttributes'
DESC 'Trust attributes for a trusted domain' EQUALITY integerMatch SYNTAX
1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {49}( 1.3.6.1.4.1.7165.2.1.72 NAME 'sambaTrustDirection'
DESC 'Direction of a trust' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.1
15.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {50}( 1.3.6.1.4.1.7165.2.1.73 NAME 'sambaTrustPartner' DE
SC 'Fully qualified name of the domain with which a trust exists' EQUALITY
caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
olcAttributeTypes: {51}( 1.3.6.1.4.1.7165.2.1.74 NAME 'sambaFlatName' DESC '
NetBIOS name of a domain' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.
115.121.1.15{128} )
olcAttributeTypes: {52}( 1.3.6.1.4.1.7165.2.1.75 NAME 'sambaTrustAuthOutgoin
g' DESC 'Authentication information for the outgoing portion of a trust' EQ
UALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
olcAttributeTypes: {53}( 1.3.6.1.4.1.7165.2.1.76 NAME 'sambaTrustAuthIncomin
g' DESC 'Authentication information for the incoming portion of a trust' EQ
UALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
olcAttributeTypes: {54}( 1.3.6.1.4.1.7165.2.1.77 NAME 'sambaSecurityIdentifi
er' DESC 'SID of a trusted domain' EQUALITY caseIgnoreIA5Match SUBSTR caseE
xactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALU
E )
olcAttributeTypes: {55}( 1.3.6.1.4.1.7165.2.1.78 NAME 'sambaTrustForestTrust
Info' DESC 'Forest trust information for a trusted domain object' EQUALITY
caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
olcAttributeTypes: {56}( 1.3.6.1.4.1.7165.2.1.79 NAME 'sambaTrustPosixOffset
' DESC 'POSIX offset of a trust' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1
466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {57}( 1.3.6.1.4.1.7165.2.1.80 NAME 'sambaSupportedEncrypt
ionTypes' DESC 'Supported encryption types of a trust' EQUALITY integerMatc
h SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcObjectClasses: {0}( 1.3.6.1.4.1.7165.2.2.6 NAME 'sambaSamAccount' DESC 'S
amba 3.0 Auxilary SAM Account' SUP top AUXILIARY MUST ( uid $ sambaSID ) MA
Y ( cn $ sambaLMPassword $ sambaNTPassword $ sambaPwdLastSet $ sambaLogonTi
me $ sambaLogoffTime $ sambaKickoffTime $ sambaPwdCanChange $ sambaPwdMustC
hange $ sambaAcctFlags $ displayName $ sambaHomePath $ sambaHomeDrive $ sam
baLogonScript $ sambaProfilePath $ description $ sambaUserWorkstations $ sa
mbaPrimaryGroupSID $ sambaDomainName $ sambaMungedDial $ sambaBadPasswordCo
unt $ sambaBadPasswordTime $ sambaPasswordHistory $ sambaLogonHours ) )
olcObjectClasses: {1}( 1.3.6.1.4.1.7165.2.2.4 NAME 'sambaGroupMapping' DESC
'Samba Group Mapping' SUP top AUXILIARY MUST ( gidNumber $ sambaSID $ samba
GroupType ) MAY ( displayName $ description $ sambaSIDList ) )
olcObjectClasses: {2}( 1.3.6.1.4.1.7165.2.2.14 NAME 'sambaTrustPassword' DES
C 'Samba Trust Password' SUP top STRUCTURAL MUST ( sambaDomainName $ sambaN
TPassword $ sambaTrustFlags ) MAY ( sambaSID $ sambaPwdLastSet ) )
olcObjectClasses: {3}( 1.3.6.1.4.1.7165.2.2.15 NAME 'sambaTrustedDomainPassw
ord' DESC 'Samba Trusted Domain Password' SUP top STRUCTURAL MUST ( sambaDo
mainName $ sambaSID $ sambaClearTextPassword $ sambaPwdLastSet ) MAY sambaP
reviousClearTextPassword )
olcObjectClasses: {4}( 1.3.6.1.4.1.7165.2.2.5 NAME 'sambaDomain' DESC 'Samba
Domain Information' SUP top STRUCTURAL MUST ( sambaDomainName $ sambaSID )
MAY ( sambaNextRid $ sambaNextGroupRid $ sambaNextUserRid $ sambaAlgorithm
icRidBase $ sambaMinPwdLength $ sambaPwdHistoryLength $ sambaLogonToChgPwd
$ sambaMaxPwdAge $ sambaMinPwdAge $ sambaLockoutDuration $ sambaLockoutObse
rvationWindow $ sambaLockoutThreshold $ sambaForceLogoff $ sambaRefuseMachi
nePwdChange ) )
olcObjectClasses: {5}( 1.3.6.1.4.1.7165.2.2.7 NAME 'sambaUnixIdPool' DESC 'P
ool for allocating UNIX uids/gids' SUP top AUXILIARY MUST ( uidNumber $ gid
Number ) )
olcObjectClasses: {6}( 1.3.6.1.4.1.7165.2.2.8 NAME 'sambaIdmapEntry' DESC 'M
apping from a SID to an ID' SUP top AUXILIARY MUST sambaSID MAY ( uidNumber
$ gidNumber ) )
olcObjectClasses: {7}( 1.3.6.1.4.1.7165.2.2.9 NAME 'sambaSidEntry' DESC 'Str
uctural Class for a SID' SUP top STRUCTURAL MUST sambaSID )
olcObjectClasses: {8}( 1.3.6.1.4.1.7165.2.2.10 NAME 'sambaConfig' DESC 'Samb
a Configuration Section' SUP top AUXILIARY MAY description )
olcObjectClasses: {9}( 1.3.6.1.4.1.7165.2.2.11 NAME 'sambaShare' DESC 'Samba
Share Section' SUP top STRUCTURAL MUST sambaShareName MAY description )
olcObjectClasses: {10}( 1.3.6.1.4.1.7165.2.2.12 NAME 'sambaConfigOption' DES
C 'Samba Configuration Option' SUP top STRUCTURAL MUST sambaOptionName MAY
( sambaBoolOption $ sambaIntegerOption $ sambaStringOption $ sambaStringLis
toption $ description ) )
olcObjectClasses: {11}( 1.3.6.1.4.1.7165.2.2.16 NAME 'sambaTrustedDomain' DE
SC 'Samba Trusted Domain Object' SUP top STRUCTURAL MUST cn MAY ( sambaTrus
tType $ sambaTrustAttributes $ sambaTrustDirection $ sambaTrustPartner $ sa
mbaFlatName $ sambaTrustAuthOutgoing $ sambaTrustAuthIncoming $ sambaSecuri
tyIdentifier $ sambaTrustForestTrustInfo $ sambaTrustPosixOffset $ sambaSup
portedEncryptionTypes ) )
structuralObjectClass: olcSchemaConfig
entryUUID: 5c692e8a-7ddc-103b-8d45-8b35b6446851
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
createTimestamp: 20210720192809Z
entryCSN: 20210720192809.760287Z#000000#000#000000
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
modifyTimestamp: 20210720192809Z
@@ -0,0 +1,18 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 6d98bb57
dn: olcDatabase={-1}frontend
objectClass: olcDatabaseConfig
objectClass: olcFrontendConfig
olcDatabase: {-1}frontend
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=extern
al,cn=auth manage by * break
olcAccess: {1}to dn.exact="" by * read
olcAccess: {2}to dn.base="cn=Subschema" by * read
olcSizeLimit: 500
structuralObjectClass: olcDatabaseConfig
entryUUID: 5c22f244-7ddc-103b-9ebc-c740e015b81b
creatorsName: cn=config
createTimestamp: 20210720192809Z
entryCSN: 20210720192809.299952Z#000000#000#000000
modifiersName: cn=config
modifyTimestamp: 20210720192809Z
@@ -0,0 +1,16 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 4503eefa
dn: olcDatabase={0}config
objectClass: olcDatabaseConfig
olcDatabase: {0}config
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=extern
al,cn=auth manage by * break
olcRootDN: cn=admin,cn=config
structuralObjectClass: olcDatabaseConfig
entryUUID: 5c248d0c-7ddc-103b-9ebd-c740e015b81b
creatorsName: cn=config
createTimestamp: 20210720192809Z
olcRootPW:: e1NTSEF9eGNpMDV6aExUSXhxL3k0NHYyVHlBR2p0bCsxblV1UHc=
entryCSN: 20210812002157.882687Z#000000#000#000000
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
modifyTimestamp: 20210812002157Z
@@ -0,0 +1,31 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 506f4bf6
dn: olcDatabase={1}mdb
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: {1}mdb
olcDbDirectory: /var/lib/ldap
olcSuffix: dc=glauth,dc=com
olcLastMod: TRUE
olcRootDN: cn=admin,dc=glauth,dc=com
olcDbCheckpoint: 512 30
olcDbMaxSize: 1073741824
structuralObjectClass: olcMdbConfig
entryUUID: 5c2cdd40-7ddc-103b-9ec4-c740e015b81b
creatorsName: cn=admin,cn=config
createTimestamp: 20210720192809Z
olcDbIndex: uid eq
olcDbIndex: mail eq
olcDbIndex: memberOf eq
olcDbIndex: entryCSN eq
olcDbIndex: entryUUID eq
olcDbIndex: objectClass eq
olcAccess: {0}to attrs=userPassword,shadowLastChange by self write by dn="cn
=serviceuser,cn=svcaccts,ou=users,dc=glauth,dc=com" write by anonymous auth
by * none
olcAccess: {1}to * by self write by dn="cn=serviceuser,cn=svcaccts,ou=users,
dc=glauth,dc=com" write by users read by * none
olcRootPW:: e1NTSEF9TzNabzVITEhzQXU1dkg5a0txNTlEVFhGclpPVDBPQUg=
entryCSN: 20210812002157.895969Z#000000#000#000000
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
modifyTimestamp: 20210812002157Z
@@ -0,0 +1,18 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 5273ae52
dn: olcOverlay={0}memberof
objectClass: olcOverlayConfig
objectClass: olcMemberOf
olcOverlay: {0}memberof
olcMemberOfDangling: ignore
olcMemberOfRefInt: TRUE
olcMemberOfGroupOC: groupOfUniqueNames
olcMemberOfMemberAD: uniqueMember
olcMemberOfMemberOfAD: memberOf
structuralObjectClass: olcMemberOf
entryUUID: 5c74c556-7ddc-103b-8d46-8b35b6446851
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
createTimestamp: 20210720192809Z
entryCSN: 20210720192809.836237Z#000000#000#000000
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
modifyTimestamp: 20210720192809Z
@@ -0,0 +1,18 @@
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 a7700674
dn: olcOverlay={1}refint
objectClass: olcOverlayConfig
objectClass: olcRefintConfig
olcOverlay: {1}refint
olcRefintAttribute: owner
olcRefintAttribute: manager
olcRefintAttribute: uniqueMember
olcRefintAttribute: member
olcRefintAttribute: memberOf
structuralObjectClass: olcRefintConfig
entryUUID: 5c78d98e-7ddc-103b-8d47-8b35b6446851
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
createTimestamp: 20210720192809Z
entryCSN: 20210720192809.862968Z#000000#000#000000
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
modifyTimestamp: 20210720192809Z
@@ -0,0 +1,4 @@
export PREVIOUS_LDAP_TLS_CA_CRT_PATH=/container/service/slapd/assets/certs/ca.crt
export PREVIOUS_LDAP_TLS_CRT_PATH=/container/service/slapd/assets/certs/ldap.crt
export PREVIOUS_LDAP_TLS_KEY_PATH=/container/service/slapd/assets/certs/ldap.key
export PREVIOUS_LDAP_TLS_DH_PARAM_PATH=/container/service/slapd/assets/certs/dhparam.pem
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+8
View File
@@ -0,0 +1,8 @@
package assets
import "embed"
// Content is the embedded assets.
//
//go:embed *.tgz js/* css/* *.html
var Content embed.FS
Binary file not shown.
+39
View File
@@ -0,0 +1,39 @@
package assets
import (
"net/http"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
type API struct {
fileServer http.Handler
logger zerolog.Logger
}
func (a *API) RegisterEndpoints(router *http.ServeMux) {
router.HandleFunc("/", a.assets)
router.Handle("/assets/", http.StripPrefix("/assets/", a.fileServer))
}
func (a *API) assets(w http.ResponseWriter, r *http.Request) {
a.logger.Info().Str("path", r.URL.Path).Msg("Web")
if r.URL.Path != "/" {
log.Info().Msg("Web 404")
http.NotFound(w, r)
return
}
a.fileServer.ServeHTTP(w, r)
}
func NewAPI(logger zerolog.Logger) *API {
a := new(API)
a.logger = logger
a.fileServer = http.FileServer(http.FS(Content))
return a
}
+123
View File
@@ -0,0 +1,123 @@
package assets
import (
"bytes"
"crypto/sha256"
"io"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/rs/zerolog"
)
// TestAPI is a small test to make sure that behavior didn't change for the
// worse when switching over to the go1.16+ "embed" package from go-bindata.
func TestAPIAssets(t *testing.T) {
tt := []struct {
Name string
Path string
Check func(testing.TB, *http.Response)
}{
{
Name: "Root",
Path: "/",
Check: func(t testing.TB, res *http.Response) {
if res.StatusCode != http.StatusOK {
t.Fatalf("unexpected response: %v", res.Status)
return
}
h := sha256.New()
if _, err := io.Copy(h, res.Body); err != nil {
t.Error(err)
}
if got, want := h.Sum(nil), mkDigest(t, "index.html"); !bytes.Equal(got, want) {
t.Fatalf("bad digest: got: %x, want %x", got, want)
}
},
},
{
Name: "NotFound",
Path: "/glauth.js",
Check: func(t testing.TB, res *http.Response) {
if res.StatusCode != http.StatusNotFound {
t.Fatalf("unexpected response: %v", res.Status)
}
},
},
{
Name: "Content-Type",
Path: "/assets/js/glauth.js",
Check: func(t testing.TB, res *http.Response) {
if res.StatusCode != http.StatusOK {
t.Fatalf("unexpected response: %v", res.Status)
}
if got, want := res.Header.Get("Content-Type"), `text/javascript; charset=utf-8`; got != want {
t.Fatalf("bad content-type: got: %q, want %q", got, want)
}
h := sha256.New()
if _, err := io.Copy(h, res.Body); err != nil {
t.Error(err)
}
if got, want := h.Sum(nil), mkDigest(t, "js/glauth.js"); !bytes.Equal(got, want) {
t.Fatalf("bad digest: got: %x, want %x", got, want)
}
},
},
{
Name: "Content-Type",
Path: "/assets/css/glauth.css",
Check: func(t testing.TB, res *http.Response) {
if res.StatusCode != http.StatusOK {
t.Fatalf("unexpected response: %v", res.Status)
}
// This isn't exactly the same, it adds a charset argument.
if got, want := res.Header.Get("Content-Type"), `text/css; charset=utf-8`; got != want {
t.Fatalf("bad content-type: got: %q, want %q", got, want)
}
h := sha256.New()
if _, err := io.Copy(h, res.Body); err != nil {
t.Error(err)
}
if got, want := h.Sum(nil), mkDigest(t, "css/glauth.css"); !bytes.Equal(got, want) {
t.Fatalf("bad digest: got: %x, want %x", got, want)
}
},
},
}
mux := http.NewServeMux()
log := zerolog.New(os.Stdout).Level(zerolog.InfoLevel)
NewAPI(log).RegisterEndpoints(mux)
srv := httptest.NewServer(mux)
defer srv.Close()
for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, srv.URL+tc.Path, nil)
if err != nil {
t.Fatal(err)
}
res, err := srv.Client().Do(req)
if err != nil {
t.Fatal(err)
}
defer res.Body.Close()
tc.Check(t, res)
})
}
}
func mkDigest(t testing.TB, path string) []byte {
f, err := Content.Open(path)
if err != nil {
t.Error(err)
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
t.Error(err)
}
return h.Sum(nil)
}
+17 -12
View File
@@ -9,9 +9,9 @@
<title>GLAuth Status</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="assets/glauth.css">
<link rel="stylesheet" href="assets/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="assets/css/glauth.css">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
@@ -29,19 +29,18 @@
<div id="statspanel" class="panel panel-default pull-right">
</div>
<h1>GLAuth</h1>
<p>This tool is in development - not quite ready for production!</p>
<div id="headerpanel"></div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-6">
<h2>stats</h2>
<h2>frontend stats</h2>
<div id="stats" class="jumbotron">
</div>
</div>
<div class="col-xs-12 col-sm-6">
<h2>backend servers</h2>
<h2>backend (LDAP) servers</h2>
<div id="servers" class="jumbotron">
</div>
</div>
@@ -49,6 +48,12 @@
</div> <!-- /container -->
<script id="header-messages-template" type="text/x-handlebars-template">
{{#each header.messages}}
<span>{{{message}}}</span><br>
{{/each}}
</script>
<script id="statspanel-template" type="text/x-handlebars-template">
<div class="panel-body">
<span class="metric">proxy.version:</span> {{proxy.version}}<br>
@@ -87,10 +92,10 @@
<tbody>
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.3/moment-with-locales.min.js"></script>
<script src="assets/glauth.js"></script>
<script src="assets/js/jquery.min.js"></script>
<script src="assets/js/bootstrap.min.js"></script>
<script src="assets/js/handlebars.min.js"></script>
<script src="assets/js/moment-with-locales.min.js"></script>
<script src="assets/js/glauth.js"></script>
</body>
</html>
File diff suppressed because one or more lines are too long
@@ -1,14 +1,32 @@
var interval = 2000;
var internalsEnabled = false;
$(function() {
updateMetrics();
//updateTail();
$.get("/internals", function() {
internalsEnabled = true;
})
.always(function() {
updateMetrics();
//updateTail();
})
});
var tailId = 0;
function updateMetrics() {
var metrics = $.getJSON( "/debug/vars", function(d) {
var template = Handlebars.compile( $("#header-messages-template").html() );
var messages = [];
if(internalsEnabled) {
messages.push({"message": "Also available: <a href='/internals'>Internals: heap, cache, objects, etc.</a>"});
}
var html = template({
"header": {
"messages": messages
}
});
$("#headerpanel").html(html);
var template = Handlebars.compile( $("#statspanel-template").html() );
var html = template(d);
$("#statspanel").html(html);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
+128
View File
@@ -0,0 +1,128 @@
package config
import "time"
// config file
type Backend struct {
BaseDN string
Datastore string
Insecure bool // For LDAP and owncloud backend only
Servers []string // For LDAP and owncloud backend only
NameFormat string
GroupFormat string
SSHKeyAttr string
UseGraphAPI bool // For ownCloud backend only
Plugin string // Path to plugin library, for plugin backend only
PluginHandler string // Name of plugin's main handler function
Database string // For Database backends only
GroupWithSearchCapability string // For PamLinux backend only
AnonymousDSE bool // For Config and Database backends only
}
type Helper struct {
Enabled bool
BaseDN string
Datastore string
Plugin string // Path to plugin library, for plugin backend only
PluginHandler string // Name of plugin's main handler function
Database string // For MySQL backend only TODO REname to match plugin
}
type Frontend struct {
AllowedBaseDNs []string // For LDAP backend only
Listen string
Cert string
Key string
TLS bool
}
type LDAP struct {
Enabled bool
Listen string
// StartTLS parameters
TLS bool
TLSCert string
TLSKey string
TLSCertPath string
TLSKeyPath string
}
type LDAPS struct {
Enabled bool
Listen string
Cert string
Key string
}
type API struct {
Cert string
Enabled bool
Internals bool
Key string
Listen string
SecretToken string
TLS bool
}
type Behaviors struct {
IgnoreCapabilities bool
LimitFailedBinds bool
NumberOfFailedBinds int
PeriodOfFailedBinds time.Duration
BlockFailedBindsFor time.Duration
PruneSourceTableEvery time.Duration
PruneSourcesOlderThan time.Duration
LegacyVersion int
}
type Capability struct {
Action string
Object string
}
// UserAuthenticator authenticates a user via custom auth from a backend
type UserAuthenticator func(user *User, pw string) error
type User struct {
Name string
OtherGroups []int
PassSHA256 string
PassBcrypt string
PassAppSHA256 []string
PassAppBcrypt []string
PassAppCustom UserAuthenticator
PrimaryGroup int
Capabilities []Capability
SSHKeys []string
OTPSecret string
Yubikey string
Disabled bool
UnixID int // TODO: remove after deprecating UnixID on User and Group
UIDNumber int
Mail string
LoginShell string
GivenName string
SN string
Homedir string
CustomAttrs map[string]interface{}
}
type Group struct {
Name string
UnixID int // TODO: remove after deprecating UnixID on User and Group
GIDNumber int
IncludeGroups []int
}
type Config struct {
API API
Backend Backend // Deprecated
Backends []Backend
Helper Helper
Behaviors Behaviors
Debug bool
Syslog bool
StructuredLog bool
WatchConfig bool
YubikeyClientID string
YubikeySecret string
Frontend Frontend
LDAP LDAP
LDAPS LDAPS
Groups []Group
Users []User
ConfigFile string
AwsAccessKeyId string
AwsSecretAccessKey string
AwsRegion string
}
+51
View File
@@ -0,0 +1,51 @@
package frontend
import (
"context"
"github.com/rs/zerolog"
"github.com/glauth/glauth/v2/pkg/config"
)
// Option defines a single option function.
type Option func(o *Options)
// Options defines the available options for this package.
type Options struct {
Logger zerolog.Logger
Config *config.API
Context context.Context
}
// newOptions initializes the available default options.
func newOptions(opts ...Option) Options {
opt := Options{}
for _, o := range opts {
o(&opt)
}
return opt
}
// Logger provides a function to set the logger option.
func Logger(val zerolog.Logger) Option {
return func(o *Options) {
o.Logger = val
}
}
// Config provides a function to set the config option.
func Config(val *config.API) Option {
return func(o *Options) {
o.Config = val
}
}
// Context provides a function to set the context option.
func Context(val context.Context) Option {
return func(o *Options) {
o.Context = val
}
}
+40
View File
@@ -0,0 +1,40 @@
package frontend
import (
"fmt"
"net/http"
"github.com/glauth/glauth/v2/internal/monitoring"
"github.com/glauth/glauth/v2/pkg/assets"
)
// RunAPI provides a basic REST API
func RunAPI(opts ...Option) {
options := newOptions(opts...)
log := options.Logger
cfg := options.Config
router := http.DefaultServeMux
assets.NewAPI(log).RegisterEndpoints(router)
monitoring.NewAPI(log).RegisterEndpoints(router)
if cfg.TLS {
log.Info().Str("address", cfg.Listen).Msg("Starting HTTPS server")
monitoring.NewCollector(fmt.Sprintf("https://%s/debug/vars", cfg.Listen))
if err := http.ListenAndServeTLS(cfg.Listen, cfg.Cert, cfg.Key, nil); err != nil {
log.Error().Err(err).Msg("error starting HTTPS server")
}
return
}
log.Info().Str("address", cfg.Listen).Msg("Starting HTTP server")
monitoring.NewCollector(fmt.Sprintf("http://%s/debug/vars", cfg.Listen))
if err := http.ListenAndServe(cfg.Listen, nil); err != nil {
log.Error().Err(err).Msg("error starting HTTP server")
}
}
+403
View File
@@ -0,0 +1,403 @@
package handler
import (
"fmt"
"net"
"regexp"
"sort"
"strings"
"time"
"github.com/rs/zerolog"
"github.com/GeertJohan/yubigo"
"github.com/glauth/glauth/v2/internal/monitoring"
"github.com/glauth/glauth/v2/pkg/config"
"github.com/glauth/glauth/v2/pkg/stats"
"github.com/glauth/ldap"
)
type configHandler struct {
backend config.Backend
log *zerolog.Logger
cfg *config.Config
yubikeyAuth *yubigo.YubiAuth
ldohelper LDAPOpsHelper
attmatcher *regexp.Regexp
monitor monitoring.MonitorInterface
}
// NewConfigHandler creates a new config backed handler
func NewConfigHandler(opts ...Option) Handler {
options := newOptions(opts...)
handler := configHandler{
backend: options.Backend,
log: options.Logger,
cfg: options.Config, // TODO only used to access Users and Groups, move that to dedicated options
yubikeyAuth: options.YubiAuth,
ldohelper: options.LDAPHelper,
attmatcher: configattributematcher,
monitor: options.Monitor,
}
return handler
}
func (h configHandler) GetBackend() config.Backend {
return h.backend
}
func (h configHandler) GetLog() *zerolog.Logger {
return h.log
}
func (h configHandler) GetCfg() *config.Config {
return h.cfg
}
func (h configHandler) GetYubikeyAuth() *yubigo.YubiAuth {
return h.yubikeyAuth
}
// Bind implements a bind request against the config file
func (h configHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (result ldap.LDAPResultCode, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "bind", "status": fmt.Sprintf("%v", result)},
time.Since(start).Seconds(),
)
}()
return h.ldohelper.Bind(h, bindDN, bindSimplePw, conn)
}
// Search implements a search request against the config file
func (h configHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (result ldap.ServerSearchResult, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "search", "status": fmt.Sprintf("%v", result.ResultCode)},
time.Since(start).Seconds(),
)
}()
return h.ldohelper.Search(h, bindDN, searchReq, conn)
}
// Add is not supported for a static config file
func (h configHandler) Add(boundDN string, req ldap.AddRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "add", "status": fmt.Sprintf("%v", result)},
time.Since(start).Seconds(),
)
}()
return ldap.LDAPResultInsufficientAccessRights, nil
}
// Modify is not supported for a static config file
func (h configHandler) Modify(boundDN string, req ldap.ModifyRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "modify", "status": fmt.Sprintf("%v", result)},
time.Since(start).Seconds(),
)
}()
return ldap.LDAPResultInsufficientAccessRights, nil
}
// Delete is not supported for a static config file
func (h configHandler) Delete(boundDN string, deleteDN string, conn net.Conn) (result ldap.LDAPResultCode, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "delete", "status": fmt.Sprintf("%v", result)},
time.Since(start).Seconds(),
)
}()
return ldap.LDAPResultInsufficientAccessRights, nil
}
func (h configHandler) FindUser(userName string, searchByUPN bool) (f bool, u config.User, err error) {
user := config.User{}
found := false
for _, u := range h.cfg.Users {
if searchByUPN {
if strings.EqualFold(u.Mail, userName) {
found = true
user = u
}
} else {
if strings.EqualFold(u.Name, userName) {
found = true
user = u
}
}
}
return found, user, nil
}
func (h configHandler) FindGroup(groupName string) (f bool, g config.Group, err error) {
// TODO Does g get erased, and above does u get erased?
// TODO and what about f?
group := config.Group{}
found := false
for _, g := range h.cfg.Groups {
if strings.EqualFold(g.Name, groupName) {
found = true
group = g
}
}
return found, group, nil
}
func (h configHandler) FindPosixAccounts(hierarchy string) (entrylist []*ldap.Entry, err error) {
entries := []*ldap.Entry{}
for _, u := range h.cfg.Users {
attrs := []*ldap.EntryAttribute{}
attrs = append(attrs, &ldap.EntryAttribute{Name: h.backend.NameFormat, Values: []string{u.Name}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "uid", Values: []string{u.Name}})
if len(u.GivenName) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{Name: "givenName", Values: []string{u.GivenName}})
}
if len(u.SN) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{Name: "sn", Values: []string{u.SN}})
}
attrs = append(attrs, &ldap.EntryAttribute{Name: "ou", Values: []string{h.getGroupName(u.PrimaryGroup)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "uidNumber", Values: []string{fmt.Sprintf("%d", u.UIDNumber)}})
if u.Disabled {
attrs = append(attrs, &ldap.EntryAttribute{Name: "accountStatus", Values: []string{"inactive"}})
} else {
attrs = append(attrs, &ldap.EntryAttribute{Name: "accountStatus", Values: []string{"active"}})
}
if len(u.Mail) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{Name: "mail", Values: []string{u.Mail}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "userPrincipalName", Values: []string{u.Mail}})
}
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixAccount", "shadowAccount"}})
if len(u.LoginShell) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{Name: "loginShell", Values: []string{u.LoginShell}})
} else {
attrs = append(attrs, &ldap.EntryAttribute{Name: "loginShell", Values: []string{"/bin/bash"}})
}
if len(u.Homedir) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{Name: "homeDirectory", Values: []string{u.Homedir}})
} else {
attrs = append(attrs, &ldap.EntryAttribute{Name: "homeDirectory", Values: []string{"/home/" + u.Name}})
}
attrs = append(attrs, &ldap.EntryAttribute{Name: "description", Values: []string{fmt.Sprintf("%s", u.Name)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "gecos", Values: []string{fmt.Sprintf("%s", u.Name)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "gidNumber", Values: []string{fmt.Sprintf("%d", u.PrimaryGroup)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberOf", Values: h.getGroupDNs(append(u.OtherGroups, u.PrimaryGroup))})
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowExpire", Values: []string{"-1"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowFlag", Values: []string{"134538308"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowInactive", Values: []string{"-1"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowLastChange", Values: []string{"11000"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowMax", Values: []string{"99999"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowMin", Values: []string{"-1"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowWarning", Values: []string{"7"}})
if len(u.SSHKeys) > 0 {
attrs = append(attrs, &ldap.EntryAttribute{Name: h.backend.SSHKeyAttr, Values: u.SSHKeys})
}
if len(u.CustomAttrs) > 0 {
for key, attr := range u.CustomAttrs {
switch typedattr := attr.(type) {
case []interface{}:
var values []string
for _, v := range typedattr {
switch typedvalue := v.(type) {
case string:
values = append(values, MaybeDecode(typedvalue))
default:
values = append(values, MaybeDecode(fmt.Sprintf("%v", typedvalue)))
}
}
attrs = append(attrs, &ldap.EntryAttribute{Name: key, Values: values})
default:
h.log.Warn().Str("key", key).Interface("value", attr).Msg("Unable to map custom attribute")
}
}
}
var dn string
if hierarchy == "" {
dn = fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(u.PrimaryGroup), h.backend.BaseDN)
} else {
dn = fmt.Sprintf("%s=%s,%s=%s,%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(u.PrimaryGroup), hierarchy, h.backend.BaseDN)
}
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
}
return entries, nil
}
func (h configHandler) FindPosixGroups(hierarchy string) (entrylist []*ldap.Entry, err error) {
asGroupOfUniqueNames := hierarchy == "ou=groups"
entries := []*ldap.Entry{}
for _, g := range h.cfg.Groups {
attrs := []*ldap.EntryAttribute{}
attrs = append(attrs, &ldap.EntryAttribute{Name: h.backend.GroupFormat, Values: []string{g.Name}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "uid", Values: []string{g.Name}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "description", Values: []string{fmt.Sprintf("%s", g.Name)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "gidNumber", Values: []string{fmt.Sprintf("%d", g.GIDNumber)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "uniqueMember", Values: h.getGroupMemberDNs(g.GIDNumber)})
if asGroupOfUniqueNames {
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"groupOfUniqueNames", "top"}})
} else {
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberUid", Values: h.getGroupMemberIDs(g.GIDNumber)})
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixGroup", "top"}})
}
dn := fmt.Sprintf("%s=%s,%s,%s", h.backend.GroupFormat, g.Name, hierarchy, h.backend.BaseDN)
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
}
return entries, nil
}
// Close does not actually close anything, because the config data is kept in memory
func (h configHandler) Close(boundDn string, conn net.Conn) error {
stats.Frontend.Add("closes", 1)
return nil
}
func (h configHandler) getGroupMemberDNs(gid int) []string {
var insertOuUsers string
if h.cfg.Behaviors.LegacyVersion > 0 && h.cfg.Behaviors.LegacyVersion <= 20100 {
insertOuUsers = ""
} else {
insertOuUsers = ",ou=users"
}
members := make(map[string]bool)
for _, u := range h.cfg.Users {
if u.PrimaryGroup == gid {
dn := fmt.Sprintf("%s=%s,%s=%s%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(u.PrimaryGroup), insertOuUsers, h.backend.BaseDN)
members[dn] = true
} else {
for _, othergid := range u.OtherGroups {
if othergid == gid {
dn := fmt.Sprintf("%s=%s,%s=%s%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(u.PrimaryGroup), insertOuUsers, h.backend.BaseDN)
members[dn] = true
}
}
}
}
for _, g := range h.cfg.Groups {
if gid == g.GIDNumber {
for _, includegroupid := range g.IncludeGroups {
if includegroupid != gid {
includegroupmembers := h.getGroupMemberDNs(includegroupid)
for _, includegroupmember := range includegroupmembers {
members[includegroupmember] = true
}
}
}
}
}
m := []string{}
for k := range members {
m = append(m, k)
}
sort.Strings(m)
return m
}
func (h configHandler) getGroupMemberIDs(gid int) []string {
members := make(map[string]bool)
for _, u := range h.cfg.Users {
if u.PrimaryGroup == gid {
members[u.Name] = true
} else {
for _, othergid := range u.OtherGroups {
if othergid == gid {
members[u.Name] = true
}
}
}
}
for _, g := range h.cfg.Groups {
if gid == g.GIDNumber {
for _, includegroupid := range g.IncludeGroups {
if includegroupid == gid {
h.log.Warn().Int("groupid", includegroupid).Msg("Ignoring myself as included group")
} else {
includegroupmemberids := h.getGroupMemberIDs(includegroupid)
for _, includegroupmemberid := range includegroupmemberids {
members[includegroupmemberid] = true
}
}
}
}
}
m := []string{}
for k := range members {
m = append(m, k)
}
sort.Strings(m)
return m
}
// Converts an array of GUIDs into an array of DNs
func (h configHandler) getGroupDNs(gids []int) []string {
groups := make(map[string]bool)
for _, gid := range gids {
for _, g := range h.cfg.Groups {
if g.GIDNumber == gid {
dn := fmt.Sprintf("%s=%s,ou=groups,%s", h.backend.GroupFormat, g.Name, h.backend.BaseDN)
groups[dn] = true
}
for _, includegroupid := range g.IncludeGroups {
if includegroupid == gid && g.GIDNumber != gid {
includegroupdns := h.getGroupDNs([]int{g.GIDNumber})
for _, includegroupdn := range includegroupdns {
groups[includegroupdn] = true
}
}
}
}
}
g := []string{}
for k := range groups {
g = append(g, k)
}
sort.Strings(g)
return g
}
func (h configHandler) getGroupName(gid int) string {
for _, g := range h.cfg.Groups {
if g.GIDNumber == gid {
return g.Name
}
}
return ""
}
+35
View File
@@ -0,0 +1,35 @@
package handler
import (
"github.com/glauth/glauth/v2/pkg/config"
"github.com/glauth/ldap"
)
type HelperMaker interface {
FindUser(userName string, searchByUPN bool) (bool, config.User, error)
FindGroup(groupName string) (bool, config.Group, error)
}
// Handler is the common interface for all datastores
type Handler interface {
// read support
ldap.Binder
ldap.Searcher
ldap.Closer
// write support
ldap.Adder
ldap.Modifier // Note: modifying eg the uid or cn might change the dn because the hierarchy is determined by the backend
ldap.Deleter
// helper
HelperMaker
}
// TODO When I grow up, I want to handle pointers same as I would in C
// and not need a counter because I would not allocate statically
// but use idiomatic slicing instead
type HandlerWrapper struct {
Handlers []Handler
Count *int
}
+18
View File
@@ -0,0 +1,18 @@
package handler
import (
"encoding/base64"
"strings"
)
func MaybeDecode(value string) string {
if strings.HasPrefix(value, "base64: ") {
udec, err := base64.StdEncoding.DecodeString(value[8:])
if err != nil {
value = "* invalid value *"
} else {
value = string(udec)
}
}
return value
}
+532
View File
@@ -0,0 +1,532 @@
package handler
import (
"crypto/sha256"
"crypto/tls"
"encoding/json"
"fmt"
"net"
"net/url"
"os"
"regexp"
"strconv"
"strings"
"sync"
"time"
"github.com/rs/zerolog"
"github.com/glauth/glauth/v2/internal/monitoring"
"github.com/glauth/glauth/v2/pkg/config"
"github.com/glauth/glauth/v2/pkg/stats"
"github.com/glauth/ldap"
"github.com/pquerna/otp/totp"
)
// global matcher
var ldapattributematcher = regexp.MustCompile(`(?i)(?P<attribute>[a-zA-Z0-9]+)\s*=\s*(?P<value>.*)`)
type ldapHandler struct {
backend config.Backend
handlers HandlerWrapper
doPing chan bool
log *zerolog.Logger
lock *sync.Mutex // for sessions and servers
sessions map[string]ldapSession
servers []ldapBackend
helper Handler
attm *regexp.Regexp
monitor monitoring.MonitorInterface
}
// global lock for ldapHandler sessions & servers manipulation
var ldaplock sync.Mutex
type ldapSession struct {
id string
c net.Conn
ldap *ldap.Conn
}
type ldapBackendStatus int
const (
Down ldapBackendStatus = iota
Up
)
type ldapBackend struct {
Scheme string
Hostname string
Port int
Status ldapBackendStatus
Ping time.Duration
}
func NewLdapHandler(opts ...Option) Handler {
options := newOptions(opts...)
handler := ldapHandler{ // set non-zero-value defaults here
backend: options.Backend,
handlers: options.Handlers,
sessions: make(map[string]ldapSession),
doPing: make(chan bool),
log: options.Logger,
helper: options.Helper,
lock: &ldaplock,
attm: ldapattributematcher,
monitor: options.Monitor,
}
// parse LDAP URLs
for _, ldapurl := range handler.backend.Servers {
l, err := parseURL(ldapurl)
if err != nil {
handler.log.Error().Err(err).Msg("could not parse url")
os.Exit(1)
}
handler.servers = append(handler.servers, l)
}
// test server connectivity before listening, then keep it updated
handler.monitorServers()
return handler
}
func (h ldapHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (result ldap.LDAPResultCode, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "bind", "status": fmt.Sprintf("%v", result)},
time.Since(start).Seconds(),
)
}()
h.log.Debug().Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("Bind request")
// if h.helper != nil {
if true {
lowerBindDN := strings.ToLower(bindDN)
baseDN := strings.ToLower("," + h.backend.BaseDN)
parts := strings.Split(strings.TrimSuffix(lowerBindDN, baseDN), ",")
userName := strings.TrimPrefix(parts[0], h.backend.NameFormat+"=")
validotp := false
// Find the user
// We are going to go through all backends and ask
// until we find our user or die of boredom.
user := config.User{}
found := false
for i, handler := range h.handlers.Handlers {
found, user, _ = handler.FindUser(userName, false)
if found {
break
}
if i >= *h.handlers.Count {
break
}
}
if !found {
validotp = true
} else {
if len(user.OTPSecret) == 0 {
validotp = true
} else {
if len(bindSimplePw) > 6 {
otp := bindSimplePw[len(bindSimplePw)-6:]
bindSimplePw = bindSimplePw[:len(bindSimplePw)-6]
validotp = totp.Validate(otp, user.OTPSecret)
}
}
}
if !validotp {
h.log.Debug().Msg(fmt.Sprintf("Bind Error: invalid OTP token as %s from %s", bindDN, conn.RemoteAddr().String()))
return ldap.LDAPResultInvalidCredentials, nil
}
}
stats.Frontend.Add("bind_reqs", 1)
s, err := h.getSession(conn)
if err != nil {
stats.Frontend.Add("bind_ldapSession_errors", 1)
h.log.Debug().Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Err(err).Msg("could not get session")
return ldap.LDAPResultOperationsError, err
}
if err := s.ldap.Bind(bindDN, bindSimplePw); err != nil {
stats.Frontend.Add("bind_errors", 1)
h.log.Debug().Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("invalid creds")
return ldap.LDAPResultInvalidCredentials, nil
}
stats.Frontend.Add("bind_successes", 1)
h.log.Debug().Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("bind success")
return ldap.LDAPResultSuccess, nil
}
func (h ldapHandler) Search(boundDN string, searchReq ldap.SearchRequest, conn net.Conn) (result ldap.ServerSearchResult, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "search", "status": fmt.Sprintf("%v", result.ResultCode)},
time.Since(start).Seconds(),
)
}()
wantAttributes := true
wantTypesOnly := false
h.log.Debug().Str("binddn", boundDN).Str("src", conn.RemoteAddr().String()).Str("filter", searchReq.Filter).Msg("Search request")
// "1.1" has special meaning: it does what an empty attribute list would do
// if it didn't already mean "return all attributes"
if len(searchReq.Attributes) == 1 && searchReq.Attributes[0] == "1.1" {
wantAttributes = false
searchReq.Attributes = searchReq.Attributes[:0]
}
// TypesOnly cannot be true: if it were, glauth would not be able to
// match the returned valuea against the query
if searchReq.TypesOnly == true {
wantTypesOnly = true
searchReq.TypesOnly = false
}
stats.Frontend.Add("search_reqs", 1)
s, err := h.getSession(conn)
if err != nil {
stats.Frontend.Add("search_ldapSession_errors", 1)
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, nil
}
search := ldap.NewSearchRequest(
searchReq.BaseDN,
searchReq.Scope,
searchReq.DerefAliases,
searchReq.SizeLimit,
searchReq.TimeLimit,
searchReq.TypesOnly,
searchReq.Filter,
searchReq.Attributes,
searchReq.Controls,
)
h.log.Debug().Interface("request", search).Msg("Search request to backend")
sr, err := s.ldap.Search(search)
h.log.Debug().Interface("result", sr).Msg("Backend Search result")
if !wantAttributes {
h.log.Debug().Str("type", "No attributes").Msg("AP: Search Info")
for _, entry := range sr.Entries {
entry.Attributes = entry.Attributes[:0]
}
}
if wantTypesOnly {
h.log.Debug().Str("type", "Types only").Msg("AP: Search Info")
for _, entry := range sr.Entries {
for _, attribute := range entry.Attributes {
attribute.Values = attribute.Values[:0]
}
}
}
// WART used to debug when testing special cases against
// glauth acting as a backend, where it may have
// the same workaround thus hiding the issue
/*
for _, entry := range sr.Entries {
for _, attribute := range entry.Attributes {
if attribute.Name == "objectclass" {
attribute.Name = "bogus"
}
}
}
*/
// If our original attribute is not present, either because:
// 1-This is a root query
// 2-We were asked not to return attributes
// 3-We were asked not to return values
// then we re-insert the correct values in there.
if searchReq.Scope == 0 && searchReq.BaseDN == "" {
h.log.Debug().Str("type", "Root search detected").Msg("AP: Search Info")
}
filters := h.buildReqAttributesList(searchReq.Filter, []string{})
for _, filter := range filters {
attbits := h.attm.FindStringSubmatch(filter)
for _, entry := range sr.Entries {
foundattname := false
for _, attribute := range entry.Attributes {
if strings.ToLower(attribute.Name) == strings.ToLower(attbits[1]) {
foundattname = true
if len(attbits[2]) == 0 {
attribute.Values = []string{attbits[2]}
}
break
}
}
if !foundattname {
entry.Attributes = append(entry.Attributes, &ldap.EntryAttribute{Name: attbits[1], Values: []string{attbits[2]}})
}
}
}
ssr := ldap.ServerSearchResult{
Entries: sr.Entries,
Referrals: sr.Referrals,
Controls: sr.Controls,
}
h.log.Debug().Interface("result", ssr).Msg("Frontend Search result")
if err != nil {
e := err.(*ldap.Error)
h.log.Debug().Err(err).Msg("search Err")
stats.Frontend.Add("search_errors", 1)
ssr.ResultCode = ldap.LDAPResultCode(e.ResultCode)
return ssr, err
}
stats.Frontend.Add("search_successes", 1)
h.log.Debug().Str("filter", search.Filter).Int("numentries", len(ssr.Entries)).Msg("AP: Search OK")
return ssr, nil
}
func (h ldapHandler) buildReqAttributesList(filter string, filters []string) []string {
maxp := len(filter)
start := -1
descended := false
for p, c := range filter {
if c == '(' {
if p+1 < maxp {
start = p + 1
}
} else if c == ')' {
if start > -1 {
descended = true
filters = h.buildReqAttributesList(filter[start:p], filters)
}
start = -1
}
}
if !descended {
filters = append(filters, filter)
}
return filters
}
// Add is not yet supported for the ldap backend
func (h ldapHandler) Add(boundDN string, req ldap.AddRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "add", "status": fmt.Sprintf("%v", result)},
time.Since(start).Seconds(),
)
}()
return ldap.LDAPResultInsufficientAccessRights, nil
}
// Modify is not yet supported for the ldap backend
func (h ldapHandler) Modify(boundDN string, req ldap.ModifyRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "modify", "status": fmt.Sprintf("%v", result)},
time.Since(start).Seconds(),
)
}()
return ldap.LDAPResultInsufficientAccessRights, nil
}
// Delete is not yet supported for the ldap backend
func (h ldapHandler) Delete(boundDN string, deleteDN string, conn net.Conn) (result ldap.LDAPResultCode, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "delete", "status": fmt.Sprintf("%v", result)},
time.Since(start).Seconds(),
)
}()
return ldap.LDAPResultInsufficientAccessRights, nil
}
func (h ldapHandler) FindUser(userName string, searchByUPN bool) (found bool, user config.User, err error) {
return false, config.User{}, nil
}
func (h ldapHandler) FindGroup(groupName string) (found bool, group config.Group, err error) {
return false, config.Group{}, nil
}
func (h ldapHandler) Close(boundDn string, conn net.Conn) error {
conn.Close() // close connection to the server when then client is closed
h.lock.Lock()
defer h.lock.Unlock()
delete(h.sessions, connID(conn))
stats.Frontend.Add("closes", 1)
stats.Backend.Add("closes", 1)
return nil
}
// monitorServers tests server connectivity before listening, then keeps it updated
func (h *ldapHandler) monitorServers() {
err := h.ping()
if err != nil {
h.log.Error().Err(err).Msg("could not ping server")
os.Exit(1)
// TODO return error
}
go func() {
for {
select {
case <-h.doPing:
h.log.Warn().Msg("doPing requested due to server failure")
err = h.ping()
if err != nil {
h.log.Error().Err(err).Msg("could not ping server")
os.Exit(1)
// TODO return error
}
case <-time.NewTimer(60 * time.Second).C:
h.log.Warn().Msg("doPing after timeout")
err = h.ping()
if err != nil {
h.log.Error().Err(err).Msg("could not ping server")
os.Exit(1)
// TODO return error
}
}
}
}()
}
func (h ldapHandler) getSession(conn net.Conn) (ldapSession, error) {
id := connID(conn)
h.lock.Lock()
s, ok := h.sessions[id] // use server connection if it exists
h.lock.Unlock()
if !ok { // open a new server connection if not
var l *ldap.Conn
server, err := h.getBestServer() // pick the best server
if err != nil {
return ldapSession{}, err
}
dest := fmt.Sprintf("%s:%d", server.Hostname, server.Port)
if server.Scheme == "ldaps" {
tlsCfg := &tls.Config{}
if h.backend.Insecure {
tlsCfg.InsecureSkipVerify = true
}
l, err = ldap.DialTLS("tcp", dest, tlsCfg)
} else if server.Scheme == "ldap" {
l, err = ldap.Dial("tcp", dest)
}
if err != nil {
select {
case h.doPing <- true: // non-blocking send
default:
}
return ldapSession{}, err
}
s = ldapSession{id: id, c: conn, ldap: l}
h.lock.Lock()
h.sessions[s.id] = s
h.lock.Unlock()
}
return s, nil
}
func (h ldapHandler) ping() error {
healthy := false
for k, s := range h.servers {
var l *ldap.Conn
var err error
dest := fmt.Sprintf("%s:%d", s.Hostname, s.Port)
start := time.Now()
if h.servers[0].Scheme == "ldaps" {
tlsCfg := &tls.Config{}
if h.backend.Insecure {
tlsCfg.InsecureSkipVerify = true
}
l, err = ldap.DialTLS("tcp", dest, tlsCfg)
} else if h.servers[0].Scheme == "ldap" {
l, err = ldap.Dial("tcp", dest)
}
elapsed := time.Since(start)
h.lock.Lock()
if err != nil || l == nil {
h.log.Fatal().Str("hostname", s.Hostname).Int("port", s.Port).Err(err).Msg("server ping failed")
h.servers[k].Ping = 0
h.servers[k].Status = Down
} else {
healthy = true
h.servers[k].Ping = elapsed
h.servers[k].Status = Up
l.Close() // prank caller
}
h.lock.Unlock()
}
h.log.Debug().Interface("servers", h.servers).Msg("Server health")
b, err := json.Marshal(h.servers)
if err != nil {
h.log.Fatal().Err(err).Msg("error encoding tail data")
}
stats.Backend.Set("servers", stats.Stringer(string(b)))
if healthy == false {
return fmt.Errorf("no healthy servers")
}
return nil
}
func (h ldapHandler) getBestServer() (ldapBackend, error) {
favorite := ldapBackend{}
forever, err := time.ParseDuration("30m")
if err != nil {
return ldapBackend{}, err
}
bestping := forever
for _, s := range h.servers {
if s.Status == Up && s.Ping < bestping {
favorite = s
bestping = s.Ping
}
}
if bestping == forever {
return ldapBackend{}, fmt.Errorf("No healthy servers found")
}
h.log.Debug().Interface("favorite", favorite).Msg("Best server")
return favorite, nil
}
// helper functions
func connID(conn net.Conn) string {
h := sha256.New()
h.Write([]byte(conn.LocalAddr().String() + conn.RemoteAddr().String()))
sha := fmt.Sprintf("% x", h.Sum(nil))
return string(sha)
}
func parseURL(ldapurl string) (ldapBackend, error) {
u, err := url.Parse(ldapurl)
if err != nil {
return ldapBackend{}, err
}
var port int
if u.Scheme == "ldaps" {
port = 636
} else if u.Scheme == "ldap" {
port = 389
} else {
return ldapBackend{}, fmt.Errorf("unknown LDAP scheme: %s", u.Scheme)
}
parts := strings.Split(u.Host, ":")
hostname := parts[0]
if len(parts) > 1 {
port, err = strconv.Atoi(parts[1])
if err != nil {
return ldapBackend{}, err
}
}
return ldapBackend{Scheme: u.Scheme, Hostname: hostname, Port: port}, nil
}
+775
View File
@@ -0,0 +1,775 @@
package handler
import (
"bufio"
"crypto/sha256"
"encoding/hex"
"fmt"
"net"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/rs/zerolog"
"github.com/GeertJohan/yubigo"
"github.com/glauth/glauth/v2/pkg/config"
"github.com/glauth/glauth/v2/pkg/stats"
"github.com/glauth/ldap"
"github.com/pquerna/otp/totp"
"golang.org/x/crypto/bcrypt"
)
var configattributematcher = regexp.MustCompile(`(?i)\((?P<attribute>[a-zA-Z0-9]+)\s*=\s*(?P<value>.*)\)`)
var emailmatcher = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
type LDAPOpsHandler interface {
GetBackend() config.Backend
GetLog() *zerolog.Logger
GetCfg() *config.Config
GetYubikeyAuth() *yubigo.YubiAuth
FindUser(userName string, searchByUPN bool) (f bool, u config.User, err error)
FindGroup(groupName string) (f bool, g config.Group, err error)
FindPosixAccounts(hierarchy string) (entrylist []*ldap.Entry, err error)
FindPosixGroups(hierarchy string) (entrylist []*ldap.Entry, err error)
}
type failedBind struct {
ts time.Time
}
type sourceInfo struct {
lastSeen time.Time
failures chan failedBind
waitUntil time.Time
}
type LDAPOpsHelper struct {
sources map[string]*sourceInfo
nextPruning time.Time
}
func NewLDAPOpsHelper() LDAPOpsHelper {
helper := LDAPOpsHelper{
sources: make(map[string]*sourceInfo),
nextPruning: time.Now(),
}
return helper
}
func (l LDAPOpsHelper) Bind(h LDAPOpsHandler, bindDN, bindSimplePw string, conn net.Conn) (resultCode ldap.LDAPResultCode, err error) {
if l.isInTimeout(h, conn) {
return ldap.LDAPResultUnwillingToPerform, nil
}
bindDN = strings.ToLower(bindDN)
h.GetLog().Info().Str("binddn", bindDN).Str("basedn", h.GetBackend().BaseDN).Str("src", conn.RemoteAddr().String()).Msg("Bind request")
stats.Frontend.Add("bind_reqs", 1)
// Special Case: bind as anonymous
if bindDN == "" && bindSimplePw == "" {
stats.Frontend.Add("bind_successes", 1)
h.GetLog().Info().Str("src", conn.RemoteAddr().String()).Msg("Anonymous Bind success")
return ldap.LDAPResultSuccess, nil
}
user, ldapcode := l.findUser(h, bindDN, true /* checkGroup */)
if ldapcode != ldap.LDAPResultSuccess {
return ldapcode, nil
}
validotp := false
if len(user.Yubikey) == 0 && len(user.OTPSecret) == 0 {
validotp = true
}
if len(user.Yubikey) > 0 && h.GetYubikeyAuth() != nil {
if len(bindSimplePw) > 44 {
otp := bindSimplePw[len(bindSimplePw)-44:]
yubikeyid := otp[0:12]
bindSimplePw = bindSimplePw[:len(bindSimplePw)-44]
if user.Yubikey == yubikeyid {
_, ok, _ := h.GetYubikeyAuth().Verify(otp)
if ok {
validotp = true
}
}
}
}
// Store the full bind password provided before possibly modifying
// in the otp check
untouchedBindSimplePw := bindSimplePw
// Test OTP if is exists
if len(user.OTPSecret) > 0 && !validotp {
if len(bindSimplePw) > 6 {
otp := bindSimplePw[len(bindSimplePw)-6:]
bindSimplePw = bindSimplePw[:len(bindSimplePw)-6]
validotp = totp.Validate(otp, user.OTPSecret)
}
}
// finally, validate user's pw
// check app passwords first
if user.PassAppBcrypt != nil {
for index, appPw := range user.PassAppBcrypt {
decoded, err := hex.DecodeString(appPw)
if err != nil {
h.GetLog().Info().Str("incorrect stored hash", "(omitted)").Msg("invalid app credentials")
} else {
if bcrypt.CompareHashAndPassword(decoded, []byte(untouchedBindSimplePw)) == nil {
stats.Frontend.Add("bind_successes", 1)
h.GetLog().Info().Int("index", index).Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("Bind success using app pw")
return ldap.LDAPResultSuccess, nil
}
}
}
}
if user.PassAppSHA256 != nil {
hashFull := sha256.New()
hashFull.Write([]byte(untouchedBindSimplePw))
for index, appPw := range user.PassAppSHA256 {
if appPw != hex.EncodeToString(hashFull.Sum(nil)) {
h.GetLog().Info().Int("index", index).Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("Attempt to bind app pw failed")
} else {
stats.Frontend.Add("bind_successes", 1)
h.GetLog().Info().Int("index", index).Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("Bind success using app pw")
return ldap.LDAPResultSuccess, nil
}
}
}
if user.PassAppCustom != nil {
err := user.PassAppCustom(user, untouchedBindSimplePw)
if err != nil {
h.GetLog().Info().Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Str("error", err.Error()).Msg("Attempt to bind app custom auth failed")
return ldap.LDAPResultInvalidCredentials, nil
}
stats.Frontend.Add("bind_successes", 1)
h.GetLog().Info().Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("Bind success using app custom auth")
return ldap.LDAPResultSuccess, nil
}
// Then ensure the OTP is valid before checking
if !validotp {
h.GetLog().Info().Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("invalid OTP token")
return ldap.LDAPResultInvalidCredentials, nil
}
// Now, check the pasword hash
if user.PassBcrypt != "" {
decoded, err := hex.DecodeString(user.PassBcrypt)
if err != nil {
h.GetLog().Info().Str("incorrect stored hash", "(omitted)").Msg("invalid credentials")
return ldap.LDAPResultInvalidCredentials, nil
}
if bcrypt.CompareHashAndPassword(decoded, []byte(bindSimplePw)) != nil {
h.GetLog().Info().Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("invalid credentials")
l.maybePutInTimeout(h, conn, true)
return ldap.LDAPResultInvalidCredentials, nil
}
}
if user.PassSHA256 != "" {
hash := sha256.New()
hash.Write([]byte(bindSimplePw))
if user.PassSHA256 != hex.EncodeToString(hash.Sum(nil)) {
h.GetLog().Info().Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("invalid credentials")
l.maybePutInTimeout(h, conn, true)
return ldap.LDAPResultInvalidCredentials, nil
}
}
stats.Frontend.Add("bind_successes", 1)
h.GetLog().Info().Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("Bind success")
return ldap.LDAPResultSuccess, nil
}
/*
* TODO #1:
* Is it possible to map, on-the-fly, ou= -> cn= to maintain backware compatibility? Could be a switch...
* Or maybe sinmply configure in the .cfg file using the nameformat and groupformat settings?
* In 3.0 we could change default from cn to ou
* TODO #2: DONE
* Returns values when scope==base or scope==sub on a group entry
* TODO #3: DONE
* Make sure that when scope==sub, we do not always return, but augment results instead
* TODO #4: DONE
* Handle groups as two distinct objectclasses like OLDAP does
* Q: Does OLDAP return the groups twice when querying root+sub?
* TODO #5:
* Document roll out of schemas
*/
func (l LDAPOpsHelper) Search(h LDAPOpsHandler, bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (result ldap.ServerSearchResult, err error) {
if l.isInTimeout(h, conn) {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultUnwillingToPerform}, fmt.Errorf("Source is in a timeout")
}
bindDN = strings.ToLower(bindDN)
baseDN := strings.ToLower(h.GetBackend().BaseDN)
searchBaseDN := strings.ToLower(searchReq.BaseDN)
anonymous := len(bindDN) < 1
var boundUser *config.User
var ldapcode ldap.LDAPResultCode
if !anonymous {
if bindDN, boundUser, ldapcode = l.searchCheckBindDN(h, baseDN, bindDN, anonymous); ldapcode != ldap.LDAPResultSuccess {
return ldap.ServerSearchResult{ResultCode: ldapcode}, fmt.Errorf("Search Error: Potential bypass of BindDN %s", bindDN)
}
}
h.GetLog().Info().Str("binddn", bindDN).Str("basedn", baseDN).Str("searchbasedn", searchBaseDN).Str("src", conn.RemoteAddr().String()).Int("scope", searchReq.Scope).Str("filter", searchReq.Filter).Msg("Search request")
stats.Frontend.Add("search_reqs", 1)
switch entries, ldapcode := l.searchMaybeRootDSEQuery(h, baseDN, searchBaseDN, searchReq, anonymous); ldapcode {
case ldap.LDAPResultUnwillingToPerform:
return ldap.ServerSearchResult{ResultCode: ldapcode}, fmt.Errorf("Search Error: No BaseDN provided")
case ldap.LDAPResultInsufficientAccessRights:
return ldap.ServerSearchResult{ResultCode: ldapcode}, fmt.Errorf("Root DSE Search Error: Anonymous BindDN not allowed %s", bindDN)
case ldap.LDAPResultSuccess:
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldapcode}, nil
}
// Past this point, there is no reason to allow anonymous searches
if anonymous {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: Anonymous BindDN not allowed %s", bindDN)
}
switch entries, ldapcode, attributename := l.searchMaybeSchemaQuery(h, baseDN, searchBaseDN, searchReq, anonymous); ldapcode {
case ldap.LDAPResultOperationsError:
return ldap.ServerSearchResult{ResultCode: ldapcode}, fmt.Errorf("Schema Error: attribute %s cannot be read", *attributename)
case ldap.LDAPResultSuccess:
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldapcode}, nil
}
// Past this further point, we are looking at tree searches... not all standard searches yet, though
// But first, let's only allow legal searches
if !strings.HasSuffix(bindDN, fmt.Sprintf(",%s", baseDN)) {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: BindDN %s not in our BaseDN %s", bindDN, h.GetBackend().BaseDN)
}
if !strings.HasSuffix(searchBaseDN, h.GetBackend().BaseDN) {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: search BaseDN %s is not in our BaseDN %s", searchBaseDN, h.GetBackend().BaseDN)
}
// Unless globally ignored, we will check that a user has capabilities allowing them to perform a search in the requested BaseDN
if !h.GetCfg().Behaviors.IgnoreCapabilities && !l.checkCapability(*boundUser, "search", []string{"*", searchBaseDN}) {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: no capability allowing BindDN %s to perform search in %s", bindDN, searchBaseDN)
}
switch entries, ldapcode := l.searchMaybeTopLevelNodes(h, baseDN, searchBaseDN, searchReq); ldapcode {
case ldap.LDAPResultSuccess:
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldapcode}, nil
}
switch entries, ldapcode := l.searchMaybeTopLevelGroupsNode(h, baseDN, searchBaseDN, searchReq); ldapcode {
case ldap.LDAPResultSuccess:
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldapcode}, nil
}
switch entries, ldapcode := l.searchMaybeTopLevelUsersNode(h, baseDN, searchBaseDN, searchReq); ldapcode {
case ldap.LDAPResultSuccess:
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldapcode}, nil
}
filterEntity, err := ldap.GetFilterObjectClass(searchReq.Filter)
if err != nil {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: error parsing filter: %s", searchReq.Filter)
}
switch entries, ldapcode := l.searchMaybePosixGroups(h, baseDN, searchBaseDN, searchReq, filterEntity); ldapcode {
case ldap.LDAPResultSuccess:
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldapcode}, nil
}
switch entries, ldapcode := l.searchMaybePosixAccounts(h, baseDN, searchBaseDN, searchReq, filterEntity); ldapcode {
case ldap.LDAPResultSuccess:
stats.Frontend.Add("search_successes", 1)
h.GetLog().Info().Str("filter", searchReq.Filter).Msg("AP: Search OK")
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldapcode}, nil
}
// So, this should be an ERROR condition! Right..?
entries := []*ldap.Entry{}
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldap.LDAPResultSuccess}, nil
}
// Returns: LDAPResultSuccess or any ldap code returned by findUser
func (l LDAPOpsHelper) searchCheckBindDN(h LDAPOpsHandler, baseDN string, bindDN string, anonymous bool) (newBindDN string, boundUser *config.User, ldapresultcode ldap.LDAPResultCode) {
boundUser, ldapcode := l.findUser(h, bindDN, false /* checkGroup */)
if ldapcode != ldap.LDAPResultSuccess {
return "", nil, ldapcode
}
// What if this user was bound using their UPN? We still want to enforce baseDN etc so we
// have to rewire them to their original DN which is of course a waste of cycles.
// TODO Down the road we would want to perform lightweight memoization of DNs to UPNs
if emailmatcher.MatchString(bindDN) {
// cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
bindDN = fmt.Sprintf("cn=%s,%s", boundUser.Name, baseDN)
}
return bindDN, boundUser, ldap.LDAPResultSuccess
}
// Search RootDSE and return information on the server
// Returns: LDAPResultSuccess, LDAPResultOther, LDAPResultUnwillingToPerform, LDAPResultInsufficientAccessRights
func (l LDAPOpsHelper) searchMaybeRootDSEQuery(h LDAPOpsHandler, baseDN string, searchBaseDN string, searchReq ldap.SearchRequest, anonymous bool) (resultentries []*ldap.Entry, ldapresultcode ldap.LDAPResultCode) {
if searchBaseDN != "" {
return nil, ldap.LDAPResultOther // OK
}
/// Only base scope searches allowed if no basedn is provided
if searchReq.Scope != ldap.ScopeBaseObject {
h.GetLog().Info().Interface("src", searchReq.Controls).Msg("Search Error: No BaseDN provided")
return nil, ldap.LDAPResultUnwillingToPerform // KO
}
if anonymous && !h.GetBackend().AnonymousDSE {
return nil, ldap.LDAPResultInsufficientAccessRights // KO
}
h.GetLog().Info().Str("special case", "root DSE").Msg("Search request")
entries := []*ldap.Entry{}
attrs := []*ldap.EntryAttribute{}
// unfortunately, objectClass is not to be included so we will respect that
// attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"*"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "supportedSASLMechanisms", Values: []string{}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "supportedLDAPVersion", Values: []string{"3"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "supportedControl", Values: []string{}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "supportedCapabilities", Values: []string{}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "subschemaSubentry", Values: []string{"cn=schema"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "serverName", Values: []string{"unknown"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "namingContexts", Values: []string{baseDN}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "defaultNamingContext", Values: []string{baseDN}})
attrs = l.collectRequestedAttributesBack(attrs, searchReq)
entries = append(entries, &ldap.Entry{DN: searchBaseDN, Attributes: attrs})
stats.Frontend.Add("search_successes", 1)
h.GetLog().Info().Str("filter", searchReq.Filter).Msg("AP: Root Search OK")
return entries, ldap.LDAPResultSuccess
}
// Search and return the information, after indirection from the RootDSE
// Returns: LDAPResultSuccess, LDAPResultOther, LDAPResultOperationsError
func (l LDAPOpsHelper) searchMaybeSchemaQuery(h LDAPOpsHandler, baseDN string, searchBaseDN string, searchReq ldap.SearchRequest, anonymous bool) (resultentries []*ldap.Entry, ldapresultcode ldap.LDAPResultCode, attributename *string) {
if searchBaseDN != "cn=schema" {
return nil, ldap.LDAPResultOther, nil // OK
}
h.GetLog().Info().Str("special case", "schema discovery").Msg("Search request")
entries := []*ldap.Entry{}
attrs := []*ldap.EntryAttribute{}
attrs = append(attrs, &ldap.EntryAttribute{Name: "cn", Values: []string{"schema"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "hasSubordinates", Values: []string{"false"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "modifiersName", Values: []string{"cn=Directory Manager"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "modifyTimeStamp", Values: []string{"Mar 8, 2021, 12:46:29 PM PST (20210308204629Z)"}})
// Iterate through schema attributes provided in schema/ directory
filenames, _ := os.ReadDir("schema")
for _, filename := range filenames {
attributename := new(string)
*attributename = filename.Name()
file, err := os.Open(filepath.Join("schema", *attributename))
if err != nil {
return nil, ldap.LDAPResultOperationsError, attributename
}
defer file.Close()
values := []string{}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
values = append(values, line)
}
attrs = append(attrs, &ldap.EntryAttribute{Name: filename.Name(), Values: values})
}
attrs = l.collectRequestedAttributesBack(attrs, searchReq)
entries = append(entries, &ldap.Entry{DN: searchBaseDN, Attributes: attrs})
stats.Frontend.Add("search_successes", 1)
h.GetLog().Info().Str("filter", searchReq.Filter).Msg("AP: Schema Discovery OK")
return entries, ldap.LDAPResultSuccess, nil
}
// Retrieve the top-levell nodes, i.e. the baseDN, groups, members...
// Returns: LDAPResultSuccess, LDAPResultOther
func (l LDAPOpsHelper) searchMaybeTopLevelNodes(h LDAPOpsHandler, baseDN string, searchBaseDN string, searchReq ldap.SearchRequest) (resultentries []*ldap.Entry, ldapresultcode ldap.LDAPResultCode) {
if baseDN != searchBaseDN {
return nil, ldap.LDAPResultOther // OK
}
h.GetLog().Info().Str("special case", "top-level browse").Msg("Search request")
entries := []*ldap.Entry{}
if searchReq.Scope == ldap.ScopeBaseObject || searchReq.Scope == ldap.ScopeWholeSubtree {
entries = append(entries, l.topLevelRootNode(searchBaseDN))
}
entries = append(entries, l.topLevelGroupsNode(searchBaseDN, "groups"))
entries = append(entries, l.topLevelUsersNode(searchBaseDN))
if searchReq.Scope == ldap.ScopeWholeSubtree {
groupentries, err := h.FindPosixGroups("ou=users")
if err != nil {
return nil, ldap.LDAPResultOperationsError
}
entries = append(entries, groupentries...)
userentries, err := h.FindPosixAccounts("ou=users")
if err != nil {
return nil, ldap.LDAPResultOperationsError
}
entries = append(entries, userentries...)
}
stats.Frontend.Add("search_successes", 1)
h.GetLog().Info().Str("filter", searchReq.Filter).Msg("AP: Top-Level Browse OK")
return entries, ldap.LDAPResultSuccess
}
// Search starting from and including the ou=groups node
// Returns: LDAPResultSuccess, LDAPResultOther
func (l LDAPOpsHelper) searchMaybeTopLevelGroupsNode(h LDAPOpsHandler, baseDN string, searchBaseDN string, searchReq ldap.SearchRequest) (resultentries []*ldap.Entry, ldapresultcode ldap.LDAPResultCode) {
if searchBaseDN != fmt.Sprintf("ou=groups,%s", baseDN) {
return nil, ldap.LDAPResultOther // OK
}
h.GetLog().Info().Str("special case", "top-level groups node").Msg("Search request")
entries := []*ldap.Entry{}
if searchReq.Scope == ldap.ScopeBaseObject || searchReq.Scope == ldap.ScopeWholeSubtree {
entries = append(entries, l.topLevelGroupsNode(searchBaseDN, "groups"))
}
if searchReq.Scope == ldap.ScopeSingleLevel || searchReq.Scope == ldap.ScopeWholeSubtree {
groupentries, err := h.FindPosixGroups("ou=groups")
if err != nil {
return nil, ldap.LDAPResultOperationsError
}
entries = append(entries, groupentries...)
}
stats.Frontend.Add("search_successes", 1)
h.GetLog().Info().Str("filter", searchReq.Filter).Msg("AP: Top-Level Groups Browse OK")
return entries, ldap.LDAPResultSuccess
}
// Search starting from and including the ou=users node
// Returns: LDAPResultSuccess, LDAPResultOther
func (l LDAPOpsHelper) searchMaybeTopLevelUsersNode(h LDAPOpsHandler, baseDN string, searchBaseDN string, searchReq ldap.SearchRequest) (resultentries []*ldap.Entry, ldapresultcode ldap.LDAPResultCode) {
if searchBaseDN != fmt.Sprintf("ou=users,%s", baseDN) {
return nil, ldap.LDAPResultOther // OK
}
h.GetLog().Info().Str("special case", "top-level users node").Msg("Search request")
entries := []*ldap.Entry{}
if searchReq.Scope == ldap.ScopeBaseObject || searchReq.Scope == ldap.ScopeWholeSubtree {
entries = append(entries, l.topLevelUsersNode(searchBaseDN))
}
if searchReq.Scope == ldap.ScopeSingleLevel || searchReq.Scope == ldap.ScopeWholeSubtree {
groupentries, err := h.FindPosixGroups("ou=users")
if err != nil {
return nil, ldap.LDAPResultOperationsError
}
entries = append(entries, groupentries...)
}
if searchReq.Scope == ldap.ScopeWholeSubtree {
userentries, err := h.FindPosixAccounts("ou=users")
if err != nil {
return nil, ldap.LDAPResultOperationsError
}
entries = append(entries, userentries...)
}
stats.Frontend.Add("search_successes", 1)
h.GetLog().Info().Str("filter", searchReq.Filter).Msg("AP: Top-Level Users Browse OK")
return entries, ldap.LDAPResultSuccess
}
// Look up posixgroup entries, either through objectlass or parent is ou=groups or ou=users
// Returns: LDAPResultSuccess, LDAPResultOther, LDAPResultOperationsError
func (l LDAPOpsHelper) searchMaybePosixGroups(h LDAPOpsHandler, baseDN string, searchBaseDN string, searchReq ldap.SearchRequest, filterEntity string) (resultentries []*ldap.Entry, ldapresultcode ldap.LDAPResultCode) {
hierarchy := "ou=groups"
if filterEntity != "posixgroup" {
bits := strings.Split(strings.Replace(searchBaseDN, baseDN, "", 1), ",")
if len(bits) != 3 || (bits[1] != "ou=groups" && bits[1] != "ou=users") {
return nil, ldap.LDAPResultOther // OK
}
hierarchy = bits[1]
}
h.GetLog().Info().Str("special case", "posix groups").Msg("Search request")
entries := []*ldap.Entry{}
if searchReq.Scope == ldap.ScopeBaseObject || searchReq.Scope == ldap.ScopeWholeSubtree {
groupentries, err := h.FindPosixGroups(hierarchy)
if err != nil {
return nil, ldap.LDAPResultOperationsError
}
entries = append(entries, l.preFilterEntries(searchBaseDN, groupentries)...)
}
if searchReq.Scope == ldap.ScopeSingleLevel || searchReq.Scope == ldap.ScopeWholeSubtree {
if hierarchy == "ou=users" {
userentries, err := h.FindPosixAccounts("ou=users")
if err != nil {
return nil, ldap.LDAPResultOperationsError
}
entries = append(entries, l.preFilterEntries(searchBaseDN, userentries)...)
}
}
stats.Frontend.Add("search_successes", 1)
h.GetLog().Info().Str("filter", searchReq.Filter).Msg("AP: Posix Groups Search OK")
return entries, ldap.LDAPResultSuccess
}
// Lookup posixaccount entries
// Returns: LDAPResultSuccess, LDAPResultOther, LDAPResultOperationsError
// This function ignores scopes... for now
func (l LDAPOpsHelper) searchMaybePosixAccounts(h LDAPOpsHandler, baseDN string, searchBaseDN string, searchReq ldap.SearchRequest, filterEntity string) (resultentries []*ldap.Entry, ldapresultcode ldap.LDAPResultCode) {
switch filterEntity {
case "posixaccount", "shadowaccount", "":
h.GetLog().Info().Str("default case", filterEntity).Msg("Search request")
default:
return nil, ldap.LDAPResultOther // OK
}
// FixUp: we may be in the process of browsing users from a group ou
hierarchyString := ""
if strings.HasSuffix(searchBaseDN, fmt.Sprintf("ou=users,%s", baseDN)) {
hierarchyString = "ou=users"
}
unscopedEntries, err := h.FindPosixAccounts(hierarchyString)
if err != nil {
return nil, ldap.LDAPResultOperationsError
}
// Filter out entries, that are not in the search base dn
entries := []*ldap.Entry{}
for _, e := range unscopedEntries {
if strings.HasSuffix(e.DN, searchBaseDN) {
entries = append(entries, e)
}
}
stats.Frontend.Add("search_successes", 1)
h.GetLog().Info().Str("filter", searchReq.Filter).Msg("AP: Account Search OK")
return entries, ldap.LDAPResultSuccess
}
func (l LDAPOpsHelper) topLevelRootNode(searchBaseDN string) *ldap.Entry {
attrs := []*ldap.EntryAttribute{}
dnBits := strings.Split(searchBaseDN, ",")
for _, dnBit := range dnBits {
chunk := strings.Split(dnBit, "=")
attrs = append(attrs, &ldap.EntryAttribute{Name: chunk[0], Values: []string{chunk[1]}})
}
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"organizationalUnit", "dcObject", "top"}})
return &ldap.Entry{DN: searchBaseDN, Attributes: attrs}
}
func (l LDAPOpsHelper) topLevelGroupsNode(searchBaseDN string, hierarchy string) *ldap.Entry {
attrs := []*ldap.EntryAttribute{}
attrs = append(attrs, &ldap.EntryAttribute{Name: "ou", Values: []string{"groups"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"organizationalUnit", "top"}})
hierarchyStringPrefix := fmt.Sprintf("ou=%s,", hierarchy)
dn := searchBaseDN
if !strings.HasPrefix(dn, hierarchyStringPrefix) {
dn = fmt.Sprintf("%s%s", hierarchyStringPrefix, dn)
}
return &ldap.Entry{DN: dn, Attributes: attrs}
}
func (l LDAPOpsHelper) topLevelUsersNode(searchBaseDN string) *ldap.Entry {
attrs := []*ldap.EntryAttribute{}
attrs = append(attrs, &ldap.EntryAttribute{Name: "ou", Values: []string{"users"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"organizationalUnit", "top"}})
dn := searchBaseDN
if !strings.HasPrefix(dn, "ou=users,") {
dn = fmt.Sprintf("ou=users,%s", dn)
}
return &ldap.Entry{DN: dn, Attributes: attrs}
}
// I am not quite sure why but I found out that, maybe due to my playing around with their DN,
// querying groups and users under a certain node (e.g. ou=users) with a scope of "sub"
// (and only in this scenario!) will defeat the LDAP library's filtering capabilities.
// Some day, hopefully, I'll fix this directly in the library.
func (l LDAPOpsHelper) preFilterEntries(searchBaseDN string, entries []*ldap.Entry) (resultentries []*ldap.Entry) {
filteredEntries := []*ldap.Entry{}
for _, entry := range entries {
if strings.HasSuffix(entry.DN, searchBaseDN) {
filteredEntries = append(filteredEntries, entry)
}
}
return filteredEntries
}
func (l LDAPOpsHelper) findUser(h LDAPOpsHandler, bindDN string, checkGroup bool) (userWhenFound *config.User, resultCode ldap.LDAPResultCode) {
var user config.User
baseDN := strings.ToLower("," + h.GetBackend().BaseDN)
// Special Case: bind using UPN
// Not using mail.ParseAddress/1 because we would allow incorrectly formatted UPNs
if emailmatcher.MatchString(bindDN) {
var foundUser bool // = false
foundUser, user, _ = h.FindUser(bindDN, true)
if !foundUser {
h.GetLog().Info().Str("userprincipalname", bindDN).Msg("User not found")
return nil, ldap.LDAPResultInvalidCredentials
}
} else {
// parse the bindDN - ensure that the bindDN ends with the BaseDN
if !strings.HasSuffix(bindDN, baseDN) {
h.GetLog().Info().Str("binddn", bindDN).Str("basedn", h.GetBackend().BaseDN).Msg("BindDN not part of our BaseDN")
// h.GetLog().Warning(fmt.Sprintf("Bind Error: BindDN %s not our BaseDN %s", bindDN, baseDN))
return nil, ldap.LDAPResultInvalidCredentials
}
parts := strings.Split(strings.TrimSuffix(bindDN, baseDN), ",")
groupName := ""
userName := ""
if len(parts) == 1 {
userName = strings.TrimPrefix(parts[0], h.GetBackend().NameFormat+"=")
} else if len(parts) == 2 || (len(parts) == 3 && parts[2] == "ou=users") {
userName = strings.TrimPrefix(parts[0], h.GetBackend().NameFormat+"=")
groupName = strings.TrimPrefix(parts[1], h.GetBackend().GroupFormat+"=")
} else {
h.GetLog().Info().Str("binddn", bindDN).Int("numparts", len(parts)).Msg("BindDN should have only one or two parts")
for _, part := range parts {
h.GetLog().Info().Str("part", part).Msg("Parts")
}
return nil, ldap.LDAPResultInvalidCredentials
}
// find the user
var foundUser bool // = false
foundUser, user, _ = h.FindUser(userName, false)
if !foundUser {
h.GetLog().Info().Str("username", userName).Msg("User not found")
return nil, ldap.LDAPResultInvalidCredentials
}
if checkGroup {
// find the group
var group config.Group // = nil
var foundGroup bool // = false
if groupName != "" {
foundGroup, group, _ = h.FindGroup(groupName)
if !foundGroup {
h.GetLog().Info().Str("groupname", groupName).Msg("Group not found")
return nil, ldap.LDAPResultInvalidCredentials
}
}
// validate group membership
if foundGroup {
if user.PrimaryGroup != group.GIDNumber {
h.GetLog().Info().Str("username", userName).Int("primarygroup", user.PrimaryGroup).Int("groupid", group.GIDNumber).Msg("primary group mismatch")
return nil, ldap.LDAPResultInvalidCredentials
}
}
}
}
return &user, ldap.LDAPResultSuccess
}
func (l LDAPOpsHelper) checkCapability(user config.User, action string, objects []string) bool {
for _, capability := range user.Capabilities {
if capability.Action == action {
for _, object := range objects {
if capability.Object == object {
return true
}
}
}
}
return false
}
// If your query is for, say 'objectClass', then our LDAP
// library will weed out this entry since it does *not* contain an objectclass attribute
// so we are going to re-inject it to keep the LDAP library happy
func (l LDAPOpsHelper) collectRequestedAttributesBack(attrs []*ldap.EntryAttribute, searchReq ldap.SearchRequest) []*ldap.EntryAttribute {
attbits := configattributematcher.FindStringSubmatch(searchReq.Filter)
if len(attbits) == 3 {
foundattname := false
for _, attr := range attrs {
if strings.ToLower(attr.Name) == strings.ToLower(attbits[1]) {
foundattname = true
break
}
}
// the ugly hack: we are going to pretend that the requested attribute is in there
if !foundattname {
attrs = append(attrs, &ldap.EntryAttribute{Name: attbits[1], Values: []string{attbits[2]}})
}
}
return attrs
}
// return true if we should not process the current operation
func (l LDAPOpsHelper) isInTimeout(handler LDAPOpsHandler, conn net.Conn) bool {
cfg := handler.GetCfg()
if !cfg.Behaviors.LimitFailedBinds {
return false
}
remoteAddr := l.getAddr(conn)
now := time.Now()
info, ok := l.sources[remoteAddr]
if !ok {
l.sources[remoteAddr] = &sourceInfo{
lastSeen: now,
failures: make(chan failedBind, cfg.Behaviors.NumberOfFailedBinds),
waitUntil: now,
}
return false
}
// update so that this source does not get pruned
info.lastSeen = now
// if we are in a time out...
if cfg.Behaviors.LimitFailedBinds && info.waitUntil.After(now) {
return true
}
return false
}
func (l LDAPOpsHelper) maybePutInTimeout(handler LDAPOpsHandler, conn net.Conn, noteFailure bool) bool {
cfg := handler.GetCfg()
if !cfg.Behaviors.LimitFailedBinds {
return false
}
remoteAddr := l.getAddr(conn)
now := time.Now()
info, _ := l.sources[remoteAddr]
// if we have a failed bind...
if noteFailure {
info.failures <- failedBind{ts: time.Now()}
// if we now have 3 failed binds in a row
if len(info.failures) == cfg.Behaviors.NumberOfFailedBinds {
// we cannot have more than 3 failed binds in our channel so pop the oldest one
pruned := <-info.failures
// if we have 3 failed bind in a row in less than 3 seconds
if pruned.ts.Add(cfg.Behaviors.PeriodOfFailedBinds * time.Second).After(now) {
// we will wait for 'n' seconds no matter what happens next
info.waitUntil = time.Now().Add(cfg.Behaviors.BlockFailedBindsFor * time.Second)
// purge our failure queue until we resume accepting operations
for len(info.failures) > 0 {
<-info.failures
}
}
}
}
// Prune old IPs
// TODO We should ensure that the time between prunings is bigger than the time to determine rapid failed binds
if l.nextPruning.Before(now) {
for sourceIP, sourceInfo := range l.sources {
if sourceInfo.lastSeen.Add(cfg.Behaviors.PruneSourcesOlderThan * time.Second).Before(now) {
delete(l.sources, sourceIP)
}
}
l.nextPruning = time.Now().Add(cfg.Behaviors.PruneSourceTableEvery * time.Second)
}
return false
}
func (l LDAPOpsHelper) getAddr(conn net.Conn) string {
fullAddr := conn.RemoteAddr().String()
sep := strings.LastIndex(fullAddr, ":")
if sep == -1 {
return fullAddr
}
return fullAddr[0:sep]
}
+112
View File
@@ -0,0 +1,112 @@
package handler
import (
"context"
"github.com/rs/zerolog"
"github.com/GeertJohan/yubigo"
"github.com/glauth/glauth/v2/internal/monitoring"
"github.com/glauth/glauth/v2/pkg/config"
)
// Option defines a single option function.
type Option func(o *Options)
// Options defines the available options for this package.
type Options struct {
Backend config.Backend
Handlers HandlerWrapper
Logger *zerolog.Logger
Config *config.Config
Context *context.Context
YubiAuth *yubigo.YubiAuth
Helper Handler
LDAPHelper LDAPOpsHelper
Monitor monitoring.MonitorInterface
}
// newOptions initializes the available default options.
func newOptions(opts ...Option) Options {
opt := Options{}
for _, o := range opts {
o(&opt)
}
return opt
}
// NewOptions initializes the available default options.
func NewOptions(opts ...Option) Options {
opt := Options{}
for _, o := range opts {
o(&opt)
}
return opt
}
// Backend is our current backend
func Backend(val config.Backend) Option {
return func(o *Options) {
o.Backend = val
}
}
// Handlers Our friendly handlers for all backends
func Handlers(val HandlerWrapper) Option {
return func(o *Options) {
o.Handlers = val
}
}
// Logger provides a function to set the logger option.
func Logger(val *zerolog.Logger) Option {
return func(o *Options) {
o.Logger = val
}
}
// Config provides a function to set the config option.
func Config(val *config.Config) Option {
return func(o *Options) {
o.Config = val
}
}
// Context provides a function to set the context option.
func Context(val *context.Context) Option {
return func(o *Options) {
o.Context = val
}
}
// YubiAuth provides a function to set the yubiauth option.
func YubiAuth(val *yubigo.YubiAuth) Option {
return func(o *Options) {
o.YubiAuth = val
}
}
// Helper If we specified a helper, for instance for OTP injection
func Helper(val Handler) Option {
return func(o *Options) {
o.Helper = val
}
}
// LDAPHelper Global LDAP Handler
func LDAPHelper(val LDAPOpsHelper) Option {
return func(o *Options) {
o.LDAPHelper = val
}
}
// Monitor provides a function to set the monitor option.
func Monitor(val monitoring.MonitorInterface) Option {
return func(o *Options) {
o.Monitor = val
}
}
+418
View File
@@ -0,0 +1,418 @@
package handler
import (
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"strings"
"sync"
"time"
"github.com/rs/zerolog"
"github.com/glauth/glauth/v2/internal/monitoring"
"github.com/glauth/glauth/v2/pkg/config"
"github.com/glauth/glauth/v2/pkg/stats"
"github.com/glauth/ldap"
msgraph "github.com/yaegashi/msgraph.go/v1.0"
)
type ownCloudSession struct {
log *zerolog.Logger
client *http.Client
user string
password string
endpoint string
useGraphAPI bool
}
type ownCloudHandler struct {
backend config.Backend
log *zerolog.Logger
client *http.Client
sessions map[string]ownCloudSession
lock *sync.Mutex
monitor monitoring.MonitorInterface
}
// global lock for ownCloudHandler sessions & servers manipulation
var ownCloudLock sync.Mutex
func (h ownCloudHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (result ldap.LDAPResultCode, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "bind", "status": fmt.Sprintf("%v", result)},
time.Since(start).Seconds(),
)
}()
bindDN = strings.ToLower(bindDN)
baseDN := strings.ToLower("," + h.backend.BaseDN)
h.log.Debug().Str("binddn", bindDN).Str("basedn", h.backend.BaseDN).Str("src", conn.RemoteAddr().String()).Msg("Bind request")
stats.Frontend.Add("bind_reqs", 1)
// parse the bindDN - ensure that the bindDN ends with the BaseDN
if !strings.HasSuffix(bindDN, baseDN) {
h.log.Warn().Str("binddn", bindDN).Str("basedn", h.backend.BaseDN).Msg("BindDN not part of our BaseDN")
return ldap.LDAPResultInvalidCredentials, nil
}
parts := strings.Split(strings.TrimSuffix(bindDN, baseDN), ",")
if len(parts) > 2 {
h.log.Warn().Str("binddn", bindDN).Int("numparts", len(parts)).Msg("BindDN should have only one or two parts")
return ldap.LDAPResultInvalidCredentials, nil
}
userName := strings.TrimPrefix(parts[0], "cn=")
// try to login
if !h.login(userName, bindSimplePw) {
h.log.Warn().Str("username", userName).Str("basedn", h.backend.BaseDN).Msg("Login failed")
return ldap.LDAPResultInvalidCredentials, nil
}
// TODO reuse HTTP connection
id := connID(conn)
s := ownCloudSession{
log: h.log,
user: userName,
password: bindSimplePw,
endpoint: h.backend.Servers[0],
useGraphAPI: h.backend.UseGraphAPI,
client: h.client,
}
h.lock.Lock()
h.sessions[id] = s
h.lock.Unlock()
stats.Frontend.Add("bind_successes", 1)
h.log.Debug().Str("binddn", bindDN).Str("basedn", h.backend.BaseDN).Str("src", conn.RemoteAddr().String()).Msg("Bind success")
return ldap.LDAPResultSuccess, nil
}
func (h ownCloudHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (result ldap.ServerSearchResult, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "search", "status": fmt.Sprintf("%v", result.ResultCode)},
time.Since(start).Seconds(),
)
}()
bindDN = strings.ToLower(bindDN)
baseDN := strings.ToLower("," + h.backend.BaseDN)
searchBaseDN := strings.ToLower(searchReq.BaseDN)
h.log.Debug().Str("binddn", bindDN).Str("basedn", baseDN).Str("src", conn.RemoteAddr().String()).Str("filter", searchReq.Filter).Msg("Search request")
stats.Frontend.Add("search_reqs", 1)
// validate the user is authenticated and has appropriate access
if len(bindDN) < 1 {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("search error: Anonymous BindDN not allowed %s", bindDN)
}
if !strings.HasSuffix(bindDN, baseDN) {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("search error: BindDN %s not in our BaseDN %s", bindDN, h.backend.BaseDN)
}
if !strings.HasSuffix(searchBaseDN, h.backend.BaseDN) {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("search error: search BaseDN %s is not in our BaseDN %s", searchBaseDN, h.backend.BaseDN)
}
// return all users in the config file - the LDAP library will filter results for us
entries := []*ldap.Entry{}
filterEntity, err := ldap.GetFilterObjectClass(searchReq.Filter)
if err != nil {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("search error: error parsing filter: %s", searchReq.Filter)
}
h.lock.Lock()
id := connID(conn)
session := h.sessions[id]
h.lock.Unlock()
switch filterEntity {
default:
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("search error: unhandled filter type: %s [%s]", filterEntity, searchReq.Filter)
case "posixgroup":
groups, err := session.getGroups()
if err != nil {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, errors.New("search error: error getting groups")
}
for _, g := range groups {
attrs := []*ldap.EntryAttribute{}
attrs = append(attrs, &ldap.EntryAttribute{Name: h.backend.GroupFormat, Values: []string{*g.ID}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "description", Values: []string{fmt.Sprintf("%s from ownCloud", *g.ID)}})
// attrs = append(attrs, &ldap.EntryAttribute{"gidNumber", []string{fmt.Sprintf("%d", g.GIDNumber)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixGroup"}})
if g.Members != nil {
members := make([]string, len(g.Members))
for i, v := range g.Members {
members[i] = *v.ID
}
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberUid", Values: members})
}
dn := fmt.Sprintf("%s=%s,ou=groups,%s", h.backend.GroupFormat, *g.ID, h.backend.BaseDN)
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
}
case "posixaccount", "":
userName := ""
if searchBaseDN != strings.ToLower(h.backend.BaseDN) {
parts := strings.Split(strings.TrimSuffix(searchBaseDN, baseDN), ",")
if len(parts) >= 1 {
userName = strings.TrimPrefix(parts[0], "cn=")
}
}
users, err := session.getUsers(userName)
if err != nil {
h.log.Debug().Str("username", userName).Err(err).Msg("Could not get user")
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, errors.New("search error: error getting users")
}
for _, u := range users {
attrs := []*ldap.EntryAttribute{}
attrs = append(attrs, &ldap.EntryAttribute{Name: h.backend.NameFormat, Values: []string{*u.ID}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "uid", Values: []string{*u.ID}})
if u.DisplayName != nil {
attrs = append(attrs, &ldap.EntryAttribute{Name: "givenName", Values: []string{*u.DisplayName}})
}
if u.Mail != nil {
attrs = append(attrs, &ldap.EntryAttribute{Name: "mail", Values: []string{*u.Mail}})
}
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixAccount"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "description", Values: []string{fmt.Sprintf("%s from ownCloud", *u.ID)}})
dn := fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormat, *u.ID, h.backend.GroupFormat, "users", h.backend.BaseDN)
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
}
}
stats.Frontend.Add("search_successes", 1)
h.log.Debug().Str("filter", searchReq.Filter).Msg("AP: Search OK")
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldap.LDAPResultSuccess}, nil
}
// Add is not yet supported for the owncloud backend
func (h ownCloudHandler) Add(boundDN string, req ldap.AddRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "add", "status": fmt.Sprintf("%v", result)},
time.Since(start).Seconds(),
)
}()
return ldap.LDAPResultInsufficientAccessRights, nil
}
// Modify is not yet supported for the owncloud backend
func (h ownCloudHandler) Modify(boundDN string, req ldap.ModifyRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "modify", "status": fmt.Sprintf("%v", result)},
time.Since(start).Seconds(),
)
}()
return ldap.LDAPResultInsufficientAccessRights, nil
}
// Delete is not yet supported for the owncloud backend
func (h ownCloudHandler) Delete(boundDN string, deleteDN string, conn net.Conn) (result ldap.LDAPResultCode, err error) {
start := time.Now()
defer func() {
h.monitor.SetResponseTimeMetric(
map[string]string{"operation": "delete", "status": fmt.Sprintf("%v", result)},
time.Since(start).Seconds(),
)
}()
return ldap.LDAPResultInsufficientAccessRights, nil
}
// FindUser with the given username. Called by the ldap backend to authenticate the bind. Optional
func (h ownCloudHandler) FindUser(userName string, searchByUPN bool) (found bool, user config.User, err error) {
return false, config.User{}, nil
}
func (h ownCloudHandler) FindGroup(groupName string) (found bool, group config.Group, err error) {
return false, config.Group{}, nil
}
func (h ownCloudHandler) Close(boundDN string, conn net.Conn) error {
conn.Close() // close connection to the server when then client is closed
h.lock.Lock()
defer h.lock.Unlock()
delete(h.sessions, connID(conn))
stats.Frontend.Add("closes", 1)
stats.Backend.Add("closes", 1)
return nil
}
func (h ownCloudHandler) login(name, pw string) bool {
var req *http.Request
if h.backend.UseGraphAPI {
// TODO oc10 graphapi app should implement /me
req, _ = http.NewRequest("GET", h.backend.Servers[0]+"/users/"+name, nil)
} else {
// use provisioning api
meURL := fmt.Sprintf("%s/ocs/v2.php/cloud/user?format=json", h.backend.Servers[0])
req, _ = http.NewRequest("GET", meURL, nil)
}
req.SetBasicAuth(name, pw)
resp, err := h.client.Do(req)
if err != nil || resp.StatusCode != http.StatusOK {
h.log.Error().Err(err).Int("status", resp.StatusCode).Msg("failed login")
return false
}
defer resp.Body.Close()
return true
}
type OCSGroupsResponse struct {
Ocs struct {
Meta struct {
Message interface{} `json:"message"`
Statuscode int `json:"statuscode"`
Status string `json:"status"`
} `json:"meta"`
Data struct {
Groups []string `json:"groups"`
} `json:"data"`
} `json:"ocs"`
}
func (s ownCloudSession) getGroups() ([]msgraph.Group, error) {
if s.useGraphAPI {
ctx := context.Background()
req := s.NewClient().Groups().Request()
req.Expand("members")
return req.Get(ctx)
}
groupsUrl := fmt.Sprintf("%s/ocs/v2.php/cloud/groups?format=json", s.endpoint)
req, _ := http.NewRequest("GET", groupsUrl, nil)
req.SetBasicAuth(s.user, s.password)
resp, err := s.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var f OCSGroupsResponse
if err := json.Unmarshal(b, &f); err != nil {
return nil, err
}
ret := make([]msgraph.Group, len(f.Ocs.Data.Groups))
for i := range f.Ocs.Data.Groups {
ret[i] = msgraph.Group{DirectoryObject: msgraph.DirectoryObject{Entity: msgraph.Entity{ID: &f.Ocs.Data.Groups[i]}}}
}
return ret, nil
}
type OCSUsersResponse struct {
Ocs struct {
Data struct {
Users []string `json:"users"`
} `json:"data"`
Meta struct {
Statuscode int `json:"statuscode"`
Message interface{} `json:"message"`
Status string `json:"status"`
} `json:"meta"`
} `json:"ocs"`
}
// NewClient returns GraphService request builder with default base URL
func (s ownCloudSession) NewClient() *msgraph.GraphServiceRequestBuilder {
httpClient := &http.Client{
Transport: s,
}
g := msgraph.NewClient(httpClient)
g.SetURL(s.endpoint)
return g
}
func (s ownCloudSession) RoundTrip(req *http.Request) (*http.Response, error) {
req.SetBasicAuth(s.user, s.password)
return s.client.Transport.RoundTrip(req)
}
func (s ownCloudSession) getUsers(userName string) ([]msgraph.User, error) {
if s.useGraphAPI {
s.log.Debug().Msg("using graph api")
ctx := context.Background()
req := s.NewClient().Users()
if len(userName) > 0 {
s.log.Debug().Msg("fetching single user")
u, err := req.ID(userName).Request().Get(ctx)
if err != nil {
return nil, err
}
return []msgraph.User{*u}, nil
}
s.log.Debug().Msg("fetching all users")
return req.Request().Get(ctx)
}
s.log.Debug().Msg("using provisioning api")
usersUrl := fmt.Sprintf("%s/ocs/v2.php/cloud/users?format=json", s.endpoint)
req, _ := http.NewRequest("GET", usersUrl, nil)
req.SetBasicAuth(s.user, s.password)
resp, err := s.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var f OCSUsersResponse
if err := json.Unmarshal(b, &f); err != nil {
return nil, err
}
ret := make([]msgraph.User, len(f.Ocs.Data.Users))
for i := range f.Ocs.Data.Users {
ret[i] = msgraph.User{
DirectoryObject: msgraph.DirectoryObject{
Entity: msgraph.Entity{ID: &f.Ocs.Data.Users[i]},
},
}
}
return ret, nil
}
func (s ownCloudSession) redirectPolicyFunc(req *http.Request, via []*http.Request) error {
s.log.Debug().Str("username", s.user).Msg("Setting user and password")
req.SetBasicAuth(s.user, s.password)
return nil
}
func NewOwnCloudHandler(opts ...Option) Handler {
options := newOptions(opts...)
return ownCloudHandler{
backend: options.Backend,
log: options.Logger,
sessions: make(map[string]ownCloudSession),
lock: &ownCloudLock,
client: &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: options.Backend.Insecure,
},
},
},
monitor: options.Monitor,
}
}
+85
View File
@@ -0,0 +1,85 @@
//go:build !windows
// +build !windows
package logging
import (
"fmt"
"io"
"log"
"log/syslog"
"os"
"regexp"
"strings"
"time"
"github.com/rs/zerolog"
)
// We will use this package to wrap log messages coming from libraries who have no interest
// in generating structured output.
var (
ldapliblogmatcher = regexp.MustCompile(`^\d{4}\/\d{1,2}\/\d{1,2} \d{1,2}\:\d{1,2}\:\d{1,2} `)
)
func InitLogging(reqdebug bool, reqsyslog bool, reqstructlog bool) zerolog.Logger {
var level zerolog.Level
if reqdebug {
level = zerolog.DebugLevel
} else {
level = zerolog.InfoLevel
}
var mainWriter io.Writer
if reqstructlog {
// Vroom vroom
mainWriter = os.Stderr
zerolog.TimeFieldFormat = time.RFC1123Z
} else {
// This is the inefficient writer
mainWriter = zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC1123Z}
}
var logr zerolog.Logger
if reqsyslog {
s, err := syslog.New(syslog.LOG_INFO, "glauth")
if err != nil {
fmt.Println("Unable to write to syslog: ignoring...")
reqsyslog = false
} else {
writers := zerolog.MultiLevelWriter(mainWriter, zerolog.SyslogLevelWriter(s))
logr = zerolog.New(writers).Level(level).With().Timestamp().Logger()
}
}
if !reqsyslog {
logr = zerolog.New(mainWriter).Level(level).With().Timestamp().Logger()
}
log.SetOutput(customWriter{logr: logr, structlog: reqstructlog})
return logr
}
type customWriter struct {
logr zerolog.Logger
structlog bool
}
func (e customWriter) Write(p []byte) (int, error) {
submatchall := ldapliblogmatcher.FindAllString(string(p), 1)
var msg string
for _, element := range submatchall {
msg = strings.TrimSpace(string(p[len(element):]))
}
if msg == "" {
msg = strings.TrimSpace(string(p))
}
if e.structlog {
fmt.Fprintf(os.Stderr, "{\"level\":\"info\",\"time\":\"%s\",\"message\":\"%s\"}\n", time.Now().Format(time.RFC1123Z), strings.Replace(strings.TrimSpace(msg), `"`, `\"`, -1))
} else {
e.logr.Info().Msg(msg)
}
return len(p), nil
}
+70
View File
@@ -0,0 +1,70 @@
//go:build windows
// +build windows
package logging
import (
"fmt"
"io"
"log"
"os"
"regexp"
"strings"
"time"
"github.com/rs/zerolog"
)
// We will use this package to wrap log messages coming from libraries who have no interest
// in generating structured output.
var (
ldapliblogmatcher = regexp.MustCompile(`^\d{4}\/\d{1,2}\/\d{1,2} \d{1,2}\:\d{1,2}\:\d{1,2} `)
)
func InitLogging(reqdebug bool, reqsyslog bool, reqstructlog bool) zerolog.Logger {
var level zerolog.Level
if reqdebug {
level = zerolog.DebugLevel
} else {
level = zerolog.InfoLevel
}
var mainWriter io.Writer
if reqstructlog {
// Vroom vroom
mainWriter = os.Stderr
zerolog.TimeFieldFormat = time.RFC1123Z
} else {
// This is the inefficient writer
mainWriter = zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC1123Z}
}
logr := zerolog.New(mainWriter).Level(level).With().Timestamp().Logger()
log.SetOutput(customWriter{logr: logr, structlog: reqstructlog})
return logr
}
type customWriter struct {
logr zerolog.Logger
structlog bool
}
func (e customWriter) Write(p []byte) (int, error) {
submatchall := ldapliblogmatcher.FindAllString(string(p), 1)
var msg string
for _, element := range submatchall {
msg = strings.TrimSpace(string(p[len(element):]))
}
if msg == "" {
msg = strings.TrimSpace(string(p))
}
if e.structlog {
fmt.Fprintf(os.Stderr, "{\"level\":\"info\",\"time\":\"%s\",\"message\":\"%s\"}\n", time.Now().Format(time.RFC1123Z), strings.Replace(strings.TrimSpace(msg), `"`, `\"`, -1))
} else {
e.logr.Info().Msg(msg)
}
return len(p), nil
}

Some files were not shown because too many files have changed in this diff Show More