Compare commits

...

107 Commits

Author SHA1 Message Date
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
33 changed files with 1732 additions and 632 deletions
+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 Travis
integration tests rely on the LDAP queries returning with a set result,
so if your changes will change the output, Travis 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.
+6 -1
View File
@@ -4,12 +4,17 @@ glauth.cfg
# Ignore the built binary during testing
glauth
# Ignore generated certs in this folder
certs/
# Built binaries
bin
bindata.go
pkg/assets/bindata.go
.*.swp
# Ignoring travis CLI tool logs
*travis.log
certs/
+14 -13
View File
@@ -2,9 +2,11 @@ language: go
# USE THIS alongside makefile tag detection: $TRAVIS_TAG
# if you change the version here, also update the travis deploy condition
go:
- 1.9.x # last version
- 1.10.x # current version
- 1.11.x # older version
- 1.12.x # last version
- 1.13.x # current version
# Speed up git clones
git:
@@ -15,18 +17,19 @@ git:
# 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
- travis_retry sudo apt-get -qq update && sudo apt-get install -y ldap-utils oathtool # need this for integration tests
- travis_retry 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
#- travis_retry go get github.com/golang/lint/golint # Linter
#- travis_retry go get honnef.co/go/tools/cmd/megacheck # Badass static analyzer/linter
# - travis_retry go get github.com/fzipp/gocyclo # Not sure what this is
# Run bindata for assets
- make bindata
# Fetch dependencies
- go get -t -v ./...
- travis_retry go get -t -v ./...
script:
@@ -63,16 +66,14 @@ script:
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=
secure: lPI5FftcQB1eHihN9daeFOT97vZcfq2w0hRb6amC6U4P+K7Ih23R9NmWhDuvGM9CPLaAqgOKiHWIUnP4q9EeBeAUMgg6FI2syr/0nDYz5cHqZMMPWFgeDvn+HqfMdFIVeiKT80+uGY/JNsMI8lsJ0eILEp0GpyIZkoAewCEyxJ/fcLM5htGnQIQ8mQ+2ZXflvPofv6/RPHgu3ryC0icJFVMREIivp2QIZBpFnawnI+OE8ZzxD5R05gfg3IJtYtc/4diX4RPyqVTCqljU/x7/g9KgNsnaLGEz37pBUS0IQcq4ITX/cm4y5LLWPV6JugxjyTq1SXKJnHEgfEFk5/hPM7Q1DMZJ5tGpb2B4aSMwB6YHwvVW83ih96Vw8ZgIrxWT9LyF4n9AHakLhDIiJieDXa3dvG7U5oTxlSP/RJkXlMZEJ3lS/1RZ9vvQ0EOEMAO6ePQzSZ4EM/UShFalBI+fqDtzqxFGeeCWw6lwiKLF2GF7enT8+wLsWOMI43JM5uPAq2g0KFMtJEEcK08IFraObvK5bcFH7snCDtsAaLyjdkVIIBteNtfDsXQ8PMi4bQpLr5kpY68dkyuxDr4IpL1GKqlpe2LbjkzsGrq5wifcHMFmUkEfY/6/ELFc9fL+1ZnMAe7YiW41AvX+hrgVmc3M8zp1kVvy2oERV4LfV1vfaEU=
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"
condition: $TRAVIS_GO_VERSION =~ ^1\.13
repo: glauth/glauth
+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.
+9 -5
View File
@@ -15,6 +15,7 @@ WORKDIR /app
ENV GOPATH=/tmp/gocode
ENV GOOS=linux
ENV GOARCH=amd64
ENV CGO_ENABLED=0
# Only needed for alpine builds
RUN apk add --no-cache git bzr make
@@ -23,7 +24,7 @@ RUN apk add --no-cache git bzr make
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
RUN go get -u github.com/jteeuwen/go-bindata/... && $GOPATH/bin/go-bindata -pkg=assets -o=pkg/assets/bindata.go assets && gofmt -w pkg/assets/bindata.go
# Build and copy final result
RUN make linux64 && cp ./bin/glauth64 /app/glauth
@@ -32,7 +33,7 @@ RUN make linux64 && cp ./bin/glauth64 /app/glauth
# Run Step
#################
FROM golang:alpine as run
FROM 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
@@ -45,11 +46,14 @@ COPY --from=build /app/glauth /app/glauth
COPY --from=build /app/scripts/docker/start.sh /app/docker/
COPY --from=build /app/scripts/docker/default-config.cfg /app/docker/
# Install ldapsearch for container health checks, then ensure ldapsearch is installed
RUN apk update && apk add --no-cache dumb-init openldap-clients && which ldapsearch && rm -rf /var/cache/apk/*
# 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
EXPOSE 389 636 5555
ENTRYPOINT ["/usr/local/bin/dumb-init", "--"]
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/bin/sh", "/app/docker/start.sh"]
+17 -11
View File
@@ -15,7 +15,8 @@ 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
BUILD_FILES=glauth.go
TRIM_FLAGS=-gcflags "all=-trimpath=${PWD}" -asmflags "all=-trimpath=${PWD}"
#####################
# High level commands
@@ -37,7 +38,7 @@ fast: setup linux64 verify cleanup
binaries: linux32 linux64 linuxarm32 linuxarm64 darwin64 win32 win64
# Setup commands to always run
setup: bindata format
setup: getdeps bindata format
#####################
# Subcommands
@@ -47,14 +48,19 @@ setup: bindata format
runtest:
./scripts/travis/integration-test.sh cleanup
# Get all dependencies
getdeps:
go get -d ./...
updatetest:
./scripts/travis/integration-test.sh
bindata:
go get -u github.com/jteeuwen/go-bindata/... && ${GOPATH}/bin/go-bindata -pkg=main assets && gofmt -w bindata.go
go get -u github.com/jteeuwen/go-bindata/... && ${GOPATH}/bin/go-bindata -pkg=assets -o=pkg/assets/bindata.go assets && gofmt -w pkg/assets/bindata.go
cleanup:
rm bindata.go
rm pkg/assets/bindata.go
format:
go fmt
@@ -64,25 +70,25 @@ devrun:
linux32:
GOOS=linux GOARCH=386 go build -ldflags "${BUILD_VARS}" -o bin/glauth32 ${BUILD_FILES} && cd bin && sha256sum glauth32 > glauth32.sha256
GOOS=linux GOARCH=386 go build ${TRIM_FLAGS} -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
GOOS=linux GOARCH=amd64 go build ${TRIM_FLAGS} -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
GOOS=linux GOARCH=arm go build ${TRIM_FLAGS} -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
GOOS=linux GOARCH=arm64 go build ${TRIM_FLAGS} -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
GOOS=darwin GOARCH=amd64 go build ${TRIM_FLAGS} -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
GOOS=windows GOARCH=386 go build ${TRIM_FLAGS} -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
GOOS=windows GOARCH=amd64 go build ${TRIM_FLAGS} -ldflags "${BUILD_VARS}" -o bin/glauth-win64 ${BUILD_FILES} && cd bin && sha256sum glauth-win64 > glauth-win64.sha256
verify:
+19 -4
View File
@@ -1,4 +1,3 @@
# GLAuth: LDAP authentication server for developers
Go-lang LDAP Authentication (GLAuth) is a secure, easy-to-use, LDAP server w/ configurable backends.
@@ -6,7 +5,7 @@ Go-lang LDAP Authentication (GLAuth) is a secure, easy-to-use, LDAP server w/ co
[![Last Commit](https://img.shields.io/github/last-commit/glauth/glauth.svg)](https://github.com/glauth/glauth/graphs/commit-activity)
[![](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/)
[![DockerHub Image Size](https://img.shields.io/microbadger/image-size/glauth/glauth.svg)](https://hub.docker.com/r/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)
@@ -40,8 +39,11 @@ This quickstart is a great way to try out GLAuth in a non-production environment
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
### Usage:
@@ -92,14 +94,14 @@ 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
More configuration options are documented here: https://github.com/glauth/glauth/blob/master/sample-simple.cfg
### Required Fields
* Name
* The user's username
* ou
* ID of the user's primary group
* uidbumber
* uidnumber
* The user's unix user id
* sshPublicKey
* Specify an array of public keys
@@ -137,6 +139,10 @@ More configuration options are documented here: https://github.com/nmcclain/glau
* Specify OTP secret used to validate OTP passcode
* Example: 3hnvnk4ycv44glzigd6s25j4dougs3rk
* default = blank
* passappsha256
* Specify an array of app passwords which can also succesfully bind - these bypass the OTP check. Hash the same way as password.
* Example: ["c32255dbf6fd6b64883ec8801f793bccfa2a860f2b1ae1315cd95cdac1338efa","4939efa7c87095dacb5e7e8b8cfb3a660fa1f5edcc9108f6d7ec20ea4d6b3a88"]
* default = blank
* yubikey
* Specify Yubikey ID for maching Yubikey OTP against the user
* Example: cccjgjgkhcbb
@@ -155,6 +161,11 @@ When using 2FA, append the 2FA code to the end of the password when authenticati
#### 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.
#### App Passwords
Additionally, you can specify an array of password hashes using the `passappsha256` for app passwords. These are not OTP validated, and are hashed in the same way as a password. This allows you to generate a long random string to be used in software which requires the ability to authenticate.
However, app passwords can be used without OTP as well.
#### 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/).
@@ -191,6 +202,10 @@ go get github.com/jteeuwen/go-bindata/...
make all
```
## Stargazers over time
[![Stargazers over time](https://starcharts.herokuapp.com/glauth/glauth.svg)](https://starcharts.herokuapp.com/glauth/glauth)
### Support
Support the ongoing development of GLAuth!
-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 ""
}
+203 -142
View File
@@ -1,17 +1,22 @@
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"
"github.com/BurntSushi/toml"
"github.com/GeertJohan/yubigo"
docopt "github.com/docopt/docopt-go"
"github.com/fsnotify/fsnotify"
"github.com/glauth/glauth/pkg/config"
"github.com/glauth/glauth/pkg/frontend"
"github.com/glauth/glauth/pkg/server"
"github.com/glauth/glauth/pkg/stats"
"github.com/jinzhu/copier"
logging "github.com/op/go-logging"
"gopkg.in/amz.v1/aws"
"gopkg.in/amz.v1/s3"
)
// Set with buildtime vars
@@ -27,7 +32,7 @@ const programName = "glauth"
var usage = `glauth: securely expose your LDAP for external auth
Usage:
glauth [options] -c <file|s3url>
glauth [options] -c <file|s3 url>
glauth -h --help
glauth --version
@@ -40,78 +45,14 @@ Options:
--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")
var (
log *logging.Logger
args map[string]interface{}
stderr *logging.LogBackend
yubiAuth *yubigo.YubiAuth
// 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)
activeConfig = &config.Config{}
)
// Reads builtime vars and returns a full string containing info about
// the currently running version of the software. Primarily used by the
@@ -156,82 +97,101 @@ func getVersionString() string {
}
func main() {
stderr := initLogging()
stderr = initLogging()
log.Debug("AP start")
cfg, err := doConfig()
if err != nil {
if err := parseArgs(); err != nil {
log.Fatalf("Could not parse command-line arguments", err.Error())
}
if err := doConfig(); err != nil {
log.Fatalf("Configuration file error: %s", err.Error())
}
if cfg.Syslog {
enableSyslog(stderr)
}
startService()
}
func startService() {
// stats
stats_general.Set("version", stringer(LastGitTag))
stats.General.Set("version", stats.Stringer(LastGitTag))
// web API
if cfg.API.Enabled {
if activeConfig.API.Enabled {
log.Debug("Web API enabled")
go RunAPI(cfg)
go frontend.RunAPI(log, activeConfig)
}
yubiAuth := (*yubigo.YubiAuth)(nil)
startConfigWatcher()
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")
}
s, err := server.NewServer(log, activeConfig)
if err != nil {
log.Fatalf("Could not start server: %s", err.Error())
}
s.ListenAndServe()
// 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
func startConfigWatcher() {
configFileLocation := getConfigLocation()
// parse the command-line args
args, err := docopt.Parse(usage, nil, true, getVersionString(), false)
if err != nil {
return &cfg, err
if strings.HasPrefix(configFileLocation, "s3://") {
return
}
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatalf("Could not start config-watcher: %s", err.Error())
}
go func() {
for {
select {
case event := <-watcher.Events:
if activeConfig.WatchConfig {
if event.Op.String() == "WRITE" {
if err := doConfig(); err != nil {
log.Warningf("Could not reload config: %sHolding on to old config", err.Error())
} else {
log.Notice("Config was reloaded")
}
}
}
case err := <-watcher.Errors:
if activeConfig.WatchConfig {
log.Info("Error!", err)
}
}
}
}()
watcher.Add(configFileLocation)
}
func parseArgs() error {
var err error
if args, err = docopt.Parse(usage, nil, true, getVersionString(), false); err != nil {
return err
}
return nil
}
func getConfigLocation() string {
return args["--config"].(string)
}
func parseConfigFile(configFileLocation string) (*config.Config, error) {
cfg := config.Config{}
// setup defaults
cfg.LDAP.Enabled = false
cfg.LDAPS.Enabled = true
cfg.Backend.NameFormat = "cn"
cfg.Backend.GroupFormat = "ou"
cfg.Backend.SSHKeyAttr = "sshPublicKey"
// parse the config file
if strings.HasPrefix(args["--config"].(string), "s3://") {
if strings.HasPrefix(configFileLocation, "s3://") {
if _, present := aws.Regions[args["-r"].(string)]; present == false {
return &cfg, fmt.Errorf("Invalid AWS region: %s", args["-r"])
}
@@ -247,7 +207,7 @@ func doConfig() (*config, error) {
}
}
// parse S3 url
s3url := strings.TrimPrefix(args["--config"].(string), "s3://")
s3url := strings.TrimPrefix(configFileLocation, "s3://")
parts := strings.SplitN(s3url, "/", 2)
if len(parts) != 2 {
return &cfg, fmt.Errorf("Invalid S3 URL: %s", s3url)
@@ -261,30 +221,129 @@ func doConfig() (*config, error) {
return &cfg, err
}
} else { // local config file
if _, err := toml.DecodeFile(args["--config"].(string), &cfg); err != nil {
if _, err := toml.DecodeFile(configFileLocation, &cfg); err != nil {
return &cfg, err
}
}
// enable features
if cfg.Debug {
logging.SetLevel(logging.DEBUG, programName)
log.Debug("Debugging enabled")
return &cfg, nil
}
func handleConfig(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.Warning("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
}
}
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")
}
}
switch cfg.Backend.Datastore {
case "":
cfg.Backend.Datastore = "config"
case "config":
case "ldap":
case "owncloud":
default:
return &cfg, fmt.Errorf("Invalid backend %s - must be 'config' or 'ldap'.", cfg.Backend.Datastore)
return &cfg, fmt.Errorf("invalid backend %s - must be 'config', 'ldap' or 'owncloud", cfg.Backend.Datastore)
}
return &cfg, nil
}
// doConfig reads the cli flags and config file
func doConfig() error {
// Parse config-file into config{} struct
cfg, err := parseConfigFile(getConfigLocation())
if err != nil {
return err
}
// Handle validation and parsing of old [frontend] section into [ldap] and/or [ldaps] sections
cfg, err = handleConfig(*cfg)
if err != nil {
return err
}
// Before greenlighting new config entirely, lets make sure the yubiauth works - in case they changed
if activeConfig.YubikeyClientID != cfg.YubikeyClientID || activeConfig.YubikeySecret != cfg.YubikeySecret {
if len(cfg.YubikeyClientID) > 0 && len(cfg.YubikeySecret) > 0 {
_yubiAuth, err := yubigo.NewYubiAuth(cfg.YubikeyClientID, cfg.YubikeySecret)
if err != nil {
return err
}
// No errors, override
yubiAuth = _yubiAuth
}
}
// All config is validated and alright, copy to ativeConfig
if err := copier.Copy(activeConfig, cfg); err != nil {
return err
}
// Handle logging settings for new config
// - we do this last to make sure we only respect a fully validated config
stderr = initLogging()
if activeConfig.Debug {
logging.SetLevel(logging.DEBUG, programName)
log.Debug("Debugging enabled")
}
if activeConfig.Syslog {
enableSyslog(stderr)
}
return nil
}
// initLogging sets up logging to stderr
func initLogging() *logging.LogBackend {
log = logging.MustGetLogger(programName)
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))
@@ -299,6 +358,8 @@ func enableSyslog(stderrBackend *logging.LogBackend) {
if err != nil {
log.Fatal(err)
}
logging.SetBackend(stderrBackend, syslogBackend)
log.Debug("Syslog enabled")
}
+21
View File
@@ -0,0 +1,21 @@
module github.com/glauth/glauth
go 1.13
require (
github.com/BurntSushi/toml v0.3.1
github.com/GeertJohan/yubigo v0.0.0-20190917122436-175bc097e60e
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
github.com/fsnotify/fsnotify v1.4.7
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/jteeuwen/go-bindata v3.0.7+incompatible // indirect
github.com/kr/pretty v0.2.0
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 // indirect
github.com/nmcclain/ldap v0.0.0-20191021200707-3b3b69a7e9e3
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/pquerna/otp v1.2.0
github.com/yaegashi/msgraph.go v0.1.1-0.20200221123608-2d438cf2a7cc
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c // indirect
gopkg.in/amz.v1 v1.0.0-20150111123259-ad23e96a31d2
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
)
+46
View File
@@ -0,0 +1,46 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
github.com/jteeuwen/go-bindata v3.0.7+incompatible h1:91Uy4d9SYVr1kyTJ15wJsog+esAZZl7JmEfTkwmhJts=
github.com/jteeuwen/go-bindata v3.0.7+incompatible/go.mod h1:JVvhzYOiGBnFSYRyV00iY8q7/0PThjIYav1p9h5dmKs=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 h1:D9EvfGQvlkKaDr2CRKN++7HbSXbefUNDrPq60T+g24s=
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484/go.mod h1:O1EljZ+oHprtxDDPHiMWVo/5dBT6PlvWX5PSwj80aBA=
github.com/nmcclain/ldap v0.0.0-20191021200707-3b3b69a7e9e3 h1:NNis9uuNpG5h97Dvxxo53Scg02qBg+3Nfabg6zjFGu8=
github.com/nmcclain/ldap v0.0.0-20191021200707-3b3b69a7e9e3/go.mod h1:YtrVB1/v9Td9SyjXpjYVmbdKgj9B0nPTBsdGUxy0i8U=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok=
github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/yaegashi/msgraph.go v0.1.1-0.20200221123608-2d438cf2a7cc h1:ejaC8rvIvCWmsaFrvmGOxhBuMxxhBB1xRshuM98XQ7M=
github.com/yaegashi/msgraph.go v0.1.1-0.20200221123608-2d438cf2a7cc/go.mod h1:tso14hwzqX4VbnWTNsxiL0DvMb2OwbGISFA7jDibdWc=
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/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c h1:jceGD5YNJGgGMkJz79agzOln1K9TaZUjv5ird16qniQ=
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/amz.v1 v1.0.0-20150111123259-ad23e96a31d2 h1:FMrsB0OTjHsPDA1NM7AhRmmZzkBPu3iGdxK/5MFfBmk=
gopkg.in/amz.v1 v1.0.0-20150111123259-ad23e96a31d2/go.mod h1:F0YaN4yi2XekmElKkPYfybh7pReQE9Ph48lSgeCkzyc=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+78
View File
@@ -0,0 +1,78 @@
package config
// config file
type Backend struct {
BaseDN string
Datastore string
Insecure bool // For LDAP backend only
Servers []string // For LDAP backend only
NameFormat string
GroupFormat string
SSHKeyAttr string
UseGraphAPI bool // For ownCloud backend only
}
type Frontend struct {
AllowedBaseDNs []string // For LDAP backend only
Listen string
Cert string
Key string
TLS bool
}
type LDAP struct {
Enabled bool
Listen string
}
type LDAPS struct {
Enabled bool
Listen string
Cert string
Key string
}
type API struct {
Cert string
Enabled bool
Key string
Listen string
SecretToken string
TLS bool
}
type User struct {
Name string
OtherGroups []int
PassSHA256 string
PassAppSHA256 []string
PrimaryGroup int
SSHKeys []string
OTPSecret string
Yubikey string
Disabled bool
UnixID int
Mail string
LoginShell string
GivenName string
SN string
Homedir string
}
type Group struct {
Name string
UnixID int
IncludeGroups []int
}
type Config struct {
API API
Backend Backend
Debug bool
WatchConfig bool
YubikeyClientID string
YubikeySecret string
Frontend Frontend
LDAP LDAP
LDAPS LDAPS
Groups []Group
Syslog bool
Users []User
ConfigFile string
AwsAccessKeyId string
AwsSecretAccessKey string
AwsRegion string
}
+24 -22
View File
@@ -1,4 +1,4 @@
package main
package frontend
import (
"bytes"
@@ -6,10 +6,14 @@ import (
"io"
"net/http"
"strings"
"github.com/glauth/glauth/pkg/assets"
"github.com/glauth/glauth/pkg/config"
logging "github.com/op/go-logging"
)
// RunAPI provides a basic REST API
func RunAPI(cfg *config) {
func RunAPI(log *logging.Logger, cfg *config.Config) {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
log.Debug(fmt.Sprintf("Web: %s", r.URL.Path))
if r.URL.Path != "/" {
@@ -17,13 +21,13 @@ func RunAPI(cfg *config) {
http.NotFound(w, r)
return
}
pageTemplate, err := Asset("assets/index.html")
pageTemplate, err := assets.Asset("assets/index.html")
if err != nil {
log.Fatal(fmt.Sprintf("Error with HTTP server template asset: %s", err.Error()))
}
fmt.Fprintf(w, string(pageTemplate))
})
http.HandleFunc("/assets/", webStaticHandler)
http.HandleFunc("/assets/", webStaticHandler(log))
if cfg.API.TLS {
log.Notice(fmt.Sprintf("Starting HTTPS server on %s", cfg.API.Listen))
err := http.ListenAndServeTLS(cfg.API.Listen, cfg.API.Cert, cfg.API.Key, nil)
@@ -40,23 +44,21 @@ func RunAPI(cfg *config) {
}
// webStaticHandler serves embedded static web files (js&css)
func webStaticHandler(w http.ResponseWriter, r *http.Request) {
assetPath := r.URL.Path[1:]
staticAsset, err := Asset(assetPath)
if err != nil {
log.Error(err.Error())
http.NotFound(w, r)
return
func webStaticHandler(log *logging.Logger) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
assetPath := r.URL.Path[1:]
staticAsset, err := assets.Asset(assetPath)
if err != nil {
log.Error(err.Error())
http.NotFound(w, r)
return
}
headers := w.Header()
if strings.HasSuffix(assetPath, ".js") {
headers["Content-Type"] = []string{"application/javascript"}
} else if strings.HasSuffix(assetPath, ".css") {
headers["Content-Type"] = []string{"text/css"}
}
io.Copy(w, bytes.NewReader(staticAsset))
}
headers := w.Header()
if strings.HasSuffix(assetPath, ".js") {
headers["Content-Type"] = []string{"application/javascript"}
} else if strings.HasSuffix(assetPath, ".css") {
headers["Content-Type"] = []string{"text/css"}
}
io.Copy(w, bytes.NewReader(staticAsset))
}
type stringer string
func (s stringer) String() string { return string(s) }
+401
View File
@@ -0,0 +1,401 @@
package handler
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"net"
"sort"
"strings"
"github.com/GeertJohan/yubigo"
"github.com/glauth/glauth/pkg/config"
"github.com/glauth/glauth/pkg/stats"
"github.com/nmcclain/ldap"
"github.com/op/go-logging"
"github.com/pquerna/otp/totp"
)
type configHandler struct {
log *logging.Logger
cfg *config.Config
yubikeyAuth *yubigo.YubiAuth
}
func NewConfigHandler(log *logging.Logger, cfg *config.Config, yubikeyAuth *yubigo.YubiAuth) Handler {
handler := configHandler{
log: log,
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)
h.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) {
h.log.Warning(fmt.Sprintf("Bind Error: BindDN %s not our BaseDN %s", bindDN, h.cfg.Backend.BaseDN))
// h.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], h.cfg.Backend.NameFormat+"=")
} else if len(parts) == 2 {
userName = strings.TrimPrefix(parts[0], h.cfg.Backend.NameFormat+"=")
groupName = strings.TrimPrefix(parts[1], h.cfg.Backend.GroupFormat+"=")
} else {
h.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 := config.User{}
found := false
for _, u := range h.cfg.Users {
if u.Name == userName {
found = true
user = u
}
}
if !found {
h.log.Warning(fmt.Sprintf("Bind Error: User %s not found.", userName))
return ldap.LDAPResultInvalidCredentials, nil
}
// find the group
group := config.Group{}
found = false
for _, g := range h.cfg.Groups {
if g.Name == groupName {
found = true
group = g
}
}
if !found {
h.log.Warning(fmt.Sprintf("Bind Error: Group %s not found.", groupName))
return ldap.LDAPResultInvalidCredentials, nil
}
// validate group membership
if user.PrimaryGroup != group.UnixID {
h.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
}
}
}
}
// Store the full bind password provided before possibly modifying
// in the otp check
// Generate a hash of the provided password
hashFull := sha256.New()
hashFull.Write([]byte(bindSimplePw))
// Test OTP if 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
for index, appPw := range user.PassAppSHA256 {
if appPw != hex.EncodeToString(hashFull.Sum(nil)) {
h.log.Debug(fmt.Sprintf("Attempted to bind app pw #%d - failure as %s from %s", index, bindDN, conn.RemoteAddr().String()))
} else {
stats.Frontend.Add("bind_successes", 1)
h.log.Debug("Bind success using app pw #%d as %s from %s", index, bindDN, conn.RemoteAddr().String())
return ldap.LDAPResultSuccess, nil
}
}
// then check main password with the hash
hash := sha256.New()
hash.Write([]byte(bindSimplePw))
// Then ensure the OTP is valid before checking
if !validotp {
h.log.Warning(fmt.Sprintf("Bind Error: invalid OTP token as %s from %s", bindDN, conn.RemoteAddr().String()))
return ldap.LDAPResultInvalidCredentials, nil
}
// Now, check the hash
if user.PassSHA256 != hex.EncodeToString(hash.Sum(nil)) {
h.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)
h.log.Debug(fmt.Sprintf("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)
h.log.Debug(fmt.Sprintf("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{Name: "cn", 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.UnixID)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixGroup"}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "uniqueMember", Values: h.getGroupMembers(g.UnixID)})
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberUid", Values: h.getGroupMemberIDs(g.UnixID)})
dn := fmt.Sprintf("cn=%s,%s=groups,%s", g.Name, h.cfg.Backend.GroupFormat, h.cfg.Backend.BaseDN)
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
}
case "posixaccount", "":
for _, u := range h.cfg.Users {
attrs := []*ldap.EntryAttribute{}
attrs = append(attrs, &ldap.EntryAttribute{Name: "cn", 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.UnixID)}})
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: "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.cfg.Backend.SSHKeyAttr, Values: u.SSHKeys})
}
dn := fmt.Sprintf("%s=%s,%s=%s,%s", h.cfg.Backend.NameFormat, u.Name, h.cfg.Backend.GroupFormat, h.getGroupName(u.PrimaryGroup), h.cfg.Backend.BaseDN)
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
}
}
stats.Frontend.Add("search_successes", 1)
h.log.Debug(fmt.Sprintf("AP: Search OK: %s", searchReq.Filter))
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: 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("%s=%s,%s=%s,%s", h.cfg.Backend.NameFormat, u.Name, h.cfg.Backend.GroupFormat, h.getGroupName(u.PrimaryGroup), h.cfg.Backend.BaseDN)
members[dn] = true
} else {
for _, othergid := range u.OtherGroups {
if othergid == gid {
dn := fmt.Sprintf("%s=%s,%s=%s,%s", h.cfg.Backend.NameFormat, u.Name, h.cfg.Backend.GroupFormat, 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 {
h.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,%s=groups,%s", g.Name, h.cfg.Backend.GroupFormat, 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 ""
}
+10
View File
@@ -0,0 +1,10 @@
package handler
import "github.com/nmcclain/ldap"
// interface for backend handler
type Handler interface {
ldap.Binder
ldap.Searcher
ldap.Closer
}
+47 -37
View File
@@ -1,28 +1,36 @@
package main
package handler
import (
"crypto/sha256"
"crypto/tls"
"encoding/json"
"fmt"
"github.com/kr/pretty"
"github.com/nmcclain/ldap"
"net"
"net/url"
"strconv"
"strings"
"sync"
"time"
"github.com/glauth/glauth/pkg/config"
"github.com/glauth/glauth/pkg/stats"
"github.com/kr/pretty"
"github.com/nmcclain/ldap"
"github.com/op/go-logging"
)
type ldapHandler struct {
doPing chan bool
log *logging.Logger
cfg *config.Config
lock *sync.Mutex // for sessions and servers
sessions map[string]ldapSession
servers []ldapBackend
lock sync.Mutex // for sessions and servers
doPing chan bool
cfg *config
}
// global lock for ldapHandler sessions & servers manipulation
var ldaplock sync.Mutex
type ldapSession struct {
id string
c net.Conn
@@ -43,11 +51,13 @@ type ldapBackend struct {
Ping time.Duration
}
func newLdapHandler(cfg *config) Backend {
func NewLdapHandler(log *logging.Logger, cfg *config.Config) Handler {
handler := ldapHandler{ // set non-zero-value defaults here
sessions: make(map[string]ldapSession),
doPing: make(chan bool),
log: log,
cfg: cfg,
lock: &ldaplock,
}
// parse LDAP URLs
for _, ldapurl := range cfg.Backend.Servers {
@@ -66,31 +76,31 @@ func newLdapHandler(cfg *config) Backend {
//
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)
h.log.Debug(fmt.Sprintf("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())
stats.Frontend.Add("bind_ldapSession_errors", 1)
h.log.Debug(fmt.Sprintf("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())
stats.Frontend.Add("bind_errors", 1)
h.log.Debug(fmt.Sprintf("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())
stats.Frontend.Add("bind_successes", 1)
h.log.Debug(fmt.Sprintf("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)
h.log.Debug(fmt.Sprintf("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)
stats.Frontend.Add("search_ldapSession_errors", 1)
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, nil
}
search := ldap.NewSearchRequest(
@@ -105,24 +115,24 @@ func (h ldapHandler) Search(boundDN string, searchReq ldap.SearchRequest, conn n
searchReq.Controls,
)
log.Debug("Search req to backend: %# v", pretty.Formatter(search))
h.log.Debug(fmt.Sprintf("Search req to backend: %# v", pretty.Formatter(search)))
sr, err := s.ldap.Search(search)
log.Debug("Backend Search result: %# v", pretty.Formatter(sr))
h.log.Debug(fmt.Sprintf("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))
h.log.Debug(fmt.Sprintf("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)
h.log.Debug(fmt.Sprintf("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))
stats.Frontend.Add("search_successes", 1)
h.log.Debug(fmt.Sprintf("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 {
@@ -130,8 +140,8 @@ func (h ldapHandler) Close(boundDn string, conn net.Conn) error {
h.lock.Lock()
defer h.lock.Unlock()
delete(h.sessions, connID(conn))
stats_frontend.Add("closes", 1)
stats_backend.Add("closes", 1)
stats.Frontend.Add("closes", 1)
stats.Backend.Add("closes", 1)
return nil
}
@@ -139,22 +149,22 @@ func (h ldapHandler) Close(boundDn string, conn net.Conn) error {
func (h *ldapHandler) monitorServers() {
err := h.ping()
if err != nil {
log.Fatal(err)
h.log.Fatal(err)
}
go func() {
for {
select {
case <-h.doPing:
log.Notice("doPing requested due to server failure")
h.log.Notice("doPing requested due to server failure")
err = h.ping()
if err != nil {
log.Fatal(err)
h.log.Fatal(err)
}
case <-time.NewTimer(60 * time.Second).C:
log.Debug("doPing after timeout")
h.log.Debug("doPing after timeout")
err = h.ping()
if err != nil {
log.Fatal(err)
h.log.Fatal(err)
}
}
}
@@ -218,7 +228,7 @@ func (h ldapHandler) ping() error {
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.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 {
@@ -229,12 +239,12 @@ func (h ldapHandler) ping() error {
}
h.lock.Unlock()
}
log.Debug("Server health: %# v", pretty.Formatter(h.servers))
h.log.Debug(fmt.Sprintf("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()))
h.log.Error(fmt.Sprintf("Error encoding tail data: %s", err.Error()))
}
stats_backend.Set("servers", stringer(string(b)))
stats.Backend.Set("servers", stats.Stringer(string(b)))
if healthy == false {
return fmt.Errorf("No healthy servers")
}
@@ -258,7 +268,7 @@ func (h ldapHandler) getBestServer() (ldapBackend, error) {
if bestping == forever {
return ldapBackend{}, fmt.Errorf("No healthy servers found")
}
log.Debug("Best server: %# v", pretty.Formatter(favorite))
h.log.Debug(fmt.Sprintf("Best server: %# v", pretty.Formatter(favorite)))
return favorite, nil
}
+319
View File
@@ -0,0 +1,319 @@
package handler
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"strings"
"sync"
"github.com/glauth/glauth/pkg/config"
"github.com/glauth/glauth/pkg/stats"
"github.com/nmcclain/ldap"
"github.com/op/go-logging"
msgraph "github.com/yaegashi/msgraph.go/v1.0"
)
type ownCloudSession struct {
log *logging.Logger
user string
password string
baseUrl string
useGraphAPI bool
}
type ownCloudHandler struct {
log *logging.Logger
cfg *config.Config
meUrl string
sessions map[string]ownCloudSession
lock sync.Mutex
}
func (h ownCloudHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (ldap.LDAPResultCode, error) {
bindDN = strings.ToLower(bindDN)
baseDN := strings.ToLower("," + h.cfg.Backend.BaseDN)
h.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) {
h.log.Warning(fmt.Sprintf("Bind Error: BindDN %s not our BaseDN %s", bindDN, h.cfg.Backend.BaseDN))
return ldap.LDAPResultInvalidCredentials, nil
}
parts := strings.Split(strings.TrimSuffix(bindDN, baseDN), ",")
if len(parts) > 2 {
h.log.Warning(fmt.Sprintf("Bind Error: BindDN %s should have only one or two parts (has %d)", bindDN, len(parts)))
return ldap.LDAPResultInvalidCredentials, nil
}
userName := strings.TrimPrefix(parts[0], "cn=")
// try to login
if !h.login(userName, bindSimplePw) {
h.log.Warning(fmt.Sprintf("Bind Error: User %s login failed", userName))
return ldap.LDAPResultInvalidCredentials, nil
}
id := connID(conn)
h.lock.Lock()
h.sessions[id] = ownCloudSession{
log: h.log,
user: userName,
password: bindSimplePw,
baseUrl: h.cfg.Backend.Servers[0],
useGraphAPI: h.cfg.Backend.UseGraphAPI,
}
h.lock.Unlock()
stats.Frontend.Add("bind_successes", 1)
h.log.Debug(fmt.Sprintf("Bind success as %s from %s", bindDN, conn.RemoteAddr().String()))
return ldap.LDAPResultSuccess, nil
}
func (h ownCloudHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) {
bindDN = strings.ToLower(bindDN)
baseDN := strings.ToLower("," + h.cfg.Backend.BaseDN)
searchBaseDN := strings.ToLower(searchReq.BaseDN)
h.log.Debug(fmt.Sprintf("Search request as %s from %s for %s on %s", bindDN, conn.RemoteAddr().String(), searchReq.Filter, searchBaseDN))
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)
}
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: "cn", 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.UnixID)}})
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("cn=%s,%s=groups,%s", *g.ID, h.cfg.Backend.GroupFormat, h.cfg.Backend.BaseDN)
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
}
case "posixaccount", "":
userName := ""
if searchBaseDN != strings.ToLower(h.cfg.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(err)
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: "cn", 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.cfg.Backend.NameFormat, *u.ID, h.cfg.Backend.GroupFormat, "users", h.cfg.Backend.BaseDN)
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
}
}
stats.Frontend.Add("search_successes", 1)
h.log.Debug(fmt.Sprintf("AP: Search OK: %s", searchReq.Filter))
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldap.LDAPResultSuccess}, 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 {
req, _ := http.NewRequest("GET", h.meUrl, nil)
req.SetBasicAuth(name, pw)
resp, err := http.DefaultClient.Do(req)
if err != nil {
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) RoundTrip(req *http.Request) (*http.Response, error) {
req.SetBasicAuth(s.user, s.password)
return http.DefaultTransport.RoundTrip(req)
}
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.baseUrl)
req, _ := http.NewRequest("GET", groupsUrl, nil)
req.SetBasicAuth(s.user, s.password)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
b, err := ioutil.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, v := range f.Ocs.Data.Groups {
ret[i] = msgraph.Group{DirectoryObject: msgraph.DirectoryObject{Entity: msgraph.Entity{ID: &v}}}
}
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 {
graphAPIBaseUrl := fmt.Sprintf("%s/index.php/apps/graphapi/v1.0/", s.baseUrl)
httpClient := &http.Client{
Transport: s,
}
g := msgraph.NewClient(httpClient)
g.SetURL(graphAPIBaseUrl)
return g
}
func (s ownCloudSession) getUsers(userName string) ([]msgraph.User, error) {
if s.useGraphAPI {
ctx := context.Background()
req := s.NewClient().Users()
if len(userName) > 0 {
u, err := req.ID(userName).Request().Get(ctx)
if err != nil {
return nil, err
}
return []msgraph.User{*u}, nil
}
return req.Request().Get(ctx)
}
usersUrl := fmt.Sprintf("%s/ocs/v2.php/cloud/users?format=json", s.baseUrl)
req, _ := http.NewRequest("GET", usersUrl, nil)
req.SetBasicAuth(s.user, s.password)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
b, err := ioutil.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, v := range f.Ocs.Data.Users {
ret[i] = msgraph.User{DirectoryObject: msgraph.DirectoryObject{Entity: msgraph.Entity{ID: &v}}}
}
return ret, nil
}
func (s ownCloudSession) redirectPolicyFunc(req *http.Request, via []*http.Request) error {
s.log.Debug("Setting user and password")
req.SetBasicAuth(s.user, s.password)
return nil
}
func NewOwnCloudHandler(log *logging.Logger, cfg *config.Config) Handler {
meUrl := fmt.Sprintf("%s/ocs/v2.php/cloud/user?format=json", cfg.Backend.Servers[0])
handler := ownCloudHandler{
log: log,
cfg: cfg,
meUrl: meUrl,
sessions: make(map[string]ownCloudSession),
}
return handler
}
+91
View File
@@ -0,0 +1,91 @@
package server
import (
"errors"
"fmt"
"github.com/GeertJohan/yubigo"
"github.com/glauth/glauth/pkg/config"
"github.com/glauth/glauth/pkg/handler"
"github.com/nmcclain/ldap"
"github.com/op/go-logging"
)
type LdapSvc struct {
log *logging.Logger
c *config.Config
yubiAuth *yubigo.YubiAuth
l *ldap.Server
}
func NewServer(log *logging.Logger, cfg *config.Config) (*LdapSvc, error) {
s := LdapSvc{
log: log,
c: cfg,
}
var err error
if len(cfg.YubikeyClientID) > 0 && len(cfg.YubikeySecret) > 0 {
s.yubiAuth, err = yubigo.NewYubiAuth(cfg.YubikeyClientID, cfg.YubikeySecret)
if err != nil {
return nil, errors.New("Yubikey Auth failed")
}
}
// configure the backend
s.l = ldap.NewServer()
s.l.EnforceLDAP = true
var h handler.Handler
switch cfg.Backend.Datastore {
case "ldap":
h = handler.NewLdapHandler(log, cfg)
case "owncloud":
h = handler.NewOwnCloudHandler(log, cfg)
case "config":
h = handler.NewConfigHandler(log, cfg, s.yubiAuth)
default:
return nil, fmt.Errorf("unsupported backend %s - must be 'config' or 'ldap'", cfg.Backend.Datastore)
}
log.Notice(fmt.Sprintf("Using %s backend", cfg.Backend.Datastore))
s.l.BindFunc("", h)
s.l.SearchFunc("", h)
s.l.CloseFunc("", h)
return &s, nil
}
func (s *LdapSvc) ListenAndServe() {
if s.c.LDAP.Enabled {
// Dont block if also starting a LDAPS server afterwards
shouldBlock := !s.c.LDAPS.Enabled
if shouldBlock {
s.startLDAP()
} else {
go s.startLDAP()
}
}
if s.c.LDAPS.Enabled {
// Always block here
s.startLDAPS()
}
}
func (s *LdapSvc) startLDAP() {
s.log.Notice(fmt.Sprintf("LDAP server listening on %s", s.c.LDAP.Listen))
if err := s.l.ListenAndServe(s.c.LDAP.Listen); err != nil {
s.log.Fatalf("LDAP Server Failed: %s", err.Error())
}
}
func (s *LdapSvc) startLDAPS() {
s.log.Notice(fmt.Sprintf("LDAPS server listening on %s", s.c.LDAPS.Listen))
if err := s.l.ListenAndServeTLS(s.c.LDAPS.Listen, s.c.LDAPS.Cert, s.c.LDAPS.Key); err != nil {
s.log.Fatalf("LDAP Server Failed: %s", err.Error())
}
}
+10
View File
@@ -0,0 +1,10 @@
package stats
import "expvar"
// exposed expvar variables
var (
Frontend = expvar.NewMap("proxy_frontend")
Backend = expvar.NewMap("proxy_backend")
General = expvar.NewMap("proxy")
)
+5
View File
@@ -0,0 +1,5 @@
package stats
type Stringer string
func (s Stringer) String() string { return string(s) }
+22
View File
@@ -0,0 +1,22 @@
#################
# General configuration.
debug = true
#################
# Server configuration.
# This ([ldap] and [ldaps]) is the new server-config format
[ldap]
enabled = true
listen = "localhost:3893"
[ldaps]
enabled = false
#################
# The backend section controls the data store.
[backend]
datastore = "owncloud"
baseDN = "dc=owncloud,dc=com"
servers = ["https://cloud.example.com"]
UseGraphAPI = true
+44 -8
View File
@@ -4,22 +4,53 @@
#################
# General configuration.
debug = true
#syslog = 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"
#################
# 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"
# Server configuration.
#
# This is the old server-config format
# - will still work, but please use new format, shown below
#
# [frontend]
# listen = "localhost:8484"
# tls = false
# cert ="certs/server.crt"
# key = "certs/server.key"
# This ([ldap] and [ldaps]) is the new server-config format
[ldap]
enabled = true
listen = "0.0.0.0:3893"
[ldaps]
enabled = true
listen = "0.0.0.0:3894"
cert = "certs/server.crt"
key = "certs/server.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"
#################
# The users section contains a hardcoded list of valid users.
@@ -42,6 +73,11 @@ debug = true
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"
@@ -78,6 +114,6 @@ debug = true
[api]
enabled = true
tls = false # enable TLS for production!!
listen = "localhost:5555"
listen = "0.0.0.0:5555"
cert = "cert.pem"
key = "key.pem"
+1 -1
View File
@@ -10,7 +10,7 @@ debug = true
# The frontend section controls how clients connect to the proxy.
[frontend]
tls = false # enable TLS for production!!
listen = "localhost:3893"
listen = "localhost:389"
cert = "cert.pem"
key = "key.pem"
+16 -1
View File
@@ -2,12 +2,27 @@
# 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;
echo "Config file not found at /app/config/config.cfg"
echo "Copying example configuration file to run."
cp /app/docker/default-config.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."
+108 -10
View File
@@ -5,14 +5,22 @@ ou: superheros
uidNumber: 5001
accountStatus: active
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/bash
homeDirectory: /home/hackers
description: hackers via LDAP
gecos: hackers via LDAP
description: hackers
gecos: hackers
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
shadowExpire: -1
shadowFlag: 134538308
shadowInactive: -1
shadowLastChange: 11000
shadowMax: 99999
shadowMin: -1
shadowWarning: 7
dn: cn=johndoe,ou=superheros,dc=glauth,dc=com
cn: johndoe
@@ -24,14 +32,22 @@ uidNumber: 5002
accountStatus: active
mail: jdoe@example.com
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/sh
homeDirectory: /root
description: johndoe via LDAP
gecos: johndoe via LDAP
description: johndoe
gecos: johndoe
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
shadowExpire: -1
shadowFlag: 134538308
shadowInactive: -1
shadowLastChange: 11000
shadowMax: 99999
shadowMin: -1
shadowWarning: 7
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwV
w3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4h
C/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLV
@@ -49,16 +65,24 @@ uidNumber: 5003
accountStatus: active
mail: jdoe2@example.com
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/sh
homeDirectory: /home/jamesdoe
description: jamesdoe via LDAP
gecos: jamesdoe via LDAP
description: jamesdoe
gecos: jamesdoe
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
shadowExpire: -1
shadowFlag: 134538308
shadowInactive: -1
shadowLastChange: 11000
shadowMax: 99999
shadowMin: -1
shadowWarning: 7
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwV
w3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4h
C/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLV
@@ -76,16 +100,82 @@ uidNumber: 5004
accountStatus: active
mail: adoe@example.com
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/sh
homeDirectory: /home/alexdoe
description: alexdoe via LDAP
gecos: alexdoe via LDAP
description: alexdoe
gecos: alexdoe
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
shadowExpire: -1
shadowFlag: 134538308
shadowInactive: -1
shadowLastChange: 11000
shadowMax: 99999
shadowMin: -1
shadowWarning: 7
dn: cn=jackdoe,ou=superheros,dc=glauth,dc=com
cn: jackdoe
uid: jackdoe
givenName: Jack
sn: Doe
ou: superheros
uidNumber: 5005
accountStatus: active
mail: jdoe3@example.com
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/sh
homeDirectory: /home/jackdoe
description: jackdoe
gecos: jackdoe
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
shadowExpire: -1
shadowFlag: 134538308
shadowInactive: -1
shadowLastChange: 11000
shadowMax: 99999
shadowMin: -1
shadowWarning: 7
dn: cn=sarahdoe,ou=superheros,dc=glauth,dc=com
cn: sarahdoe
uid: sarahdoe
givenName: Sarah
sn: Doe
ou: superheros
uidNumber: 5006
accountStatus: active
mail: sdoe@example.com
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/sh
homeDirectory: /home/sarahdoe
description: sarahdoe
gecos: sarahdoe
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
shadowExpire: -1
shadowFlag: 134538308
shadowInactive: -1
shadowLastChange: 11000
shadowMax: 99999
shadowMin: -1
shadowWarning: 7
dn: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
cn: serviceuser
@@ -94,11 +184,19 @@ ou: svcaccts
uidNumber: 5003
accountStatus: active
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/bash
homeDirectory: /home/serviceuser
description: serviceuser via LDAP
gecos: serviceuser via LDAP
description: serviceuser
gecos: serviceuser
gidNumber: 5502
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=svcaccts,ou=groups,dc=glauth,dc=com
shadowExpire: -1
shadowFlag: 134538308
shadowInactive: -1
shadowLastChange: 11000
shadowMax: 99999
shadowMin: -1
shadowWarning: 7
+34 -7
View File
@@ -1,20 +1,26 @@
dn: cn=superheros,ou=groups,dc=glauth,dc=com
cn: superheros
description: superheros via LDAP
uid: superheros
description: superheros
gidNumber: 5501
objectClass: posixGroup
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=hackers,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=jackdoe,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=sarahdoe,ou=superheros,dc=glauth,dc=com
memberUid: alexdoe
memberUid: hackers
memberUid: jackdoe
memberUid: jamesdoe
memberUid: johndoe
memberUid: sarahdoe
dn: cn=svcaccts,ou=groups,dc=glauth,dc=com
cn: svcaccts
description: svcaccts via LDAP
uid: svcaccts
description: svcaccts
gidNumber: 5502
objectClass: posixGroup
uniqueMember: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
@@ -22,57 +28,78 @@ memberUid: serviceuser
dn: cn=vpnaccess,ou=groups,dc=glauth,dc=com
cn: vpnaccess
description: vpnaccess via LDAP
uid: vpnaccess
description: vpnaccess
gidNumber: 5503
objectClass: posixGroup
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=hackers,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=jackdoe,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=sarahdoe,ou=superheros,dc=glauth,dc=com
memberUid: alexdoe
memberUid: hackers
memberUid: jackdoe
memberUid: jamesdoe
memberUid: johndoe
memberUid: sarahdoe
dn: cn=allaccs,ou=groups,dc=glauth,dc=com
cn: allaccs
description: allaccs via LDAP
uid: allaccs
description: allaccs
gidNumber: 5504
objectClass: posixGroup
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=hackers,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=jackdoe,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=sarahdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
memberUid: alexdoe
memberUid: hackers
memberUid: jackdoe
memberUid: jamesdoe
memberUid: johndoe
memberUid: sarahdoe
memberUid: serviceuser
dn: cn=mailadmin,ou=groups,dc=glauth,dc=com
cn: mailadmin
description: mailadmin via LDAP
uid: mailadmin
description: mailadmin
gidNumber: 5505
objectClass: posixGroup
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=jackdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=sarahdoe,ou=superheros,dc=glauth,dc=com
memberUid: alexdoe
memberUid: jackdoe
memberUid: jamesdoe
memberUid: sarahdoe
dn: cn=webmail,ou=groups,dc=glauth,dc=com
cn: webmail
description: webmail via LDAP
uid: webmail
description: webmail
gidNumber: 5506
objectClass: posixGroup
dn: cn=fulltime,ou=groups,dc=glauth,dc=com
cn: fulltime
description: fulltime via LDAP
uid: fulltime
description: fulltime
gidNumber: 5507
objectClass: posixGroup
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=jackdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=sarahdoe,ou=superheros,dc=glauth,dc=com
memberUid: alexdoe
memberUid: jackdoe
memberUid: jamesdoe
memberUid: sarahdoe
+10 -2
View File
@@ -5,12 +5,20 @@ ou: superheros
uidNumber: 5001
accountStatus: active
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/bash
homeDirectory: /home/hackers
description: hackers via LDAP
gecos: hackers via LDAP
description: hackers
gecos: hackers
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
shadowExpire: -1
shadowFlag: 134538308
shadowInactive: -1
shadowLastChange: 11000
shadowMax: 99999
shadowMin: -1
shadowWarning: 7
+10 -2
View File
@@ -8,14 +8,22 @@ uidNumber: 5002
accountStatus: active
mail: jdoe@example.com
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/sh
homeDirectory: /root
description: johndoe via LDAP
gecos: johndoe via LDAP
description: johndoe
gecos: johndoe
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
shadowExpire: -1
shadowFlag: 134538308
shadowInactive: -1
shadowLastChange: 11000
shadowMax: 99999
shadowMin: -1
shadowWarning: 7
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwV
w3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4h
C/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLV
+10 -2
View File
@@ -5,11 +5,19 @@ ou: svcaccts
uidNumber: 5003
accountStatus: active
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/bash
homeDirectory: /home/serviceuser
description: serviceuser via LDAP
gecos: serviceuser via LDAP
description: serviceuser
gecos: serviceuser
gidNumber: 5502
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=svcaccts,ou=groups,dc=glauth,dc=com
shadowExpire: -1
shadowFlag: 134538308
shadowInactive: -1
shadowLastChange: 11000
shadowMax: 99999
shadowMin: -1
shadowWarning: 7
+10 -2
View File
@@ -8,16 +8,24 @@ uidNumber: 5003
accountStatus: active
mail: jdoe2@example.com
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/sh
homeDirectory: /home/jamesdoe
description: jamesdoe via LDAP
gecos: jamesdoe via LDAP
description: jamesdoe
gecos: jamesdoe
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
shadowExpire: -1
shadowFlag: 134538308
shadowInactive: -1
shadowLastChange: 11000
shadowMax: 99999
shadowMin: -1
shadowWarning: 7
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwV
w3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4h
C/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLV
+10 -2
View File
@@ -8,14 +8,22 @@ uidNumber: 5004
accountStatus: active
mail: adoe@example.com
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/sh
homeDirectory: /home/alexdoe
description: alexdoe via LDAP
gecos: alexdoe via LDAP
description: alexdoe
gecos: alexdoe
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
shadowExpire: -1
shadowFlag: 134538308
shadowInactive: -1
shadowLastChange: 11000
shadowMax: 99999
shadowMin: -1
shadowWarning: 7
+67 -2
View File
@@ -4,6 +4,15 @@ export CLEANUP="$1"
## Main Methods
# Dep check
command -v oathtool ;
if [[ "$?" = "1" ]] ; then
echo "Please install oathtool or add it to \$PATH before continuing."
exit 1;
fi
# Get the git working directory base if travis build dir isn't set
if [[ "$TRAVIS_BUILD_DIR" == "" ]] ; then
export TRAVIS_BUILD_DIR="$(git rev-parse --show-toplevel)"
@@ -25,6 +34,9 @@ echo ""
# Start in background, capture PID
"$TRAVIS_BUILD_DIR/bin/glauth64" -c "$TRAVIS_BUILD_DIR/scripts/travis/test-config.cfg" &> /dev/null &
# Use this instead to see glauth logs while running
# "$TRAVIS_BUILD_DIR/bin/glauth64" -c "$TRAVIS_BUILD_DIR/scripts/travis/test-config.cfg" &
glauthPid="$!"
echo "Running glauth at PID=$glauthPid"
@@ -60,9 +72,9 @@ function bindTest() {
exitCode="$?"
if [[ "$exitCode" = "0" ]] ; then
echo " - PASS : 2FA test '$4'";
echo " - PASS : Bind test '$4'";
else
echo " - FAIL : 2FA test '$4'";
echo " - FAIL : Bind test '$4'";
FAIL="1"
fi
@@ -170,6 +182,59 @@ bindTest "cn=alexdoe,ou=superheros,dc=glauth,dc=com" \
"cn=alexdoe" \
"OtpAlexDoe"
## App Password Bind Test
# Test the main pw
bindTest "cn=jackdoe,ou=superheros,dc=glauth,dc=com" \
"dogood1" \
"cn=jackdoe" \
"AppPwNoOtp0"
# App passwords on user
bindTest "cn=jackdoe,ou=superheros,dc=glauth,dc=com" \
"TestAppPw1" \
"cn=jackdoe" \
"AppPwNoOtp1"
bindTest "cn=jackdoe,ou=superheros,dc=glauth,dc=com" \
"TestAppPw2" \
"cn=jackdoe" \
"AppPwNoOtp2"
bindTest "cn=jackdoe,ou=superheros,dc=glauth,dc=com" \
"TestAppPw3" \
"cn=jackdoe" \
"AppPwNoOtp3"
####
# Test for a user who also uses OTP
####
otpCode="$(oathtool --totp -b -d 6 '3hnvnk4ycv44glzigd6s25j4dougs3rk')"
pass="dogood1"
# Test the main pw
bindTest "cn=sarahdoe,ou=superheros,dc=glauth,dc=com" \
"$pass$otpCode" \
"cn=sarahdoe" \
"AppPwOtp0"
# App passwords on user
bindTest "cn=sarahdoe,ou=superheros,dc=glauth,dc=com" \
"TestAppPw1" \
"cn=sarahdoe" \
"AppPwOtp1"
bindTest "cn=sarahdoe,ou=superheros,dc=glauth,dc=com" \
"TestAppPw2" \
"cn=sarahdoe" \
"AppPwOtp2"
bindTest "cn=sarahdoe,ou=superheros,dc=glauth,dc=com" \
"TestAppPw3" \
"cn=sarahdoe" \
"AppPwOtp3"
#############
## Cleanup
+40
View File
@@ -10,6 +10,9 @@ debug = true
# The frontend section controls how clients connect to the proxy.
[frontend]
tls = false # enable TLS for production!!
# Using a nonstandard port to ensure there are no
# conflicts if running a test alongside a prod instance
listen = "localhost:3893"
cert = "cert.pem"
key = "key.pem"
@@ -70,6 +73,43 @@ debug = true
passsha256 = "b1cae08a1fd8b0bd0855e48f74ba2f8548904dd5de72311e6234617a3f661e16" # ThisAloneWontWork!
# App PW test user without OTP
[[users]]
name = "jackdoe"
givenname="Jack"
sn="Doe"
mail = "jdoe3@example.com"
unixid = 5005
primarygroup = 5501
loginShell = "/bin/sh"
otherGroups = [5505,5507]
passsha256 = "ff522a502031e5f37054892d642c2c0f36af6c4e8f62225470d4a1a99f3c9c2a" #dogood1
passappsha256 = [
"c32255dbf6fd6b64883ec8801f793bccfa2a860f2b1ae1315cd95cdac1338efa", # TestAppPw1
"c9853d5f2599e90497e9f8cc671bd2022b0fb5d1bd7cfff92f079e8f8f02b8d3", # TestAppPw2
"4939efa7c87095dacb5e7e8b8cfb3a660fa1f5edcc9108f6d7ec20ea4d6b3a88", # TestAppPw3
]
# App PW test user with OTP
[[users]]
name = "sarahdoe"
givenname="Sarah"
sn="Doe"
mail = "sdoe@example.com"
unixid = 5006
primarygroup = 5501
loginShell = "/bin/sh"
otherGroups = [5505,5507]
otpsecret = "3hnvnk4ycv44glzigd6s25j4dougs3rk"
passsha256 = "ff522a502031e5f37054892d642c2c0f36af6c4e8f62225470d4a1a99f3c9c2a" #dogood1
passappsha256 = [
"c32255dbf6fd6b64883ec8801f793bccfa2a860f2b1ae1315cd95cdac1338efa", # TestAppPw1
"c9853d5f2599e90497e9f8cc671bd2022b0fb5d1bd7cfff92f079e8f8f02b8d3", # TestAppPw2
"4939efa7c87095dacb5e7e8b8cfb3a660fa1f5edcc9108f6d7ec20ea4d6b3a88", # TestAppPw3
]
[[users]]
name = "serviceuser"
unixid = 5003