Compare commits

...

22 Commits

Author SHA1 Message Date
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
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 2b4aa3b78d Update README.md 2018-08-30 11:51:52 -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 d410b01955 Switching docker hub size badge to a working one 2018-07-31 16:02:52 -05:00
Ben Yanke 3a513c4ee6 Removing bindata - should have been previously gitignored 2018-07-20 23:38:14 -05:00
Ben Yanke 6a3f96c04d Pulling from Dev for v1.1 (#48)
Includes a number of fixes and improvements:

 - Add 2 factor auth (thanks @ryskov)
 - Dockerfile imporvements
 - Removing repo cruft
 - Improving travis builds and integration tests
 - Fixing broken amazon s3 packages (thanks @ryskov)
 - Vastly improved version string - now autogenerates based on git status in the repo at the time of build
 - Starting on travis builds for releases (currently built on my workstation)
 - Starting framework for unit tests
 - Add codecov to CI
2018-07-20 23:31:55 -05:00
Ben Yanke fef26916e9 Adding donate info 2018-06-15 22:15:52 -05:00
Ben Yanke 8e48638f05 Adding donate info 2018-06-15 22:14:59 -05:00
Ben Yanke 1876eb9ff7 Adding nano swap files to gitignore 2018-05-11 12:40:51 -05:00
Ben Yanke a3797bbe08 Merging in minor fixes for v1.0.1 (#29)
Merging in:

 * fixes to amazon s3 packages
 * dockerfiles (build was previously not yet working)
 * adding version number to bin so `glauth --version` reports correctly
 * Some Travis CI tweaks
2018-05-11 12:35:39 -05:00
Ben Yanke 18d128dd0c Update README.md 2018-04-28 23:23:38 -05:00
Ben Yanke f317b73009 Update README.md 2018-04-28 23:23:13 -05:00
Ben Yanke efe89aff4b Update README.md 2018-04-28 23:22:47 -05:00
Ben Yanke 17bce790a9 Update README.md 2018-04-28 22:25:39 -05:00
Ben Yanke 5aa56138ce Adding more readme badges 2018-04-28 21:39:36 -05:00
Ben Yanke 8b00a524fb Adding readme badges 2018-04-28 17:08:51 -05:00
Ben Yanke 41ec0fdb10 Adding readme badges 2018-04-28 16:55:12 -05:00
Ben Yanke d1d51d5c7c updating for v1.0.0 release 2018-04-24 20:07:08 -05:00
39 changed files with 1654 additions and 405 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.
+13
View File
@@ -3,3 +3,16 @@ glauth.cfg
# Ignore the built binary during testing
glauth
# Ignore generated certs in this folder
certs/
# Built binaries
bin
bindata.go
.*.swp
# Ignoring travis CLI tool logs
*travis.log
+80
View File
@@ -0,0 +1,80 @@
language: go
# USE THIS alongside makefile tag detection: $TRAVIS_TAG
go:
- 1.9.x # older version
- 1.10.x # last version
- 1.11.x # current version
# Speed up git clones
git:
depth: 1
# Anything in before_script that returns a nonzero exit code will
# flunk the build and immediately stop. It's sorta like having
# set -e enabled in bash.
before_script:
- 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/
#- 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
- travis_retry go get -t -v ./...
script:
- env # Dump the env to make debugging easier
- diff -u <(echo -n) <(gofmt -d ./) # Fail if a .go file hasn't been formatted with 'go fmt'
# - go vet ./... " # go vet is the official Go static analyzer
# - go test -v -race ./... # Run all the tests with the race detector enabled
# - megacheck ./... # "go vet on steroids" + linter
# - gocyclo -over 19 $GO_FILES # forbid code with huge functions
# - golint -set_exit_status $(go list ./...) # one last linter
# Unit tests
# - tbd - use 'go test' here when we have unit tests written. example command below. This will generate output for codecov
# - go test -race -coverprofile=coverage.txt -covermode=atomic
# Full compile test
# - go build -o ./glauth glauth.go bindata.go ldapbackend.go webapi.go configbackend.go
- make all
# Output hashes to the build log
- cat bin/*.sha256
# Integration test with compiled binary
- make test
# after_success:
# - bash <(curl -s https://codecov.io/bash)
deploy:
provider: releases
api_key:
secure: CsQb802CJJlum5suwo3Yvw1TkmX56O6TPrtJqMI0Qm6A878dhrZIjyVmfG3Iq9rtZvfFhK+Y3SIfPZp8BjV3cicc+X1Yh1VvMG12ukqHSruvGvLG49dtnq40P31u85Y4HdBk+W8YIsCQHEonsID9l+q2LbhuiC358UF+HNgzESEz6ZyTZz0AvAJzm1uyiUiPTHMFwqN7qQsookmLXf490rqp9YmFFqte+o3/JwoYXc3pE/rBMJitPSJNGULzu2QofV5gJ0JxgPWlfF5DsHHEi1jhpBcTfiFzEvkdcl1SVme9MMNPue7j8u9+PLJ6uIdlnTeBcMPKgtzOg/kBqY4+3QzY4aPXjKm3+MF3Cau66+g3QC7dKenvYefapQYN7t1SSpiR9X3TLWZrBbw2ovMe3em+uMnIM08bemzj8DapRgKXmYKWnwTlA9KLf0Vl22255OzMGarkhx94cDvUHmEkNo7qBXqmUxPo0Fc5XJyD7JmUNgYoWj0/NjyN09VG22FL2FW44HR3eitq2wSFSMO/jsC2rLuCnyLHViP+O2YJu0T4Q3gdQUcdQmjT7y8ogH5Shn9t2wY4NRxI3MyXO7+fGEOj1DaD6CFQwcSwbJVpIzS6J8z7+rRrzE9QkXeCmwNNt5UkpR9lYmOEzpc00rTRimwjfmJn/nTXsIDiQg9iZJM=
file_glob: true
file: bin/*
draft: true
name: "DRAFT RELEASE: $TRAVIS_TAG"
body: "Draft release, committed with message: $TRAVIS_COMMIT_MESSAGE"
body: "# Commit Details<br />$TRAVIS_COMMIT_MESSAGE<br /><br /># Note About Non-Linux 64bit Files<br /> While I expect the code is system-independent, there is not a good (and free) CI system which can be easily used to continuously test releases on ARM, BSD, Linux-32bit, and Windows.<br /><br />As such, the primary release platform is Linux 64 bit. The extent of testing on these packages consists solely of cross-compiling for these architectures from a linux 64 bit system.<br /><br />We will accept PRs which fix bugs on these platforms, but be aware these binaries will not be tested regularly, and instead are provided for the convenience of those who feel comfortable using them with this knowledge."
skip_cleanup: true
on:
branch: master
tags: true
condition: $TRAVIS_GO_VERSION = "1.10.1"
+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.
+59
View File
@@ -0,0 +1,59 @@
#################
# Build Step
#################
FROM golang:alpine as build
MAINTAINER Ben Yanke <ben@benyanke.com>
# Setup work env
RUN mkdir /app /tmp/gocode
ADD . /app/
WORKDIR /app
# Required envs for GO
ENV GOPATH=/tmp/gocode
ENV GOOS=linux
ENV GOARCH=amd64
# Only needed for alpine builds
RUN apk add --no-cache git bzr make
# Install deps
RUN go get -d -v ./...
# Run go-bindata to embed data for API
RUN go get -u github.com/jteeuwen/go-bindata/... && $GOPATH/bin/go-bindata -pkg=main assets && gofmt -w bindata.go
# Build and copy final result
RUN make linux64 && cp ./bin/glauth64 /app/glauth
#################
# Run Step
#################
FROM golang:alpine as run
MAINTAINER Ben Yanke <ben@benyanke.com>
# Copies a sample config to be used if a volume isn't mounted with user's config
ADD sample-simple.cfg /app/config/config.cfg
# Copy binary from build container
COPY --from=build /app/glauth /app/glauth
# Copy docker specific scripts from build container
COPY --from=build /app/scripts/docker/start.sh /app/docker/
COPY --from=build /app/scripts/docker/default-config.cfg /app/docker/
# Install ldapsearch for container health checks, then ensure ldapsearch is installed
RUN apk update && apk add --no-cache 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 636 5555
ENTRYPOINT ["/usr/local/bin/dumb-init", "--"]
CMD ["/bin/sh", "/app/docker/start.sh"]
+83 -11
View File
@@ -1,22 +1,94 @@
VERSION=$(shell bin/glauth64 --version)
GIT_COMMIT=$(shell git rev-list -1 HEAD )
BUILD_TIME=$(shell date --utc +%Y%m%d_%H%M%SZ)
GIT_CLEAN=$(shell git status | grep -E "working (tree|directory) clean" | wc -l)
run:
go run glauth.go bindata.go ldapbackend.go webapi.go configbackend.go -c glauth.cfg
# Last git tag
LAST_GIT_TAG=$(shell git describe --abbrev=0 --tags 2> /dev/null)
all: bindata binaries
# this=1 if the current commit is the tagged commit (ie, if this is a release build)
GIT_IS_TAG_COMMIT=$(shell git describe --abbrev=0 --tags > /dev/null 2> /dev/null && echo "1" || echo "0")
binaries: bindata linux32 linux64 darwin64
# Used when a tag isn't available
GIT_BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
# Build variables
BUILD_VARS=-X main.GitCommit=${GIT_COMMIT} -X main.GitBranch=${GIT_BRANCH} -X main.BuildTime=${BUILD_TIME} -X main.GitClean=${GIT_CLEAN} -X main.LastGitTag=${LAST_GIT_TAG} -X main.GitTagIsCommit=${GIT_IS_TAG_COMMIT}
BUILD_FILES=glauth.go bindata.go ldapbackend.go webapi.go configbackend.go
#####################
# High level commands
#####################
# Build and run - used for development
run: setup devrun cleanup
# Run the integration test on linux64 (eventually allow the binary to be set)
test: runtest
# Run build process for all binaries
all: setup binaries verify cleanup
# Run build process for only linux64
fast: setup linux64 verify cleanup
# list of binary formats to build
binaries: linux32 linux64 linuxarm32 linuxarm64 darwin64 win32 win64
# Setup commands to always run
setup: getdeps bindata format
#####################
# Subcommands
#####################
# Run integration test
runtest:
./scripts/travis/integration-test.sh cleanup
# Get all dependencies
getdeps:
go get -d ./...
updatetest:
./scripts/travis/integration-test.sh
bindata:
go-bindata -pkg=main assets
go get -u github.com/jteeuwen/go-bindata/... && ${GOPATH}/bin/go-bindata -pkg=main assets && gofmt -w bindata.go
linux32: bindata
GOOS=linux GOARCH=386 go build -o bin/glauth32 glauth.go bindata.go ldapbackend.go webapi.go configbackend.go
linux64: bindata
GOOS=linux GOARCH=amd64 go build -o bin/glauth64 glauth.go bindata.go ldapbackend.go webapi.go configbackend.go
cleanup:
rm bindata.go
darwin64: bindata
GOOS=darwin GOARCH=amd64 go build -o bin/glauthOSX glauth.go bindata.go ldapbackend.go webapi.go configbackend.go
format:
go fmt
devrun:
go run ${BUILD_FILES} -c glauth.cfg
linux32:
GOOS=linux GOARCH=386 go build -ldflags "${BUILD_VARS}" -o bin/glauth32 ${BUILD_FILES} && cd bin && sha256sum glauth32 > glauth32.sha256
linux64:
GOOS=linux GOARCH=amd64 go build -ldflags "${BUILD_VARS}" -o bin/glauth64 ${BUILD_FILES} && cd bin && sha256sum glauth64 > glauth64.sha256
linuxarm32:
GOOS=linux GOARCH=arm go build -ldflags "${BUILD_VARS}" -o bin/glauth-arm32 ${BUILD_FILES} && cd bin && sha256sum glauth-arm32 > glauth-arm32.sha256
linuxarm64:
GOOS=linux GOARCH=arm64 go build -ldflags "${BUILD_VARS}" -o bin/glauth-arm64 ${BUILD_FILES} && cd bin && sha256sum glauth-arm64 > glauth-arm64.sha256
darwin64:
GOOS=darwin GOARCH=amd64 go build -ldflags "${BUILD_VARS}" -o bin/glauthOSX ${BUILD_FILES} && cd bin && sha256sum glauthOSX > glauthOSX.sha256
win32:
GOOS=windows GOARCH=386 go build -ldflags "${BUILD_VARS}" -o bin/glauth-win32 ${BUILD_FILES} && cd bin && sha256sum glauth-win32 > glauth-win32.sha256
win64:
GOOS=windows GOARCH=amd64 go build -ldflags "${BUILD_VARS}" -o bin/glauth-win64 ${BUILD_FILES} && cd bin && sha256sum glauth-win64 > glauth-win64.sha256
verify:
cd bin && sha256sum *.sha256 -c && cd ../;
+98 -8
View File
@@ -1,6 +1,17 @@
# GLAuth: LDAP authentication server for developers
Go-lang LDAP Authentication (GLAuth) is a secure, easy-to-use, LDAP server w/ configurable backends.
[![Travis Build - Master](https://img.shields.io/travis/glauth/glauth.svg)](https://travis-ci.org/glauth/glauth)
[![Last Commit](https://img.shields.io/github/last-commit/glauth/glauth.svg)](https://github.com/glauth/glauth/graphs/commit-activity)
[![](https://img.shields.io/docker/build/glauth/glauth.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)
[![Donate via Paypal](https://img.shields.io/badge/Donate-PayPal-green.svg)](http://paypal.me/benyanke)
* Centrally manage accounts across your infrastructure
* Centrally manage SSH keys, Linux accounts, and passwords for cloud servers.
* Lightweight alternative to OpenLDAP and Active Directory for development, or a homelab.
@@ -14,11 +25,26 @@ Please base all PRs on [dev](https://github.com/nmcclain/glauth/tree/dev), not m
### Quickstart
This quickstart is a great way to try out GLAuth in a non-production environment. *Be warned that you should take the extra steps to setup SSL (TLS) for production use!*
1. Install GLAuth on a test server
1. Clone the repo: `git clone https://github.com/nmcclain/glauth`
2. Start the GLAuth server: `cd glauth; sudo bin/glauth32 -c sample-simple.cfg`
2. Test with traditional LDAP tools
1. `ldapsearch -LLL -H ldap://localhost:389 -D cn=serviceuser,ou=svcaccts,dc=glauth,dc=com -w mysecret -x -bdc=glauth,dc=com cn=hackers`
1. Download a precompiled binary from the [releases](https://github.com/glauth/glauth/releases) page.
2. Download the [example config file](https://github.com/glauth/glauth/blob/master/sample-simple.cfg).
3. Start the GLAuth server, referencing the path to the desired config file with `-c`.
- `sudo ./glauth64 -c sample-simple.cfg`
4. Test with traditional LDAP tools
- For example: `ldapsearch -LLL -H ldap://localhost:389 -D cn=serviceuser,ou=svcaccts,dc=glauth,dc=com -w mysecret -x -bdc=glauth,dc=com cn=hackers`
### Make Commands
Note - makefile uses git data to inject build-time variables. For best results, run in the context of the git repo.
*make all* - run build binaries for platforms
*make fast* - run build for only linux 64 bit
*make run* - wrapper for the 'go run' command, setting up the needed tooling
*make test* - run the integration test on linux64 binary
### Usage:
```
@@ -68,19 +94,23 @@ 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
### Optional Fields
* otherGroups
* Array of IDs of groups the user is a member of.
* Example: [5501, 5002]
* default = blank
* givenname
* First name
* Example: John
@@ -105,11 +135,48 @@ More configuration options are documented here: https://github.com/nmcclain/glau
* Specify an overridden home directory for the user
* Example: /home/itadmin
* default = /home/[username]
* otpsecret
* Specify OTP secret used to validate OTP passcode
* Example: 3hnvnk4ycv44glzigd6s25j4dougs3rk
* default = blank
* 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
* default = blank
### OpenSSH keys:
GLAuth can store a user's SSH authorized keys. Add one or more keys per user as shown above, then setup the goklp helper: https://github.com/appliedtrust/goklp
### Two Factor Authentication
GLAuth can be configured to accept OTP tokens as appended to a users password. Support is added for both **TOTP tokens** (often known by it's most prominent implementation, "Google Authenticator") and **Yubikey OTP tokens**.
When using 2FA, append the 2FA code to the end of the password when authenticating. For example, if your password is "monkey" and your otp is "123456", enter "monkey123456" as your password.
#### TOTP Configuration
To enable TOTP authentication on a user, you can use a tool [like this](https://freeotp.github.io/qrcode.html) to generate a QR code (pick 'Timeout' and optionally let it generate a random secret for you), which can be scanned and used with the [Google Authenticator](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en) app. To enable TOTP authentication, configure the `otpsecret` for the user with the TOTP secret.
#### 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/).
Now configure the the `yubikeyclientid` and `yubikeysecret` fields in the general section in the configuration file.
To enable Yubikey OTP authentication for a user, you must specify their Yubikey ID on the users `yubikey` field. The Yubikey ID is the first 12 characters of the Yubikey OTP, as explained in the below chart.
![Yubikey OTP](https://developers.yubico.com/OTP/otp_details.png)
When a user has been configured with either one of the OTP options, the OTP authentication is required for the user. If both are configured, either one will work.
### Backends:
For advanced users, GLAuth supports pluggable backends. Currently, it can use a local file, S3 or an existing LDAP infrastructure. In the future, we hope to have backends that support Mongo, SQL, and other datastores.
```toml
@@ -122,9 +189,32 @@ For advanced users, GLAuth supports pluggable backends. Currently, it can use a
Any of the architectures above will work for production. Just remember:
* Always use legit SSL certs for production!
# Other Architectures
A small note about other architectures: while I expect the code is, for the most part, system-independent, there is not a good (and free) CI system which can be easily used to continuously test releases on ARM, BSD, Linux-32bit, and Windows. As such, all of the non-linux-64bit packages are provided as is. The extent of testing on these packages consists solely of cross-compiling for these architectures from a linux 64 bit system.
We will accept PRs which fix bugs on these platforms, but be aware these binaries will not be tested regularly, and instead are provided for the convenience of those who feel comfortable with this.
### Building:
You'll need go-bindata to build GLAuth:
You'll need go-bindata to build GLAuth. Then use the Makefile.
```unix
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!
**Paypal**
[![Donate via Paypal](https://img.shields.io/badge/Donate-PayPal-green.svg)](http://paypal.me/benyanke)
**Bitcoin Address**
39z2Zkoc24LsuiqCQNFe7QrX4da3mzbGjK
-5
View File
@@ -1,5 +0,0 @@
---
- name: deploy glauth.yml
hosts: localhost
roles:
- glauth
-2
View File
@@ -1,2 +0,0 @@
localhost ansible_connection=local
-6
View File
@@ -1,6 +0,0 @@
Name: activate mkhomedir
Default: yes
Priority: 900
Session-Type: Additional
Session: required pam_mkhomedir.so
-7
View File
@@ -1,7 +0,0 @@
---
- name: restart nscd
sudo: true
service: name=nscd state=restarted
- name: restart sshd
sudo: true
service: name=ssh state=restarted
-56
View File
@@ -1,56 +0,0 @@
---
- name: install packages needed for glauth
sudo: true
apt: pkg={{item}} state=installed
with_items:
- ldap-auth-client
- nscd
# - ldap-utils
# - netcat
- name: configure nsswitch passwd
sudo: true
lineinfile: 'dest=/etc/nsswitch.conf state=present backrefs=yes regexp="^passwd:\s+compat" line="passwd: files ldap"'
notify: restart nscd
- name: configure nsswitch group
sudo: true
lineinfile: 'dest=/etc/nsswitch.conf state=present backrefs=yes regexp="^group:\s+compat" line="group: files ldap"'
notify: restart nscd
- name: configure nsswitch shadow
sudo: true
lineinfile: 'dest=/etc/nsswitch.conf state=present backrefs=yes regexp="^shadow:\s+compat" line="shadow: files ldap"'
notify: restart nscd
- name: configure pam common-session
sudo: true
lineinfile: 'dest=/etc/pam.d/common-session state=present regexp="^session\s+required\s+pam_mkhomedir.so" line="session required pam_mkhomedir.so"'
notify: restart nscd
- name: configure pam common-session-noninteractive
sudo: true
lineinfile: 'dest=/etc/pam.d/common-session-noninteractive state=present regexp="^session\s+required\s+pam_mkhomedir.so" line="session required pam_mkhomedir.so"'
notify: restart nscd
- name: copy over ldap.conf file
sudo: true
template: src=ldap.conf dest=/etc/ldap.conf mode=0600
notify: restart nscd
- name: copy over mkdhomedir pam config file
sudo: true
copy: src=mkhomedir dest=/usr/share/pam-configs/my_mkhomedir mode=0600
- name: download goklp command
sudo: true
get_url: url=https://github.com/AppliedTrust/goklp/releases/download/1.4/goklp dest=/usr/local/sbin/goklp mode=0755 owner=root
- name: copy over goklp config
sudo: true
template: src=goklp.ini dest=/usr/local/sbin/goklp.ini mode=0600 owner=root
#- name: disable ssh password auth
# sudo: true
# lineinfile: dest=/etc/ssh/sshd_config state=present regexp="^PasswordAuthentication" line="PasswordAuthentication no"
# notify: restart sshd
- name: configure ssh AuthorizedKeysCommand for glauth
sudo: true
lineinfile: dest=/etc/ssh/sshd_config state=present regexp="^AuthorizedKeysCommand\s+" line="AuthorizedKeysCommand /usr/local/sbin/goklp"
notify: restart sshd
- name: configure ssh AuthorizedKeysCommandUser for glauth
sudo: true
lineinfile: dest=/etc/ssh/sshd_config state=present regexp="^AuthorizedKeysCommandUser\s+" line="AuthorizedKeysCommandUser root"
notify: restart sshd
-5
View File
@@ -1,5 +0,0 @@
goklp_ldap_uri = {{ glauth_ldap_uri }}
goklp_ldap_bind_dn = {{ glauth_bind_dn }}
goklp_ldap_base_dn = {{ glauth_base_dn }}
goklp_ldap_bind_pw = {{ glauth_bind_pw }}
goklp_ldap_timeout_secs = 3
-7
View File
@@ -1,7 +0,0 @@
ldap_version 3
uri {{ glauth_ldap_uri }}
base {{ glauth_base_dn }}
binddn {{ glauth_bind_dn }}
bindpw {{ glauth_bind_pw }}
pam_password_prohibit_message Please change your password in the central system.
nss_initgroups_ignoreusers root,daemon,bin,sys,sync,games,man,lp,mail,news,uucp,proxy,www-data,backup,list,irc,gnats,nobody,libuuid,syslog,messagebus,landscape,sshd,pollinate,ubuntu
-6
View File
@@ -1,6 +0,0 @@
---
glauth_ldap_uri: ldap://localhost:389
glauth_bind_dn: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
glauth_base_dn: dc=glauth,dc=com
glauth_bind_pw: mysecret
glauth_timeout_secs: 5
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
-223
View File
@@ -1,223 +0,0 @@
package main
import (
"bytes"
"compress/gzip"
"fmt"
"io"
)
func bindata_read(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
gz.Close()
if err != nil {
return nil, fmt.Errorf("Read %q: %v", name, err)
}
return buf.Bytes(), nil
}
func assets_glauth_css() ([]byte, error) {
return bindata_read([]byte{
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x00, 0xff, 0x8c, 0x8f,
0xc1, 0x72, 0x83, 0x20, 0x14, 0x45, 0xf7, 0x7c, 0xc5, 0x9b, 0x71, 0x1d,
0x47, 0x0d, 0xa6, 0x60, 0xbe, 0x86, 0x07, 0x4f, 0xa5, 0x35, 0xe0, 0x00,
0x69, 0x3b, 0xed, 0xf4, 0xdf, 0x1b, 0xcd, 0x98, 0x29, 0x71, 0xd3, 0x2d,
0xf7, 0xdc, 0x7b, 0x78, 0xac, 0x7c, 0xbd, 0x5e, 0xd0, 0xa7, 0xe0, 0x1d,
0x7c, 0x33, 0x00, 0xed, 0x27, 0x1f, 0x3a, 0x28, 0x74, 0x75, 0x94, 0x0d,
0x9e, 0x6f, 0x2f, 0xa8, 0xf4, 0xdb, 0x10, 0xfc, 0xd5, 0x99, 0xc3, 0x16,
0x92, 0xee, 0xab, 0xbe, 0x3e, 0xb3, 0x1f, 0xc6, 0x8a, 0x98, 0x54, 0x8a,
0xb3, 0x72, 0x34, 0x65, 0xfd, 0x23, 0xe7, 0xb2, 0xa5, 0x1d, 0x52, 0x5e,
0x28, 0x05, 0xab, 0x9f, 0x50, 0x29, 0x0c, 0xfe, 0x41, 0xb3, 0x54, 0x62,
0x2b, 0xf1, 0xb4, 0x7c, 0x64, 0x56, 0xc6, 0x58, 0x37, 0x74, 0x20, 0xe6,
0xcf, 0x85, 0x2e, 0x22, 0x85, 0x77, 0x0a, 0xff, 0xc5, 0xd9, 0xd8, 0x2c,
0xe4, 0x06, 0x36, 0x52, 0x54, 0x28, 0xef, 0xd6, 0xa4, 0xec, 0x04, 0x69,
0xcc, 0x86, 0x5e, 0x7a, 0xa1, 0x85, 0x59, 0x3d, 0xf7, 0xd8, 0xac, 0xf1,
0x63, 0x95, 0x2f, 0xab, 0xfb, 0x7b, 0x01, 0x7a, 0xef, 0xd2, 0x21, 0xda,
0x2f, 0xea, 0xa0, 0xde, 0xd4, 0xdb, 0x44, 0xe9, 0xe7, 0x4c, 0x22, 0x88,
0x73, 0x65, 0x1e, 0xad, 0x0f, 0xb2, 0xc3, 0x98, 0x3a, 0x40, 0x3f, 0x65,
0xe6, 0x5b, 0xad, 0x44, 0xeb, 0x4c, 0xd6, 0xad, 0x4f, 0xaa, 0x12, 0xed,
0x33, 0x16, 0x49, 0x05, 0x9d, 0x5f, 0x52, 0x2b, 0xd4, 0x52, 0xaf, 0xff,
0xf8, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x91, 0x33, 0xcc, 0x6e, 0xee, 0x01,
0x00, 0x00,
},
"assets/glauth.css",
)
}
func assets_glauth_js() ([]byte, error) {
return bindata_read([]byte{
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x00, 0xff, 0xa4, 0x54,
0x5d, 0x6b, 0xdb, 0x30, 0x14, 0x7d, 0xcf, 0xaf, 0xb8, 0xf3, 0x02, 0x95,
0x59, 0x26, 0x87, 0xbd, 0x86, 0xee, 0xad, 0xd0, 0x0e, 0xd6, 0xc1, 0x96,
0x3f, 0xa0, 0x58, 0xd7, 0x89, 0x98, 0x3e, 0x8c, 0x2c, 0x27, 0x1b, 0xc5,
0xff, 0x7d, 0x57, 0x96, 0x9d, 0xb8, 0x69, 0x0b, 0x1d, 0x7e, 0x88, 0x13,
0xae, 0xce, 0x39, 0x3e, 0xbe, 0xe7, 0xc4, 0x47, 0xe1, 0x41, 0xd9, 0x80,
0xfe, 0x28, 0x34, 0xdc, 0xc2, 0x97, 0xf5, 0x7a, 0xbd, 0x59, 0x2c, 0x96,
0xac, 0x6a, 0x6d, 0x19, 0x94, 0xb3, 0x2c, 0x87, 0xa7, 0x05, 0x40, 0x5b,
0x4b, 0x11, 0xf0, 0x3b, 0x06, 0xaf, 0xca, 0x86, 0xe5, 0x1b, 0x1a, 0x15,
0x45, 0x1a, 0x6e, 0x85, 0xd2, 0x71, 0xd2, 0xd1, 0x67, 0x11, 0xe5, 0x02,
0x0d, 0x1e, 0x24, 0x89, 0x45, 0xa5, 0x51, 0xe7, 0x5a, 0xa1, 0x57, 0x8d,
0x68, 0x93, 0x26, 0x04, 0x5f, 0xf2, 0x3d, 0x86, 0x6f, 0xbf, 0x7e, 0x3c,
0x32, 0xc8, 0x0a, 0x89, 0xbb, 0x76, 0x5f, 0x10, 0xa0, 0xc9, 0x56, 0x70,
0x36, 0x23, 0x13, 0x2f, 0x31, 0x03, 0x9a, 0x5a, 0x93, 0x26, 0x51, 0xef,
0x85, 0x95, 0x1a, 0x77, 0x84, 0xe6, 0xa5, 0x33, 0xb5, 0xd2, 0xc8, 0x60,
0xc9, 0xb2, 0x8f, 0x4d, 0x10, 0xa1, 0xa9, 0x85, 0x45, 0xfd, 0x79, 0x44,
0x67, 0x39, 0x3f, 0x04, 0x43, 0x8e, 0xa1, 0x7f, 0x8a, 0x24, 0x15, 0x27,
0xf1, 0xf7, 0xed, 0x59, 0x94, 0x6e, 0x95, 0x8e, 0x9f, 0xcb, 0x8c, 0xec,
0x78, 0x99, 0xf0, 0xdf, 0x6b, 0x65, 0xbe, 0x8b, 0x19, 0x06, 0x28, 0x63,
0xf4, 0x33, 0x2d, 0x24, 0x8d, 0x17, 0x26, 0xba, 0x9c, 0x2e, 0xbc, 0x8a,
0x4d, 0xb8, 0x2a, 0xce, 0x5b, 0x0b, 0xcc, 0xee, 0xbc, 0x77, 0xfe, 0x43,
0x36, 0xa1, 0x0b, 0x7d, 0x12, 0x7f, 0x9b, 0x97, 0x02, 0x0d, 0x86, 0xad,
0x32, 0xe8, 0xda, 0xc0, 0x9e, 0x95, 0x68, 0x35, 0xf6, 0x76, 0xd0, 0xa0,
0x0a, 0x5e, 0xd7, 0x2d, 0x75, 0xf3, 0xdc, 0xb5, 0xd8, 0xcc, 0xeb, 0xa2,
0xc5, 0x59, 0x91, 0xc1, 0xa7, 0xb1, 0xb6, 0xaf, 0xb5, 0x4d, 0xf2, 0xca,
0xf9, 0x3b, 0x51, 0x1e, 0x2e, 0xe6, 0x70, 0x3c, 0x03, 0x50, 0x15, 0x30,
0xe4, 0x44, 0xfd, 0x3a, 0x68, 0x5c, 0x8e, 0xe0, 0xf2, 0x67, 0x88, 0x88,
0xcd, 0x30, 0xee, 0xfa, 0xef, 0xee, 0xff, 0xf2, 0x8b, 0x4a, 0x33, 0xc2,
0xeb, 0xb3, 0xe2, 0x07, 0x25, 0x91, 0xe5, 0xbc, 0xf6, 0x58, 0xa3, 0x95,
0x5b, 0x37, 0xe8, 0x92, 0x5c, 0x25, 0x24, 0x3e, 0x58, 0x16, 0x5f, 0x00,
0xef, 0x8a, 0x74, 0xa0, 0xcd, 0x0e, 0x33, 0x46, 0xf4, 0x5a, 0x92, 0x8b,
0xc9, 0x1e, 0x3c, 0xee, 0x55, 0x43, 0x88, 0x7b, 0xd4, 0x35, 0x7a, 0x76,
0x13, 0x79, 0x3f, 0x91, 0x1e, 0x50, 0x1d, 0xf1, 0x66, 0x12, 0x58, 0xe9,
0x48, 0xe7, 0x4f, 0x58, 0xc1, 0x4e, 0xbb, 0xf2, 0x77, 0xba, 0x65, 0x4c,
0xe7, 0xa4, 0xac, 0x74, 0x27, 0x6e, 0x9c, 0x41, 0x1b, 0x46, 0x27, 0x45,
0xe1, 0x31, 0xb4, 0xde, 0x42, 0x1a, 0x8f, 0x64, 0xda, 0x84, 0x77, 0xe6,
0xd1, 0x9d, 0xd8, 0xb0, 0xba, 0xb7, 0x50, 0xce, 0x1b, 0x11, 0x12, 0xa8,
0x43, 0xdd, 0xe0, 0xd3, 0x14, 0x3d, 0xc0, 0xfa, 0xc3, 0xe1, 0xd5, 0xf8,
0x2f, 0x00, 0x00, 0xff, 0xff, 0x1d, 0x12, 0x43, 0xad, 0x6a, 0x05, 0x00,
0x00,
},
"assets/glauth.js",
)
}
func assets_index_html() ([]byte, error) {
return bindata_read([]byte{
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x00, 0xff, 0xb4, 0x57,
0xeb, 0x6e, 0xdb, 0x36, 0x14, 0xfe, 0x9f, 0xa7, 0x60, 0xb4, 0xbf, 0x93,
0x34, 0xa7, 0xeb, 0x96, 0x15, 0xb2, 0x81, 0x2e, 0xcd, 0xd6, 0x00, 0xed,
0x1a, 0x24, 0x19, 0xb6, 0xa1, 0x28, 0x0a, 0x9a, 0x3c, 0xb6, 0xe8, 0x50,
0xa2, 0x42, 0x52, 0x8e, 0x0d, 0xc1, 0xef, 0xbe, 0x43, 0x51, 0xb6, 0x25,
0xcf, 0xf1, 0x0d, 0x2b, 0x10, 0xc4, 0xbc, 0x7c, 0xe7, 0x7e, 0xe1, 0x51,
0x72, 0xfe, 0xee, 0xd3, 0xd5, 0xc3, 0x3f, 0xb7, 0xd7, 0x24, 0xb5, 0x99,
0x1c, 0x9c, 0x25, 0xee, 0x87, 0x48, 0x9a, 0x8f, 0xfb, 0x01, 0xe4, 0xc1,
0xe0, 0x8c, 0x90, 0x24, 0x05, 0xca, 0xdd, 0x02, 0x97, 0x19, 0x58, 0x4a,
0x58, 0x4a, 0xb5, 0x01, 0xdb, 0x0f, 0x4a, 0x3b, 0x0a, 0x2f, 0x83, 0xf6,
0x55, 0x6a, 0x6d, 0x11, 0xc2, 0x53, 0x29, 0xa6, 0xfd, 0xe0, 0xef, 0xf0,
0xcf, 0xb7, 0xe1, 0x95, 0xca, 0x0a, 0x6a, 0xc5, 0x50, 0x42, 0x40, 0x98,
0xca, 0x2d, 0xe4, 0x48, 0x77, 0x73, 0xdd, 0x07, 0x3e, 0x86, 0x0e, 0x65,
0x4e, 0x33, 0xe8, 0x07, 0x53, 0x01, 0xcf, 0x85, 0xd2, 0xb6, 0x05, 0x7e,
0x16, 0xdc, 0xa6, 0x7d, 0x0e, 0x53, 0xc1, 0x20, 0xac, 0x37, 0xdf, 0x13,
0x91, 0x0b, 0x2b, 0xa8, 0x0c, 0x0d, 0xa3, 0x12, 0xfa, 0xbd, 0x2d, 0x8c,
0x38, 0x18, 0xa6, 0x45, 0x61, 0x85, 0xca, 0x5b, 0xbc, 0xb6, 0x00, 0x69,
0x69, 0x53, 0xa5, 0xbb, 0x18, 0x0f, 0xb2, 0xc2, 0x4a, 0x18, 0xfc, 0xfe,
0xe1, 0x2d, 0x22, 0xc8, 0xbd, 0xa5, 0xb6, 0x34, 0x49, 0xec, 0x0f, 0x1b,
0x84, 0x14, 0xf9, 0x23, 0xd1, 0x20, 0xfb, 0x81, 0xb1, 0x73, 0x09, 0x26,
0x05, 0x40, 0xcd, 0x53, 0x0d, 0xa3, 0x7e, 0xe0, 0x3c, 0x61, 0xde, 0xc4,
0x71, 0x46, 0x67, 0x8c, 0xe7, 0xd1, 0x50, 0x29, 0x6b, 0xac, 0xa6, 0x85,
0xdb, 0x30, 0x95, 0xc5, 0xab, 0x83, 0xf8, 0x55, 0x74, 0x11, 0xfd, 0x10,
0x33, 0x63, 0xd6, 0x67, 0x51, 0x26, 0x10, 0x65, 0xcc, 0x52, 0xdf, 0x6f,
0x26, 0x28, 0xb4, 0x29, 0x64, 0x70, 0x8c, 0x38, 0x6a, 0x30, 0xf4, 0x26,
0x1e, 0x4b, 0xe7, 0xb8, 0x86, 0xc8, 0x53, 0x9d, 0x87, 0x21, 0x79, 0xff,
0xf0, 0xf1, 0xc3, 0x6b, 0x62, 0x52, 0x91, 0x11, 0x9a, 0x73, 0x72, 0x07,
0xa6, 0x50, 0x39, 0x8f, 0x26, 0x86, 0xdc, 0x5c, 0x5f, 0x12, 0x53, 0x16,
0x2e, 0xb8, 0x44, 0x8d, 0x1a, 0x20, 0x48, 0x94, 0x9e, 0x5b, 0x53, 0x83,
0x33, 0xe0, 0x82, 0x92, 0xa7, 0x12, 0xb4, 0x00, 0x43, 0xc2, 0x70, 0xb0,
0x62, 0xfb, 0x59, 0x8c, 0x88, 0xb4, 0xc8, 0x82, 0xfc, 0xf2, 0xc5, 0x9f,
0xe2, 0xb9, 0x8f, 0x2f, 0x31, 0x9a, 0xad, 0x7d, 0xa0, 0x8c, 0x89, 0x1a,
0x3f, 0x38, 0xd3, 0x5d, 0x2e, 0xbf, 0x46, 0x65, 0xa6, 0x68, 0xfa, 0xcf,
0xd1, 0xc5, 0x7a, 0x5f, 0x1b, 0x3c, 0x41, 0xd5, 0x93, 0xd8, 0xb3, 0x39,
0x86, 0xab, 0xf6, 0x46, 0xc5, 0xbd, 0xe8, 0x47, 0xe4, 0xd9, 0xec, 0x5e,
0xe0, 0x98, 0x9c, 0x7f, 0x86, 0x9c, 0x8b, 0xd1, 0x17, 0x6f, 0x4e, 0x12,
0xfb, 0x5a, 0x72, 0xcb, 0xa1, 0xe2, 0x73, 0xa2, 0x95, 0x74, 0xc9, 0xaa,
0x58, 0xe9, 0xfc, 0xb0, 0x72, 0x25, 0x17, 0x53, 0xc2, 0x24, 0xba, 0xba,
0x1f, 0xb8, 0xbc, 0xa4, 0x22, 0x07, 0x4d, 0xea, 0x50, 0x85, 0x26, 0x55,
0xcf, 0x8c, 0x1a, 0x2c, 0x27, 0x4f, 0x9b, 0xe1, 0xe5, 0x92, 0xae, 0x09,
0xc2, 0x47, 0x3c, 0x22, 0x93, 0x32, 0x1b, 0x2a, 0xab, 0x55, 0x4e, 0x46,
0x4a, 0x13, 0x4a, 0x0a, 0x2d, 0x32, 0xaa, 0xe7, 0x04, 0xff, 0x3d, 0x82,
0x15, 0xf9, 0x18, 0xfd, 0x6d, 0x0c, 0x1d, 0x03, 0xc1, 0x6b, 0x2c, 0x23,
0x49, 0xac, 0x22, 0x94, 0xb9, 0x82, 0x59, 0xf9, 0xbe, 0xab, 0xc9, 0x8a,
0x63, 0xb0, 0xbc, 0x5d, 0x21, 0x04, 0x77, 0xa9, 0x42, 0xad, 0x29, 0x68,
0x0e, 0x32, 0x58, 0x52, 0xd4, 0x3b, 0x52, 0xff, 0x0f, 0x39, 0x8c, 0x68,
0x89, 0x41, 0x2c, 0x4a, 0x29, 0x43, 0x2d, 0xc6, 0xa9, 0xed, 0xb2, 0x89,
0x91, 0xcf, 0xfa, 0x20, 0x49, 0x7b, 0x4d, 0xe5, 0xa1, 0xcb, 0x7a, 0xad,
0xf3, 0x62, 0xf0, 0x90, 0x0a, 0x83, 0xba, 0x2a, 0x49, 0xf0, 0x17, 0x0d,
0xc5, 0xce, 0x00, 0x52, 0x15, 0xce, 0x7f, 0x24, 0x24, 0xb9, 0xb2, 0x98,
0x42, 0xc2, 0x02, 0xe6, 0x2f, 0x45, 0x07, 0x3b, 0xe3, 0x0b, 0xad, 0x78,
0x59, 0x5b, 0x76, 0x9e, 0xc4, 0xc5, 0xda, 0x57, 0x5e, 0xe4, 0x16, 0x4b,
0xb5, 0x7a, 0x6e, 0x29, 0xd7, 0x8d, 0x86, 0x0c, 0x67, 0x26, 0xec, 0x5d,
0x10, 0xb7, 0x32, 0x59, 0xf8, 0x53, 0xd7, 0x8a, 0xf4, 0x62, 0x50, 0xfb,
0x01, 0xb5, 0xbe, 0x78, 0xd9, 0x4b, 0xc1, 0x1e, 0x97, 0x6e, 0xf8, 0xa2,
0xa3, 0xe7, 0xd1, 0xfa, 0x0c, 0x29, 0x7b, 0xc4, 0x1c, 0x24, 0x06, 0xf4,
0x14, 0xf4, 0x0e, 0xcd, 0xfc, 0xfd, 0x49, 0xba, 0xfd, 0x57, 0x51, 0xbf,
0xf6, 0xf9, 0x18, 0xaf, 0x73, 0xd8, 0x65, 0xd6, 0x59, 0xbb, 0xd0, 0xba,
0x99, 0x13, 0x5a, 0xc8, 0x0a, 0x49, 0x2d, 0x66, 0xb7, 0x9d, 0x17, 0x98,
0xdd, 0x16, 0x66, 0x36, 0x9e, 0x85, 0x29, 0xf6, 0x07, 0x09, 0x43, 0x7c,
0x76, 0xd6, 0x88, 0xed, 0x01, 0xf2, 0x6c, 0x5c, 0x6d, 0x75, 0xd4, 0x76,
0x02, 0xf1, 0x6a, 0x89, 0xc2, 0xfe, 0xaf, 0x05, 0x0b, 0x06, 0x98, 0x19,
0xb3, 0x79, 0xe4, 0xcc, 0xc6, 0xe4, 0x78, 0x83, 0xa5, 0x8b, 0x98, 0x01,
0xa9, 0xaa, 0xce, 0xf9, 0x62, 0x91, 0x0c, 0xf5, 0x56, 0x93, 0xd7, 0xb5,
0xbe, 0xdd, 0xa6, 0x53, 0xcc, 0x79, 0x59, 0xcf, 0xaf, 0x23, 0x5d, 0xbf,
0x51, 0x3c, 0x1a, 0x8a, 0x9c, 0x7f, 0xd5, 0xf0, 0x64, 0x36, 0x55, 0xde,
0x02, 0xe9, 0x68, 0x7f, 0x30, 0x73, 0x53, 0x32, 0x86, 0xed, 0x01, 0xf6,
0x49, 0x58, 0xe1, 0x4e, 0x13, 0x03, 0x5a, 0x2b, 0xbd, 0x4f, 0x86, 0x07,
0x1d, 0x2d, 0xc0, 0x00, 0xd5, 0x2c, 0xdd, 0xe3, 0xa6, 0x16, 0xe8, 0x54,
0x01, 0x07, 0xb8, 0x6a, 0x13, 0x79, 0xaa, 0xa8, 0x7d, 0xee, 0xea, 0xc0,
0x8e, 0x16, 0xc2, 0xa4, 0xda, 0x69, 0x85, 0xbf, 0x6f, 0xb1, 0xdd, 0x95,
0xfe, 0xbe, 0x99, 0x9c, 0x52, 0x00, 0x55, 0xf5, 0x1d, 0x50, 0x96, 0x12,
0x2f, 0xbd, 0xe9, 0x5e, 0x51, 0xc3, 0x70, 0xb1, 0x38, 0xdb, 0x59, 0xd0,
0x55, 0x75, 0xcf, 0xdc, 0x33, 0xb9, 0x58, 0xe0, 0xbb, 0x5d, 0x55, 0xef,
0x95, 0xb1, 0x6e, 0xcc, 0xc3, 0x6d, 0x55, 0xdd, 0xe2, 0xe8, 0x81, 0xda,
0xaf, 0xcc, 0xbb, 0xc5, 0x47, 0x70, 0xb1, 0xc0, 0x85, 0x9f, 0xed, 0x70,
0xd9, 0x72, 0x58, 0x55, 0xc5, 0x4e, 0x8b, 0x46, 0xde, 0x0e, 0x4b, 0xb1,
0xb1, 0x9d, 0xd4, 0xb6, 0x12, 0xeb, 0x9a, 0xd4, 0xba, 0xad, 0x2c, 0xcd,
0xb6, 0xf8, 0xbc, 0xb5, 0xad, 0xb4, 0x7a, 0x69, 0xe3, 0x66, 0x43, 0xb3,
0x7c, 0x79, 0xa3, 0x0a, 0x24, 0xff, 0x54, 0x2c, 0x16, 0xce, 0x01, 0xee,
0x17, 0xe7, 0x54, 0x7e, 0x18, 0xfa, 0x57, 0xac, 0xb0, 0x77, 0x7f, 0x1c,
0x43, 0x71, 0x5f, 0x27, 0xd9, 0x1d, 0x3c, 0x45, 0xbf, 0x09, 0x69, 0x41,
0xef, 0xa1, 0x75, 0x24, 0x38, 0x06, 0xe2, 0xd3, 0x7f, 0xa5, 0x38, 0x1c,
0x00, 0x5e, 0xf2, 0x77, 0x24, 0xe6, 0x00, 0xfc, 0x95, 0x14, 0xf8, 0xf4,
0xdf, 0xdc, 0x1e, 0x00, 0xe5, 0x18, 0x80, 0x3b, 0xc0, 0x30, 0x88, 0x29,
0x90, 0xbf, 0x52, 0xc8, 0x37, 0x69, 0x70, 0xa7, 0xdb, 0x21, 0x69, 0xe7,
0x40, 0x37, 0x64, 0x2f, 0x64, 0x44, 0x67, 0x6e, 0xa4, 0x13, 0x3a, 0x8b,
0xc6, 0x4a, 0x8d, 0x25, 0xd0, 0x42, 0x98, 0x7a, 0x78, 0x74, 0x67, 0xb1,
0x14, 0x43, 0x13, 0x4f, 0xdc, 0xa4, 0x3b, 0xc7, 0x29, 0xb2, 0x87, 0x7f,
0xcd, 0xee, 0xa5, 0x29, 0x72, 0x1b, 0xf7, 0x43, 0xe7, 0xfd, 0xc9, 0xe6,
0x77, 0xc5, 0x81, 0xfc, 0x91, 0xdf, 0xc4, 0xb8, 0xa2, 0x2f, 0xf9, 0x48,
0x52, 0x0d, 0x1b, 0xea, 0xaf, 0x73, 0x1b, 0x19, 0xc6, 0x28, 0x08, 0x45,
0xb5, 0xce, 0xfe, 0x3f, 0x41, 0x99, 0x72, 0xa3, 0x9d, 0x17, 0x72, 0x19,
0xbd, 0x6a, 0xf6, 0xf8, 0x25, 0x68, 0xd3, 0x50, 0x2a, 0xf7, 0x01, 0x78,
0x90, 0xb4, 0xee, 0x77, 0xcb, 0x26, 0x38, 0x89, 0x7d, 0x68, 0x93, 0xd8,
0x7f, 0x06, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x08, 0x9c, 0x44, 0x70,
0x17, 0x0f, 0x00, 0x00,
},
"assets/index.html",
)
}
// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
if f, ok := _bindata[name]; ok {
return f()
}
return nil, fmt.Errorf("Asset %s not found", name)
}
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string] func() ([]byte, error) {
"assets/glauth.css": assets_glauth_css,
"assets/glauth.js": assets_glauth_js,
"assets/index.html": assets_index_html,
}
+135 -15
View File
@@ -4,19 +4,23 @@ 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
cfg *config
yubikeyAuth *yubigo.YubiAuth
}
func newConfigHandler(cfg *config) Backend {
func newConfigHandler(cfg *config, yubikeyAuth *yubigo.YubiAuth) Backend {
handler := configHandler{
cfg: cfg,
}
cfg: cfg,
yubikeyAuth: yubikeyAuth}
return handler
}
@@ -24,12 +28,15 @@ func newConfigHandler(cfg *config) Backend {
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("Bind request as %s from %s", bindDN, conn.RemoteAddr().String())
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
// 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), ",")
@@ -54,7 +61,7 @@ func (h configHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resultC
}
}
if !found {
log.Warning(fmt.Sprintf("Bind Error: User %s not found.", user))
log.Warning(fmt.Sprintf("Bind Error: User %s not found.", userName))
return ldap.LDAPResultInvalidCredentials, nil
}
// find the group
@@ -67,7 +74,7 @@ func (h configHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resultC
}
}
if !found {
log.Warning(fmt.Sprintf("Bind Error: Group %s not found.", group))
log.Warning(fmt.Sprintf("Bind Error: Group %s not found.", groupName))
return ldap.LDAPResultInvalidCredentials, nil
}
// validate group membership
@@ -76,15 +83,77 @@ func (h configHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resultC
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)) {
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)
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 {
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)) {
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())
log.Debug(fmt.Sprintf("Bind success as %s from %s", bindDN, conn.RemoteAddr().String()))
return ldap.LDAPResultSuccess, nil
}
@@ -93,7 +162,7 @@ func (h configHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn
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)
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
@@ -141,11 +210,10 @@ func (h configHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn
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) {
if u.Disabled {
attrs = append(attrs, &ldap.EntryAttribute{"accountStatus", []string{"inactive"}})
} else {
attrs = append(attrs, &ldap.EntryAttribute{"accountStatus", []string{"active"}})
@@ -172,7 +240,7 @@ func (h configHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn
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(u.OtherGroups)})
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})
}
@@ -181,7 +249,7 @@ func (h configHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn
}
}
stats_frontend.Add("search_successes", 1)
log.Debug("AP: Search OK: %s", searchReq.Filter)
log.Debug(fmt.Sprintf("AP: Search OK: %s", searchReq.Filter))
return ldap.ServerSearchResult{entries, []string{}, []ldap.Control{}, ldap.LDAPResultSuccess}, nil
}
@@ -207,10 +275,28 @@ func (h configHandler) getGroupMembers(gid int) []string {
}
}
}
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
}
@@ -228,14 +314,34 @@ func (h configHandler) getGroupMemberIDs(gid int) []string {
}
}
}
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 {
@@ -244,12 +350,26 @@ func (h configHandler) getGroupDNs(gids []int) []string {
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
}
+180 -34
View File
@@ -3,17 +3,25 @@ package main
import (
"expvar"
"fmt"
"os"
"strings"
"github.com/BurntSushi/toml"
"github.com/GeertJohan/yubigo"
"github.com/docopt/docopt-go"
"github.com/nmcclain/ldap"
"github.com/op/go-logging"
"launchpad.net/goamz/aws"
"launchpad.net/goamz/s3"
"os"
"strings"
"gopkg.in/amz.v1/aws"
"gopkg.in/amz.v1/s3"
)
var version = "0.1"
// Set with buildtime vars
var LastGitTag string
var BuildTime string
var GitCommit string
var GitClean string
var GitBranch string
var GitTagIsCommit string
const programName = "glauth"
@@ -54,11 +62,21 @@ type configBackend struct {
}
type configFrontend struct {
AllowedBaseDNs []string // For LDAP backend only
Listen string
Cert string
Key string
Listen string
TLS bool
}
type configLDAP struct {
Enabled bool
Listen string
}
type configLDAPS struct {
Enabled bool
Listen string
Cert string
Key string
}
type configAPI struct {
Cert string
Enabled bool
@@ -68,28 +86,36 @@ type configAPI struct {
TLS bool
}
type configUser struct {
Name string
OtherGroups []int
PassSHA256 string
PrimaryGroup int
SSHKeys []string
Disabled bool
UnixID int
Mail string
LoginShell string
GivenName string
SN string
Homedir string
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 configGroup struct {
Name string
UnixID int
Name string
UnixID int
IncludeGroups []int
}
type config struct {
API configAPI
Backend configBackend
Debug bool
YubikeyClientID string
YubikeySecret string
Frontend configFrontend
LDAP configLDAP
LDAPS configLDAPS
Groups []configGroup
Syslog bool
Users []configUser
@@ -101,6 +127,48 @@ type config struct {
var log = logging.MustGetLogger(programName)
// Reads builtime vars and returns a full string containing info about
// the currently running version of the software. Primarily used by the
// --version flag at runtime.
func getVersionString() string {
var versionstr string
versionstr = "GLauth"
// Notate the git context of the build
switch {
// If a release, use the tag
case GitClean == "1" && GitTagIsCommit == "1":
versionstr += " " + LastGitTag + "\n\n"
// If this branch had a tag before, mention the branch and the tag to give a rough idea of the base version
case len(GitBranch) > 1 && len(LastGitTag) > 1:
versionstr += "\nNon-release build from branch " + GitBranch + ", based on tag " + LastGitTag + "\n\n"
// If no previous tag specified, just mention the branch
case len(GitBranch) > 1:
versionstr += "\nNon-release build from branch " + GitBranch + "\n\n"
// Fallback message, if all else fails
default:
versionstr += "\nNon-release build\n\n"
}
// Include build time
if len(BuildTime) > 1 {
versionstr += "Build time: " + BuildTime + "\n"
}
// Add commit hash
if GitClean == "1" && len(GitCommit) > 1 {
versionstr += "Commit: " + GitCommit + "\n"
}
return versionstr
}
func main() {
stderr := initLogging()
log.Debug("AP start")
@@ -114,7 +182,7 @@ func main() {
}
// stats
stats_general.Set("version", stringer(version))
stats_general.Set("version", stringer(LastGitTag))
// web API
if cfg.API.Enabled {
@@ -122,6 +190,16 @@ func main() {
go RunAPI(cfg)
}
yubiAuth := (*yubigo.YubiAuth)(nil)
if len(cfg.YubikeyClientID) > 0 && len(cfg.YubikeySecret) > 0 {
yubiAuth, err = yubigo.NewYubiAuth(cfg.YubikeyClientID, cfg.YubikeySecret)
if err != nil {
log.Fatalf("Yubikey Auth failed")
}
}
// configure the backend
s := ldap.NewServer()
s.EnforceLDAP = true
@@ -130,7 +208,7 @@ func main() {
case "ldap":
handler = newLdapHandler(cfg)
case "config":
handler = newConfigHandler(cfg)
handler = newConfigHandler(cfg, yubiAuth)
default:
log.Fatalf("Unsupported backend %s - must be 'config' or 'ldap'.", cfg.Backend.Datastore)
}
@@ -139,29 +217,48 @@ func main() {
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())
if cfg.LDAP.Enabled {
// Dont block if also starting a LDAPS server afterwards
shouldBlock := !cfg.LDAPS.Enabled
if shouldBlock {
startLDAP(&cfg.LDAP, s)
} else {
go startLDAP(&cfg.LDAP, s)
}
}
if cfg.LDAPS.Enabled {
// Always block here
startLDAPS(&cfg.LDAPS, s)
}
log.Critical("AP exit")
}
func startLDAP(ldapConfig *configLDAP, server *ldap.Server) {
log.Notice(fmt.Sprintf("LDAP server listening on %s", ldapConfig.Listen))
if err := server.ListenAndServe(ldapConfig.Listen); err != nil {
log.Fatalf("LDAP Server Failed: %s", err.Error())
}
}
func startLDAPS(ldapsConfig *configLDAPS, server *ldap.Server) {
log.Notice(fmt.Sprintf("LDAPS server listening on %s", ldapsConfig.Listen))
if err := server.ListenAndServeTLS(ldapsConfig.Listen, ldapsConfig.Cert, ldapsConfig.Key); err != nil {
log.Fatalf("LDAP Server Failed: %s", err.Error())
}
}
// doConfig reads the cli flags and config file
func doConfig() (*config, error) {
cfg := config{}
// setup defaults
cfg.Frontend.TLS = true
cfg.LDAP.Enabled = false
cfg.LDAPS.Enabled = true
// parse the command-line args
args, err := docopt.Parse(usage, nil, true, version, false)
args, err := docopt.Parse(usage, nil, true, getVersionString(), false)
if err != nil {
return &cfg, err
}
@@ -206,6 +303,55 @@ func doConfig() (*config, error) {
logging.SetLevel(logging.DEBUG, programName)
log.Debug("Debugging enabled")
}
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"
+32
View File
@@ -0,0 +1,32 @@
package main
// Just the beginning of testing - more to be added
import (
"testing"
)
func TestGetVersionString(t *testing.T) {
// Setup config and check output
LastGitTag = "7"
BuildTime = "20180616_042045Z"
GitCommit = "a9837f3112fc049fd1e9faca6f56d8be1bec91bd"
GitClean = "1"
GitBranch = "dev"
GitTagIsCommit = "0"
versionOutput := getVersionString()
if versionOutput != "7" {
t.Errorf("Finish writing this test, output was: %s", versionOutput)
}
// TODO: write these cases
// Cases to test:
// 1) if GitTagIsCommit=1 - check for release-based strings (this build is release)
// 2) if GitClean=1 - check for "Commit: " as well as value of commit str
// 3) if GitClean=0 - Ensure "Commit: " is NOT there, as well ensure value of commit str is not there
// 4) do additional checks for release behavior, as well as branch name
}
+12 -12
View File
@@ -66,27 +66,27 @@ 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())
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())
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())
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())
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)
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 {
@@ -105,24 +105,24 @@ func (h ldapHandler) Search(boundDN string, searchReq ldap.SearchRequest, conn n
searchReq.Controls,
)
log.Debug("Search req to backend: %# v", pretty.Formatter(search))
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))
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))
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))
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))
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 {
@@ -229,7 +229,7 @@ func (h ldapHandler) ping() error {
}
h.lock.Unlock()
}
log.Debug("Server health: %# v", pretty.Formatter(h.servers))
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()))
@@ -258,7 +258,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))
log.Debug(fmt.Sprintf("Best server: %# v", pretty.Formatter(favorite)))
return favorite, nil
}
+59 -8
View File
@@ -7,12 +7,31 @@ debug = true
#syslog = true
#################
# The frontend section controls how clients connect to the proxy.
[frontend]
tls = false # enable TLS for production!!
listen = "localhost:389"
cert = "cert.pem"
key = "key.pem"
# yubikeyclientid = "yubi-api-clientid"
# yubikeysecret = "yubi-api-secret"
#################
# 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 = "localhost:3893"
[ldaps]
enabled = true
listen = "localhost:3894"
cert = "certs/server.crt"
key = "certs/server.key"
#################
# The backend section controls the data store.
@@ -29,12 +48,40 @@ debug = true
primarygroup = 5501
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
# This user record shows all of the possible fields available
[[users]]
name = "johndoe"
givenname="John"
sn="Doe"
mail = "jdoe@example.com"
unixid = 5002
primarygroup = 5501
loginShell = "/bin/sh"
homeDir = "/root"
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
passappsha256 = ["6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a"]
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"
unixid = 5002
unixid = 5003
primarygroup = 5502
passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret
# Test user showing 2 factor auth authentication
[[users]]
name = "otpuser"
unixid = 5004
primarygroup = 5501
passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret
otpsecret = "3hnvnk4ycv44glzigd6s25j4dougs3rk"
yubikey = "vvjrcfalhlaa"
#################
# The groups section contains a hardcoded list of valid users.
[[groups]]
@@ -45,6 +92,11 @@ debug = true
name = "svcaccts"
unixid = 5502
[[groups]]
name = "vpn"
unixid = 5503
includegroups = [ 5501 ]
#################
# Enable and configure the optional REST API here.
[api]
@@ -53,4 +105,3 @@ debug = true
listen = "localhost:5555"
cert = "cert.pem"
key = "key.pem"
+70
View File
@@ -0,0 +1,70 @@
#################
# glauth.conf
#################
# General configuration.
debug = true
#syslog = true
#################
# The frontend section controls how clients connect to the proxy.
[frontend]
tls = false # enable TLS for production!!
listen = "localhost:389"
cert = "cert.pem"
key = "key.pem"
#################
# The backend section controls the data store.
[backend]
datastore = "config"
baseDN = "dc=glauth,dc=com"
#################
# The users section contains a hardcoded list of valid users.
# to create a passSHA256: echo -n "mysecret" | openssl dgst -sha256
[[users]]
name = "hackers"
unixid = 5001
primarygroup = 5501
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
# This user record shows all of the possible fields available
[[users]]
name = "johndoe"
givenname="John"
sn="Doe"
mail = "jdoe@example.com"
unixid = 5002
primarygroup = 5501
loginShell = "/bin/sh"
homeDir = "/root"
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
sshkeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwVw3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4hC/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLVIfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XBYnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+txSuhw== rsa-key-20160209"]
[[users]]
name = "serviceuser"
unixid = 5003
primarygroup = 5502
passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret
#################
# The groups section contains a hardcoded list of valid users.
[[groups]]
name = "superheros"
unixid = 5501
[[groups]]
name = "svcaccts"
unixid = 5502
#################
# Enable and configure the optional REST API here.
[api]
enabled = true
tls = false # enable TLS for production!!
listen = "localhost:5555"
cert = "cert.pem"
key = "key.pem"
+28
View File
@@ -0,0 +1,28 @@
#!/bin/sh
# Copy config file if it doesn't exist so that the app can start
if [ ! -f /app/config/config.cfg ] ; then
echo "Config file not found at /app/config/config.cfg"
echo "Copying example configuration file to run."
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."
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
img="localhost/glauth-dev"
sudo docker rmi -f "$img"
sudo docker build -f Dockerfile -t "$img" .
sudo docker run -P "$img"
+1
View File
@@ -0,0 +1 @@
/test-results
@@ -0,0 +1,146 @@
dn: cn=hackers,ou=superheros,dc=glauth,dc=com
cn: hackers
uid: hackers
ou: superheros
uidNumber: 5001
accountStatus: active
objectClass: posixAccount
loginShell: /bin/bash
homeDirectory: /home/hackers
description: hackers via LDAP
gecos: hackers via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
dn: cn=johndoe,ou=superheros,dc=glauth,dc=com
cn: johndoe
uid: johndoe
givenName: John
sn: Doe
ou: superheros
uidNumber: 5002
accountStatus: active
mail: jdoe@example.com
objectClass: posixAccount
loginShell: /bin/sh
homeDirectory: /root
description: johndoe via LDAP
gecos: johndoe via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwV
w3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4h
C/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLV
IfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XB
YnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+
txSuhw== rsa-key-20160209
dn: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
cn: jamesdoe
uid: jamesdoe
givenName: James
sn: Doe
ou: superheros
uidNumber: 5003
accountStatus: active
mail: jdoe2@example.com
objectClass: posixAccount
loginShell: /bin/sh
homeDirectory: /home/jamesdoe
description: jamesdoe via LDAP
gecos: jamesdoe via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwV
w3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4h
C/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLV
IfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XB
YnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+
txSuhw== rsa-key-20160209
dn: cn=alexdoe,ou=superheros,dc=glauth,dc=com
cn: alexdoe
uid: alexdoe
givenName: Alex
sn: Doe
ou: superheros
uidNumber: 5004
accountStatus: active
mail: adoe@example.com
objectClass: posixAccount
loginShell: /bin/sh
homeDirectory: /home/alexdoe
description: alexdoe via LDAP
gecos: alexdoe via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
dn: cn=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
loginShell: /bin/sh
homeDirectory: /home/jackdoe
description: jackdoe via LDAP
gecos: jackdoe via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
dn: cn=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
loginShell: /bin/sh
homeDirectory: /home/sarahdoe
description: sarahdoe via LDAP
gecos: sarahdoe via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
dn: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
cn: serviceuser
uid: serviceuser
ou: svcaccts
uidNumber: 5003
accountStatus: active
objectClass: posixAccount
loginShell: /bin/bash
homeDirectory: /home/serviceuser
description: serviceuser via LDAP
gecos: serviceuser via LDAP
gidNumber: 5502
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=svcaccts,ou=groups,dc=glauth,dc=com
@@ -0,0 +1,98 @@
dn: cn=superheros,ou=groups,dc=glauth,dc=com
cn: superheros
description: superheros via LDAP
gidNumber: 5501
objectClass: posixGroup
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=hackers,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=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
gidNumber: 5502
objectClass: posixGroup
uniqueMember: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
memberUid: serviceuser
dn: cn=vpnaccess,ou=groups,dc=glauth,dc=com
cn: vpnaccess
description: vpnaccess via LDAP
gidNumber: 5503
objectClass: posixGroup
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=hackers,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=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
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
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
gidNumber: 5506
objectClass: posixGroup
dn: cn=fulltime,ou=groups,dc=glauth,dc=com
cn: fulltime
description: fulltime via LDAP
gidNumber: 5507
objectClass: posixGroup
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
uniqueMember: cn=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
@@ -0,0 +1,16 @@
dn: cn=hackers,ou=superheros,dc=glauth,dc=com
cn: hackers
uid: hackers
ou: superheros
uidNumber: 5001
accountStatus: active
objectClass: posixAccount
loginShell: /bin/bash
homeDirectory: /home/hackers
description: hackers via LDAP
gecos: hackers via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
@@ -0,0 +1,25 @@
dn: cn=johndoe,ou=superheros,dc=glauth,dc=com
cn: johndoe
uid: johndoe
givenName: John
sn: Doe
ou: superheros
uidNumber: 5002
accountStatus: active
mail: jdoe@example.com
objectClass: posixAccount
loginShell: /bin/sh
homeDirectory: /root
description: johndoe via LDAP
gecos: johndoe via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwV
w3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4h
C/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLV
IfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XB
YnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+
txSuhw== rsa-key-20160209
@@ -0,0 +1,15 @@
dn: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
cn: serviceuser
uid: serviceuser
ou: svcaccts
uidNumber: 5003
accountStatus: active
objectClass: posixAccount
loginShell: /bin/bash
homeDirectory: /home/serviceuser
description: serviceuser via LDAP
gecos: serviceuser via LDAP
gidNumber: 5502
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=svcaccts,ou=groups,dc=glauth,dc=com
@@ -0,0 +1,27 @@
dn: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
cn: jamesdoe
uid: jamesdoe
givenName: James
sn: Doe
ou: superheros
uidNumber: 5003
accountStatus: active
mail: jdoe2@example.com
objectClass: posixAccount
loginShell: /bin/sh
homeDirectory: /home/jamesdoe
description: jamesdoe via LDAP
gecos: jamesdoe via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwV
w3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4h
C/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLV
IfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XB
YnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+
txSuhw== rsa-key-20160209
@@ -0,0 +1,21 @@
dn: cn=alexdoe,ou=superheros,dc=glauth,dc=com
cn: alexdoe
uid: alexdoe
givenName: Alex
sn: Doe
ou: superheros
uidNumber: 5004
accountStatus: active
mail: adoe@example.com
objectClass: posixAccount
loginShell: /bin/sh
homeDirectory: /home/alexdoe
description: alexdoe via LDAP
gecos: alexdoe via LDAP
gidNumber: 5501
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
+257
View File
@@ -0,0 +1,257 @@
#!/bin/bash
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)"
fi
# This script requires that "$TRAVIS_BUILD_DIR" is set to the repo root
# Ensure ldap utils are installed (for example - when running this outside of travis)
if [[ ! `which ldapsearch` ]]; then
sudo apt-get -qq update && sudo apt-get -qq install -y ldap-utils || exit 1;
fi
# Display version string
echo "";
echo ""
echo "Version string of tested binary:"
"$TRAVIS_BUILD_DIR/bin/glauth64" --version
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"
# Sleep 2 sec, to ensure it comes online successfully and stays up
sleep 2;
# Check if process is still running before continuing
ps aux | grep -v "grep" | grep "$glauthPid" &> /dev/null || FAIL="1"
if [[ "$FAIL" = "1" ]] ; then
echo "Integration test FAILED - process did not remain running > 2 sec"
exit 255;
fi
FAIL="0"
# Used for OTP testing
# Arguments:
# $1 - Full Bind DN
# $2 - Full Bind PW
# $3 - query to run
# $4 - test name
function bindTest() {
ldapsearch -LLL \
-H ldap://localhost:3893 \
-D "$1" \
-w "$2" \
-x -bdc=glauth,dc=com \
"$3" > /dev/null
exitCode="$?"
if [[ "$exitCode" = "0" ]] ; then
echo " - PASS : Bind test '$4'";
else
echo " - FAIL : Bind test '$4'";
FAIL="1"
fi
}
# Arguments:
# $1 - query
# $2 - name of snapshot
function snapshotTest() {
goodResults="$TRAVIS_BUILD_DIR/scripts/travis/good-results"
testResults="$TRAVIS_BUILD_DIR/scripts/travis/test-results"
mkdir "$testResults" &> /dev/null
# Run tests here
ldapCmd="ldapsearch -LLL \
-H ldap://localhost:3893 \
-D cn=serviceuser,ou=svcaccts,dc=glauth,dc=com \
-w mysecret \
-x -bdc=glauth,dc=com \
$1";
# Run the ldap command, pipe to file
# "$ldapCmd" &> "$testResults/$2"
# Useful for debugging - output the command run
# echo "$ldapCmd";
ldapsearch -LLL \
-H ldap://localhost:3893 \
-D cn=serviceuser,ou=svcaccts,dc=glauth,dc=com \
-w mysecret \
-x -bdc=glauth,dc=com \
$1 > "$testResults/$2"
# Handle the first-run case, saving the test results
if [ ! -f "$goodResults/$2" ]; then
echo " - FIRST RUN - SAVING SNAPSHOT, RUN AGAIN TO PASS : '$2'";
# Copy the results to the goodResults dir, for future runs.
cp "$testResults/$2" "$goodResults/$2";
# NOTE: fail=1 must still be set, otherwise CI runs would succeed when they shouldn't
FAIL="1"
else
THISFAIL="0"
diff -u "$goodResults/$2" "$testResults/$2" || THISFAIL="1"
if [[ "$CLEANUP" = "cleanup" ]] ; then
rm -rf "$testResults"
fi
if [[ "$THISFAIL" = "0" ]] ; then
echo " - PASS : snapshot '$2'";
else
echo " - FAIL : snapshot '$2'";
FAIL="1"
fi
fi
}
echo "";
echo "";
echo "RUNNING TESTS:"
echo "";
#################
## TEST RUNS
#################
## Query output tests
# Regular single-user fetches
snapshotTest "cn=hackers" userFetchTest0
snapshotTest "cn=johndoe" userFetchTest1
snapshotTest "cn=serviceuser" userFetchTest2
snapshotTest "cn=jamesdoe" userFetchTest3
snapshotTest "cn=alexdoe" userFetchTest4
# Test result of fetching nonexistent users
snapshotTest "cn=fakeuser" userFetchNonexistentUser0
snapshotTest "cn=janedoe" userFetchNonexistentUser1
# List all posixgroups
snapshotTest "objectClass=posixgroup" posixGroupList0
snapshotTest "objectClass=posixaccount" posixAccountList0
## 2FA Bind Test
# Fetch the OTP for this moment
otpCode="$(oathtool --totp -b -d 6 '3hnvnk4ycv44glzigd6s25j4dougs3rk')"
pass="ThisAloneWontWork!"
# Try to bind with it
bindTest "cn=alexdoe,ou=superheros,dc=glauth,dc=com" \
"$pass$otpCode" \
"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
#############
echo "";
echo "";
# Kill saved PID when done
# However - throw the fail flag if the process isn't there (ie, exited prematurely)
echo "Killing glauth"
kill "$glauthPid" || FAIL="1"
if [[ "$FAIL" = "0" ]] ; then
echo "Integration test success"
exit 0;
else
echo "Integration test FAILED"
exit 255;
fi
+160
View File
@@ -0,0 +1,160 @@
#################
# glauth.conf
#################
# General configuration.
debug = true
#syslog = true
#################
# The frontend section controls how clients connect to the proxy.
[frontend]
tls = false # enable TLS for production!!
# 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"
#################
# The backend section controls the data store.
[backend]
datastore = "config"
baseDN = "dc=glauth,dc=com"
#################
# The users section contains a hardcoded list of valid users.
# to create a passSHA256: echo -n "mysecret" | openssl dgst -sha256
[[users]]
name = "hackers"
unixid = 5001
primarygroup = 5501
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
# This user record shows all of the possible fields available
[[users]]
name = "johndoe"
givenname="John"
sn="Doe"
mail = "jdoe@example.com"
unixid = 5002
primarygroup = 5501
loginShell = "/bin/sh"
homeDir = "/root"
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
sshkeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwVw3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4hC/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLVIfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XBYnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+txSuhw== rsa-key-20160209"]
# This user record tests lots of groups, including group inheritence
[[users]]
name = "jamesdoe"
givenname="James"
sn="Doe"
mail = "jdoe2@example.com"
unixid = 5003
primarygroup = 5501
loginShell = "/bin/sh"
otherGroups = [5505,5507]
homeDir = "/home/jamesdoe"
passsha256 = "ff522a502031e5f37054892d642c2c0f36af6c4e8f62225470d4a1a99f3c9c2a" # dogood1
sshkeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwVw3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4hC/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLVIfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XBYnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+txSuhw== rsa-key-20160209"]
# 2FA test user
[[users]]
name = "alexdoe"
givenname="Alex"
sn="Doe"
mail = "adoe@example.com"
unixid = 5004
primarygroup = 5501
loginShell = "/bin/sh"
otherGroups = [5505,5507]
otpsecret = "3hnvnk4ycv44glzigd6s25j4dougs3rk"
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
primarygroup = 5502
passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret
#################
# The groups section contains a hardcoded list of valid users.
[[groups]]
name = "superheros"
unixid = 5501
[[groups]]
name = "svcaccts"
unixid = 5502
[[groups]]
name = "vpnaccess"
unixid = 5503
includegroups = [ 5501 ]
[[groups]]
name = "allaccs"
unixid = 5504
includegroups = [ 5501, 5502 ]
[[groups]]
name = "mailadmin"
unixid = 5505
[[groups]]
name = "webmail"
unixid = 5506
[[groups]]
name = "fulltime"
unixid = 5507
#################
# Enable and configure the optional REST API here.
[api]
enabled = false
tls = false # enable TLS for production!!
listen = "localhost:5555"
cert = "cert.pem"
key = "key.pem"