mirror of
https://github.com/glauth/glauth.git
synced 2026-10-27 06:26:59 +00:00
Compare commits
38 Commits
v2.3.2
...
tests-fixups
| Author | SHA1 | Date | |
|---|---|---|---|
| 5147d1165a | |||
| 374041c358 | |||
| 5bdeb5118b | |||
| 319cc9a3c7 | |||
| 26cd4447c8 | |||
| 96a283fea2 | |||
| c07a1c978c | |||
| c1c57c62e9 | |||
| 700745e640 | |||
| 1c497d76ba | |||
| cf0eb7f43f | |||
| fb65713fbd | |||
| cb121cdc2c | |||
| 4b2b1398b8 | |||
| be396ae635 | |||
| 719f6a2a2d | |||
| ff7a9e6215 | |||
| 45185dafd1 | |||
| aedc0f2867 | |||
| 0774c95c1a | |||
| 9e04c8b756 | |||
| d3df7252ec | |||
| 5123a3da8e | |||
| eabf67e266 | |||
| d68c78a18b | |||
| 645dbb2266 | |||
| eea7bfbb4f | |||
| b498736cfd | |||
| f8dee8da87 | |||
| c4a020a4f5 | |||
| e92008680c | |||
| 8f972db4cf | |||
| e9a4a5623a | |||
| 8e042199a2 | |||
| c0ed90822f | |||
| a5ecc26e06 | |||
| a90f37b23b | |||
| 8476506b8c |
@@ -7,10 +7,10 @@ on:
|
||||
- master
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.21.x]
|
||||
go-version: [1.25.x]
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
name: release-please
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
@@ -8,20 +9,12 @@ permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
name: release-please
|
||||
|
||||
jobs:
|
||||
release-please:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: google-github-actions/release-please-action@v3
|
||||
- uses: google-github-actions/release-please-action@v4
|
||||
with:
|
||||
release-type: go
|
||||
package-name: GLAuth
|
||||
path: v2
|
||||
default-branch: master
|
||||
pull-request-title-pattern: "ci: release ${version}"
|
||||
token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
|
||||
extra-files: |
|
||||
v2/internal/version/const.go
|
||||
config-file: release-please-config.json
|
||||
manifest-file: .release-please-manifest.json
|
||||
|
||||
@@ -13,6 +13,9 @@ v2/buildlogs
|
||||
/vendor/
|
||||
/Godeps/
|
||||
|
||||
# Various
|
||||
go.work.sum
|
||||
|
||||
# gomocks auto-generated
|
||||
**/mock_*.go
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"v2": "2.5.2"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Ned McClain & Ben Yanke
|
||||
Copyright (c) 2018 Ned McClain & Ben Yanke & Chris Ravenscroft
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
# GLAuth: LDAP authentication server for developers
|
||||
Go-lang LDAP Authentication (GLAuth) is a secure, easy-to-use, LDAP server w/ configurable backends.
|
||||
|
||||
[](https://gitter.im/glauth/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
[](hey)
|
||||
# GLAuth: LDAP authentication server for developers
|
||||
[](https://glauth.github.io/)
|
||||
[](https://github.com/fusion/kittendns)
|
||||
[](https://github.com/fusion/wing-vpn)
|
||||
|
||||
Go-lang LDAP Authentication (GLAuth) is a secure, easy-to-use, LDAP server w/ configurable backends.
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
* Centrally manage accounts across your infrastructure
|
||||
* Centrally manage SSH keys, Linux accounts, and passwords for cloud servers.
|
||||
@@ -21,7 +21,7 @@ Use it to centralize account management across your Linux servers, your OSX mach
|
||||
|
||||
### Contributing
|
||||
- Please base all Pull Requests on [dev](https://github.com/glauth/glauth/tree/dev), not master.
|
||||
- Format your code autonmatically using `gofmt -d ./` before committing
|
||||
- Format your code automatically using `gofmt -d ./` before committing
|
||||
|
||||
### Quickstart
|
||||
This quickstart is a great way to try out GLAuth in a non-production environment. *Be warned that you should take the extra steps to setup SSL (TLS) for production use!*
|
||||
@@ -110,4 +110,3 @@ For advanced users, GLAuth supports pluggable backends. Currently, it can use a
|
||||
# Stargazers over time
|
||||
|
||||
[](https://starchart.cc/glauth/glauth)
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"packages": {
|
||||
"v2": {
|
||||
"release-type": "go",
|
||||
"package-name": "GLAuth",
|
||||
"pull-request-title-pattern": "ci: release ${version}",
|
||||
"extra-files": [
|
||||
"internal/version/const.go"
|
||||
]
|
||||
}
|
||||
},
|
||||
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
|
||||
}
|
||||
@@ -1,5 +1,73 @@
|
||||
# Changelog
|
||||
|
||||
## [2.5.2](https://github.com/glauth/glauth/compare/GLAuth-v2.5.1...GLAuth-v2.5.2) (2026-07-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* check plugin compatibility at release time ([700745e](https://github.com/glauth/glauth/commit/700745e640c5371361a58ef3c90245a4637c5224))
|
||||
|
||||
## [2.5.1](https://github.com/glauth/glauth/compare/GLAuth-v2.5.0...GLAuth-v2.5.1) (2026-07-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* reject disabled users on all backends in Bind ([#470](https://github.com/glauth/glauth/issues/470)) ([fb65713](https://github.com/glauth/glauth/commit/fb65713fbdfd626cb033675fb42724a5fb3d06ff))
|
||||
* team atlanta arbitrary password ([#471](https://github.com/glauth/glauth/issues/471)) ([cb121cd](https://github.com/glauth/glauth/commit/cb121cdc2c9b831abdf7831835ec30ead2536a7a))
|
||||
|
||||
## [2.5.0](https://github.com/glauth/glauth/compare/GLAuth-v2.5.0...GLAuth-v2.5.0) (2026-04-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* allow tracing configuration via main config ([f692394](https://github.com/glauth/glauth/commit/f692394942aa0f93a1aa1572331fd1c3e3553156))
|
||||
* alt container build registry ([f8dee8d](https://github.com/glauth/glauth/commit/f8dee8da879232f5c6ae2a2a19565562c7d0d9fd))
|
||||
* embedded plugins ([c0ed908](https://github.com/glauth/glauth/commit/c0ed90822fd4d2ee7104a1979d423a0c93d758ac))
|
||||
* introduce context for otlp spans into handler pkg ([d05630f](https://github.com/glauth/glauth/commit/d05630f66b80a776fd406782ad1fde5c6c66eac6))
|
||||
* introduce context for otlp spans into plugins pkg ([46e49b6](https://github.com/glauth/glauth/commit/46e49b6976318a9f3670b88bdcb3411dfac0a17c))
|
||||
* introduce legacy behavior, based on past versions ([#318](https://github.com/glauth/glauth/issues/318)) ([34a6b30](https://github.com/glauth/glauth/commit/34a6b30b8fe69c90cab09eb85890ffdfe8fc80fd))
|
||||
* introduce otelsql ([2ca5312](https://github.com/glauth/glauth/commit/2ca53126965aa7d42b23aee15f750df12822d454))
|
||||
* introduce otlp tracer ([0cf0403](https://github.com/glauth/glauth/commit/0cf04037a2a7b38c8ed7af2451b115f48c5427b5))
|
||||
* proxying ldap paging compat ([#422](https://github.com/glauth/glauth/issues/422)) ([8476506](https://github.com/glauth/glauth/commit/8476506b8c51e993eb90c73d612f69fec5350b89))
|
||||
* starttls and ldaps now matching features ([#458](https://github.com/glauth/glauth/issues/458)) ([d68c78a](https://github.com/glauth/glauth/commit/d68c78a18b150679038e5ce549b6b227c31518d7))
|
||||
* wire up basic tracer ([1c2b23c](https://github.com/glauth/glauth/commit/1c2b23c00ff85b83a6d2e4bf4a9a68081aaf2777))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 246 and [#252](https://github.com/glauth/glauth/issues/252) ([ceefc66](https://github.com/glauth/glauth/commit/ceefc66dbba9ae054958269825de31d22d30e262))
|
||||
* config watcher now updates whenf ile added to dir ([#424](https://github.com/glauth/glauth/issues/424)) ([a90f37b](https://github.com/glauth/glauth/commit/a90f37b23b76af22c700392d68bf227b7aee1894))
|
||||
* Credentials leaked on INFO log level ([#451](https://github.com/glauth/glauth/issues/451)) ([645dbb2](https://github.com/glauth/glauth/commit/645dbb226683ae57f3d5382e21ea42444e940752))
|
||||
* drop vendored toml ([24455e3](https://github.com/glauth/glauth/commit/24455e39889716ce7ae1e7e8a7dacfa7d4c96080))
|
||||
* formatting ([c90cbfe](https://github.com/glauth/glauth/commit/c90cbfe5fb090fc19a55d64e01cd0e31c38bf2f0))
|
||||
* go test not checking otp within allowed basedn ([#403](https://github.com/glauth/glauth/issues/403)) ([ed52a91](https://github.com/glauth/glauth/commit/ed52a91ec4117ff58fe606a1d8ba10786501a1e5))
|
||||
* move all toml parsing into a new internal package, drop the mappings in favour of toml.Primitive decoding ([3ba8e11](https://github.com/glauth/glauth/commit/3ba8e1113217be647d240261322453d213ea7da4))
|
||||
* patch group name fetches for the rest of handlers ([#450](https://github.com/glauth/glauth/issues/450)) ([b498736](https://github.com/glauth/glauth/commit/b498736cfdd0ee6f1585d682a5c54c9d927b8dbb))
|
||||
* remove spinlock in monitoring code ([#405](https://github.com/glauth/glauth/issues/405)) ([a2c151b](https://github.com/glauth/glauth/commit/a2c151b0d025332462369a846a51ef4deff5332b))
|
||||
* removed config setup from main, reshoring log configuration ([5fe8aca](https://github.com/glauth/glauth/commit/5fe8aca852bdfe7e375ea99b87074bae35fc3407))
|
||||
* update ldap library to fix [#389](https://github.com/glauth/glauth/issues/389) ([#430](https://github.com/glauth/glauth/issues/430)) ([a5ecc26](https://github.com/glauth/glauth/commit/a5ecc26e064cea9e4dab3dd234ebd5d5eb06be0e))
|
||||
* update tracing code to work with breaking otlp 1.20 changes ([1a37396](https://github.com/glauth/glauth/commit/1a3739610997b58100040d0c8a405596fccc8e23))
|
||||
* upgrade to use BurntSushi/toml ([f9addbc](https://github.com/glauth/glauth/commit/f9addbc2c5b13ccc2779dbabc4c55bc8706f53d6))
|
||||
|
||||
|
||||
### Miscellaneous Chores
|
||||
|
||||
* force release 2.5.0 ([ff7a9e6](https://github.com/glauth/glauth/commit/ff7a9e6215857e0dd3450504b7b063409ccfc374))
|
||||
* release 2.3.1 ([0bf3d4a](https://github.com/glauth/glauth/commit/0bf3d4a82a8451e7bbda74e3730345aab5a855a7))
|
||||
|
||||
## [2.4.0](https://github.com/glauth/glauth/compare/v2.3.2...v2.4.0) (2024-08-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* proxying ldap paging compat ([#422](https://github.com/glauth/glauth/issues/422)) ([8476506](https://github.com/glauth/glauth/commit/8476506b8c51e993eb90c73d612f69fec5350b89))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* config watcher now updates whenf ile added to dir ([#424](https://github.com/glauth/glauth/issues/424)) ([a90f37b](https://github.com/glauth/glauth/commit/a90f37b23b76af22c700392d68bf227b7aee1894))
|
||||
* update ldap library to fix [#389](https://github.com/glauth/glauth/issues/389) ([#430](https://github.com/glauth/glauth/issues/430)) ([a5ecc26](https://github.com/glauth/glauth/commit/a5ecc26e064cea9e4dab3dd234ebd5d5eb06be0e))
|
||||
|
||||
## [2.3.2](https://github.com/glauth/glauth/compare/v2.3.1...v2.3.2) (2024-02-19)
|
||||
|
||||
|
||||
|
||||
+176
-47
@@ -2,7 +2,7 @@
|
||||
|
||||
MAKEFLAGS += --no-print-directory
|
||||
|
||||
VERSION=$(shell bin/linuxamd64/glauth --version)
|
||||
VERSION=$(shell if [ -x bin/linuxamd64/glauth ]; then bin/linuxamd64/glauth --version; else bin/glauth-linux-amd64 --version; fi)
|
||||
|
||||
GIT_COMMIT=$(shell git rev-list -1 HEAD )
|
||||
BUILD_TIME=$(shell date -u +%Y%m%d_%H%M%SZ)
|
||||
@@ -21,10 +21,12 @@ GIT_BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
|
||||
BUILD_VARS?=-s -w
|
||||
BUILD_FILES=.
|
||||
TRIM_FLAGS>?=-trimpath
|
||||
BUILD_TAGS?=noembed
|
||||
|
||||
# Targets
|
||||
MAIN_TARGETS?=linux/amd64,linux/386,linux/arm64,linux/arm-7,darwin/amd64,darwin/arm64,windows/amd64,windows/386
|
||||
PLUGIN_TARGETS?=linux/amd64,linux/386,linux/arm64,linux/arm-7,darwin/amd64,darwin/arm64
|
||||
MAIN_TARGETS?=linux/amd64,linux/386,linux/arm64,linux/arm-7,linux/riscv64,darwin/amd64,darwin/arm64,windows/amd64,windows/386
|
||||
PLUGIN_TARGETS?=linux/amd64,linux/386,linux/arm64,linux/arm-7,linux/riscv64,darwin/amd64,darwin/arm64
|
||||
EMBED_TARGETS?=windows/amd64
|
||||
|
||||
# For release process
|
||||
GO_RELEASE_V=$(shell go version | { read _ _ v _; echo $${v#go}; })
|
||||
@@ -56,10 +58,9 @@ mocks:
|
||||
vet:
|
||||
-go vet ./...
|
||||
.PHONY: vet
|
||||
#test: @ runs the integration test on linuxamd64 (eventually allow the binary to be set)
|
||||
#test: @ runs package, root integration, and shell integration tests
|
||||
test: mocks vet
|
||||
# also run unit tests for packages, skip glauth_test.go for now
|
||||
go test -v -cover -coverprofile coverage.out ./pkg/... ./internal/...
|
||||
go test -v -cover -coverprofile coverage.out ./pkg/... ./internal/... .
|
||||
$(MAKE) runtest
|
||||
#all: @ run build process for all binaries
|
||||
all: setup binaries verify
|
||||
@@ -67,8 +68,11 @@ all: setup binaries verify
|
||||
#fast: @ run build process for only linuxamd64
|
||||
fast: setup linuxamd64
|
||||
|
||||
#fastdebug: @ run build process for only linuxamd64 in debug mode
|
||||
fastdebug: setup linuxamd64debug
|
||||
|
||||
#binaries: @ list of binary formats to build
|
||||
binaries: linux386 linuxamd64 linuxarm linuxarm64 darwinamd64 darwinarm64 win386 winamd64
|
||||
binaries: linux386 linuxamd64 linuxarm linuxarm64 linuxriscv64 darwinamd64 darwinarm64 win386 winamd64
|
||||
|
||||
#setup: @ setup commands to always run
|
||||
setup: getdeps format
|
||||
@@ -76,11 +80,26 @@ setup: getdeps format
|
||||
#####################
|
||||
# Subcommands
|
||||
#####################
|
||||
# @echo \- removing any changes and pulling latest master && \
|
||||
# git reset --hard HEAD >/dev/null 2>&1 && \
|
||||
# git checkout master >/dev/null 2>&1 && \
|
||||
# git pull >/dev/null 2>&1 && \
|
||||
|
||||
prepbuild:
|
||||
@if [ ! -e $M/.ignore ] ; \
|
||||
then \
|
||||
mkdir -p buildlogs;\
|
||||
echo \- taking care of ignore files && \
|
||||
sudo rm -rf bin/* >/dev/null && \
|
||||
echo \- cleaning up plugins remnants && \
|
||||
make forget-plugins && \
|
||||
make remember-plugins
|
||||
|
||||
forget-embed:
|
||||
@if [ -f pkg/server/embed_sqlite.go ]; then \
|
||||
mv pkg/server/embed_sqlite.go /tmp/; \
|
||||
fi
|
||||
|
||||
remember-embed:
|
||||
@if [ ! -f pkg/server/embed_sqlite.go ]; then \
|
||||
mv /tmp/embed_sqlite.go pkg/server/; \
|
||||
fi
|
||||
|
||||
# Run integration test
|
||||
@@ -100,20 +119,61 @@ format:
|
||||
devrun:
|
||||
go run ${BUILD_FILES} -c sample-simple.cfg
|
||||
|
||||
#testplugin: @ start GLAuth with a dynamically loaded plugin and stop it cleanly
|
||||
GLAUTH_BIN?=bin/glauth-linux-amd64
|
||||
PLUGIN_BIN?=bin/sqlite-linux-amd64.so
|
||||
PLUGIN_CONFIG?=pkg/plugins/glauth-sqlite/sample-database.cfg
|
||||
PLUGIN_LDAP_PORT?=13893
|
||||
PLUGIN_API_PORT?=15555
|
||||
|
||||
testplugin:
|
||||
@set -e; \
|
||||
test -x "$(GLAUTH_BIN)"; \
|
||||
test -r "$(PLUGIN_BIN)"; \
|
||||
test -f "$(PLUGIN_CONFIG)"; \
|
||||
tmpdir=$$(mktemp -d); \
|
||||
trap 'rm -rf "$$tmpdir"' EXIT; \
|
||||
sed \
|
||||
-e 's#^[[:space:]]*plugin = .*# plugin = "$(abspath $(PLUGIN_BIN))"#' \
|
||||
-e "s#^[[:space:]]*database = .*# database = \"$$tmpdir/gl.db\"#" \
|
||||
-e 's#listen = "0.0.0.0:3893"#listen = "127.0.0.1:$(PLUGIN_LDAP_PORT)"#' \
|
||||
-e 's#listen = "0.0.0.0:5555"#listen = "127.0.0.1:$(PLUGIN_API_PORT)"#' \
|
||||
"$(PLUGIN_CONFIG)" > "$$tmpdir/config.cfg"; \
|
||||
"$(GLAUTH_BIN)" -c "$$tmpdir/config.cfg" > "$$tmpdir/glauth.log" 2>&1 & \
|
||||
pid=$$!; \
|
||||
sleep 2; \
|
||||
if ! kill -0 "$$pid" 2>/dev/null; then \
|
||||
set +e; wait "$$pid"; status=$$?; set -e; \
|
||||
cat "$$tmpdir/glauth.log"; \
|
||||
exit $$status; \
|
||||
fi; \
|
||||
kill -TERM "$$pid"; \
|
||||
set +e; wait "$$pid"; status=$$?; set -e; \
|
||||
cat "$$tmpdir/glauth.log"; \
|
||||
exit $$status
|
||||
|
||||
mkbindir:
|
||||
@echo "create directory bin/$(GOOS)$(GOARCH)"
|
||||
@mkdir -p bin/$(GOOS)$(GOARCH)
|
||||
.PHONY: mkbindir
|
||||
|
||||
build: mkbindir
|
||||
@go build ${TRIM_FLAGS} -ldflags "${BUILD_VARS}" -o bin/$(GOOS)$(GOARCH)/glauth -buildvcs .
|
||||
@go build ${TRIM_FLAGS} -tags ${BUILD_TAGS} -ldflags "${BUILD_VARS}" -o bin/$(GOOS)$(GOARCH)/glauth -buildvcs .
|
||||
$(MAKE) sha256
|
||||
.PHONY: build
|
||||
|
||||
builddebug: mkbindir
|
||||
@go build ${TRIM_FLAGS} -tags ${BUILD_TAGS} -gcflags="all=-N -l" -o bin/$(GOOS)$(GOARCH)/glauth -buildvcs .
|
||||
$(MAKE) sha256
|
||||
.PHONY: builddebug
|
||||
|
||||
sha256:
|
||||
@sha256sum bin/$(GOOS)$(GOARCH)/glauth > bin/$(GOOS)$(GOARCH)/glauth.sha256
|
||||
.PHONY: sha256
|
||||
|
||||
linuxamd64debug:
|
||||
GOOS=linux GOARCH=amd64 $(MAKE) builddebug
|
||||
|
||||
linux386:
|
||||
GOOS=linux GOARCH=386 $(MAKE) build
|
||||
|
||||
@@ -126,6 +186,16 @@ linuxarm:
|
||||
linuxarm64:
|
||||
GOOS=linux GOARCH=arm64 $(MAKE) build
|
||||
|
||||
linuxriscv64:
|
||||
GOOS=linux GOARCH=riscv64 $(MAKE) build
|
||||
|
||||
testing:
|
||||
mkdir -p pkg/embed && \
|
||||
cat pkg/plugins/glauth-sqlite/sqlite.go | sed -e 's/func main() {}//' -e 's/^package main/package embed/' > pkg/embed/sqlite.go && \
|
||||
go mod tidy && \
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=1 BUILD_TAGS=embedsqlite $(MAKE) build && \
|
||||
rm -rf pkg/embed
|
||||
|
||||
darwinamd64:
|
||||
GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 $(MAKE) build
|
||||
|
||||
@@ -139,73 +209,128 @@ winamd64:
|
||||
GOOS=windows GOARCH=amd64 $(MAKE) build
|
||||
|
||||
verify:
|
||||
@for binary in linux386 linuxamd64 linuxarm linuxarm64 darwinamd64 darwinarm64 win386 winamd64; do cd bin/$$binary && sha256sum glauth.sha256 -c && cd ../..; done
|
||||
@for binary in linux386 linuxamd64 linuxarm linuxarm64 linuxriscv64 darwinamd64 darwinarm64 win386 winamd64; do cd bin/$$binary && sha256sum glauth.sha256 -c && cd ../..; done
|
||||
|
||||
pull-plugin-dependencies:
|
||||
(for pkg in $$(cat $$M/go.mod | awk '/^require [a-z]/{print $$2} /^\)/{rblock=0} rblock{if($$4!="indirect" && $$1!~"glauth"){print $$1}} /^require \(/{rblock=1}'); do if [ "$$(grep $$pkg go.mod &>/dev/null)" = "" ]; then go get $$pkg; fi; done)
|
||||
|
||||
#prepare-plugins-release: @ pull plugins dependencies for local building
|
||||
prepare-plugins-release:
|
||||
#prepare-plugins-buid: @ pull plugins dependencies for local building
|
||||
prepare-plugins-build:
|
||||
@M=pkg/plugins/glauth-sqlite make pull-plugin-dependencies && \
|
||||
M=pkg/plugins/glauth-mysql make pull-plugin-dependencies && \
|
||||
M=pkg/plugins/glauth-postgres make pull-plugin-dependencies && \
|
||||
M=pkg/plugins/glauth-pam make pull-plugin-dependencies
|
||||
|
||||
forget-plugin:
|
||||
rm -rf $M ../.git/modules/v2/$M && \
|
||||
git config --remove-section submodule.v2/$M && \
|
||||
git rm --cache $M && \
|
||||
go mod tidy
|
||||
rm -rf $M ../.git/modules/v2/$M; \
|
||||
git config --remove-section submodule.v2/$M; \
|
||||
git rm --cache $M; \
|
||||
go mod tidy; \
|
||||
true
|
||||
|
||||
#forget-plugin: @ remove plugins, restoring original file structure
|
||||
forget-plugins:
|
||||
for pkg in pkg/plugins/*/go.mod; do\
|
||||
P=$$(echo $$pkg | cut -d'/' -f 3) M=$$(dirname $$pkg) make forget-plugin;\
|
||||
done
|
||||
for pkg in pkg/plugins/*; do \
|
||||
if [ -d "$$pkg" ]; then \
|
||||
P=$$(echo $$pkg | cut -d'/' -f 3) M=$$pkg make forget-plugin >/dev/null 2>&1;\
|
||||
fi; \
|
||||
done; \
|
||||
rm -rf buildlogs
|
||||
|
||||
#releasemain: @ build main binaries for distribution
|
||||
releasemain: prepbuild
|
||||
remember-plugins:
|
||||
cd ../ && \
|
||||
for module in $$(cat .gitmodules | awk '/path =/{P=$$3} /url =/{print $$3 "," P}'); do \
|
||||
git submodule add $${module%%,*} $${module#*,}; \
|
||||
done; \
|
||||
git submodule update --init --recursive
|
||||
|
||||
_buildmain:
|
||||
@xgo -v -ldflags="${BUILD_VARS}" -trimpath -go ${GO_RELEASE_V} -out glauth -dest bin -buildvcs=false --targets="${MAIN_TARGETS}" .
|
||||
|
||||
releaseplugin: prepbuild
|
||||
@echo "Building $P plugin"; \
|
||||
if [ ! -e $M/.ignore ] ; \
|
||||
then \
|
||||
xgo -v -ldflags="${BUILD_VARS}" -trimpath -go ${GO_RELEASE_V} -out $P -dest bin -buildvcs=false -buildmode=plugin --targets="${PLUGIN_TARGETS}" --pkg $M/$P.go . && \
|
||||
(cd bin && for lib in $$(ls $$P-*); do sudo mv $$lib $$lib.so; done); \
|
||||
#buildmain: @ build main binaries for distribution
|
||||
buildmain: prepbuild _buildmain
|
||||
|
||||
buildsingleembed:
|
||||
git checkout -b build_embeds && \
|
||||
mkdir -p pkg/embed && \
|
||||
cat pkg/plugins/$P/$M | sed -e 's/func main() {}//' -e 's/^package main/package embed/' > pkg/embed/$M && \
|
||||
go mod tidy && \
|
||||
git add pkg/embed/$M go.mod go.sum && \
|
||||
git commit -m "build cruft" && \
|
||||
git push -u origin build_embeds && \
|
||||
xgo -v -ldflags="${BUILD_VARS}" --tags=$T -trimpath -go ${GO_RELEASE_V} -out glauth -dest bin -buildvcs=false --targets="${EMBED_TARGETS}" . && \
|
||||
rm -rf pkg/embed && \
|
||||
git checkout master && \
|
||||
git push -d origin build_embeds && \
|
||||
git branch -D build_embeds
|
||||
|
||||
_buildmainembeds:
|
||||
P=glauth-sqlite M=sqlite.go T=embedsqlite make buildsingleembed
|
||||
|
||||
#buildmainembeds: @ build main binaries for distribution, including embedded plugins
|
||||
buildmainembeds: prepbuild _buildmainembeds
|
||||
|
||||
buildplugin:
|
||||
ifeq ($P,pam)
|
||||
xgo -v -ldflags="${BUILD_VARS}" -trimpath -go ${GO_RELEASE_V} -out $P -dest bin -buildvcs=false -buildmode=plugin --targets="${PLUGIN_TARGETS}" --pkg $M/$P.go --bringupcmd 'apt update && apt install -y libpam0g-dev' .
|
||||
else
|
||||
xgo -v -ldflags="${BUILD_VARS}" -trimpath -go ${GO_RELEASE_V} -out $P -dest bin -buildvcs=false -buildmode=plugin --targets="${PLUGIN_TARGETS}" --pkg $M/$P.go .
|
||||
endif
|
||||
(cd bin && for lib in $$(ls $$P-* | grep -v \.so$$); do sudo mv $$lib $$lib.so; done);
|
||||
|
||||
_buildplugins: prepare-plugins-build
|
||||
for pkg in pkg/plugins/*; do \
|
||||
if [ -d "$$pkg" ]; then \
|
||||
M=pkg/plugins/$$(echo $$pkg | cut -d'/' -f 3) && P=$$(echo $$M | grep -oP 'glauth-\K\w+') && M=$$M P=$$P make buildplugin;\
|
||||
fi; \
|
||||
done; \
|
||||
|
||||
#buildplugins: @ build base plugins for distribution
|
||||
buildplugins: forget-embed _buildplugins remember-embed
|
||||
|
||||
_checkdockerauth:
|
||||
@if ! grep -q "index.docker.io" $(HOME)/.docker/config.json; then \
|
||||
echo "❌ Not logged in to Docker Hub (index.docker.io not found in config)."; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
if ! grep -q "ghcr.io" $(HOME)/.docker/config.json; then \
|
||||
echo "❌ Not logged in to GitHub Hub (ghcr.io not found in config)."; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
#releaseplugins: @ build base plugins for distribution
|
||||
releaseplugins:
|
||||
@for pkg in pkg/plugins/*/go.mod; do\
|
||||
M=pkg/plugins/$$(echo $$pkg | cut -d'/' -f 3) && P=$$(echo $$M | grep -oP 'glauth-\K\w+') && M=$$M P=$$P make releaseplugin;\
|
||||
done
|
||||
|
||||
releasedockermain:
|
||||
$(if $(TAG),,$(error Must set TAG))
|
||||
$(if $(REPO),,$(error Must set REPO - glauth or other))
|
||||
@mkdir -p docker/assets/linux/amd64 docker/assets/linux/arm64 docker/assets/linux/arm/v7 && \
|
||||
builddockermain:
|
||||
$(if $(TAG),,$(error ❌ Must set TAG))
|
||||
$(if $(REPO),,$(error ❌ Must set REPO - glauth or other))
|
||||
@mkdir -p docker/assets/linux/amd64 docker/assets/linux/arm64 docker/assets/linux/arm/v7 docker/assets/linux/riscv64 && \
|
||||
cp -f bin/glauth-linux-amd64 docker/assets/linux/amd64/glauth && \
|
||||
cp -f bin/glauth-linux-arm64 docker/assets/linux/arm64/glauth && \
|
||||
cp -f bin/glauth-linux-arm-7 docker/assets/linux/arm/v7/glauth && \
|
||||
docker buildx build --tag $$REPO/glauth:$$TAG -t $$REPO/glauth:latest -f docker/Dockerfile-standalone --platform linux/amd64,linux/arm64,linux/arm/v7 --push docker
|
||||
cp -f bin/glauth-linux-riscv64 docker/assets/linux/riscv64/glauth && \
|
||||
docker buildx build --tag ghcr.io/$$REPO/glauth:$$TAG -t ghcr.io/$$REPO/glauth:latest -f docker/Dockerfile-standalone --platform linux/amd64,linux/arm64,linux/arm/v7,linux/riscv64 --push docker && \
|
||||
docker buildx build --tag $$REPO/glauth:$$TAG -t $$REPO/glauth:latest -f docker/Dockerfile-standalone --platform linux/amd64,linux/arm64,linux/arm/v7,linux/riscv64 --push docker
|
||||
|
||||
releasedockerplugins:
|
||||
$(if $(TAG),,$(error Must set TAG))
|
||||
$(if $(REPO),,$(error Must set REPO - glauth or other))
|
||||
@mkdir -p docker/assets/linux/amd64 docker/assets/linux/arm64 docker/assets/linux/arm/v7 && \
|
||||
builddockerplugins:
|
||||
$(if $(TAG),,$(error ❌ Must set TAG))
|
||||
$(if $(REPO),,$(error ❌ Must set REPO - glauth or other))
|
||||
@mkdir -p docker/assets/linux/amd64 docker/assets/linux/arm64 docker/assets/linux/arm/v7 docker/assets/linux/riscv64 && \
|
||||
cp -f bin/sqlite-linux-amd64.so docker/assets/linux/amd64/sqlite.so && \
|
||||
cp -f bin/sqlite-linux-arm64.so docker/assets/linux/arm64/sqlite.so && \
|
||||
cp -f bin/sqlite-linux-arm-7.so docker/assets/linux/arm/v7/sqlite.so && \
|
||||
cp -f bin/sqlite-linux-riscv64.so docker/assets/linux/riscv64/sqlite.so && \
|
||||
cp -f bin/mysql-linux-amd64.so docker/assets/linux/amd64/mysql.so && \
|
||||
cp -f bin/mysql-linux-arm64.so docker/assets/linux/arm64/mysql.so && \
|
||||
cp -f bin/mysql-linux-arm-7.so docker/assets/linux/arm/v7/mysql.so && \
|
||||
cp -f bin/mysql-linux-riscv64.so docker/assets/linux/riscv64/mysql.so && \
|
||||
cp -f bin/postgres-linux-amd64.so docker/assets/linux/amd64/postgres.so && \
|
||||
cp -f bin/postgres-linux-arm64.so docker/assets/linux/arm64/postgres.so && \
|
||||
cp -f bin/postgres-linux-arm-7.so docker/assets/linux/arm/v7/postgres.so && \
|
||||
docker buildx build --tag $$REPO/glauth-plugins:$$TAG -t $$REPO/glauth-plugins:latest -f docker/Dockerfile-plugins --platform linux/amd64,linux/arm64,linux/arm/v7 --push docker
|
||||
cp -f bin/postgres-linux-riscv64.so docker/assets/linux/riscv64/postgres.so && \
|
||||
docker buildx build --tag ghcr.io/$$REPO/glauth-plugins:$$TAG -t ghcr.io/$$REPO/glauth-plugins:latest -f docker/Dockerfile-plugins --platform linux/amd64,linux/arm64,linux/arm/v7,linux/riscv64 --push docker && \
|
||||
docker buildx build --tag $$REPO/glauth-plugins:$$TAG -t $$REPO/glauth-plugins:latest -f docker/Dockerfile-plugins --platform linux/amd64,linux/arm64,linux/arm/v7,linux/riscv64 --push docker
|
||||
|
||||
releasedocker: releasedockermain releasedockerplugins
|
||||
builddocker: _checkdockerauth builddockermain builddockerplugins
|
||||
|
||||
buildeverything: prepbuild forget-embed _buildplugins _buildmain testplugin remember-embed _buildmainembeds builddocker
|
||||
|
||||
#testdocker: @ run integration test using docker
|
||||
testdocker:
|
||||
@@ -247,4 +372,8 @@ testdocker:
|
||||
docker rm glauth-test && \
|
||||
echo "==> Testing complete."
|
||||
|
||||
.PHONY: all prepbuild run test fast binaries setup getdeps runtest updatetest format devrun linux386 linuxamd64 linuxarm linuxarm64 darwinamd64 darwinarm64 win386 winamd64 verify pull-plugin pull-base-plugins forget-plugin forget-plugins releasemain releaseplugin release releaseplugins releasedockermain releaserdockerplugins releasedocker testdocker
|
||||
.PHONY: all \
|
||||
prepbuild run test fast binaries setup getdeps runtest updatetest format devrun \
|
||||
linux386 linuxamd64 linuxarm linuxarm64 linuxriscv64 darwinamd64 darwinarm64 win386 winamd64 verify \
|
||||
pull-plugin pull-base-plugins forget-plugin forget-plugins buildmain buildplugin build \
|
||||
buildplugins builddockermain builddockerplugins builddocker testdocker testplugin buildeverything
|
||||
|
||||
@@ -10,8 +10,17 @@ RUN apk add --no-cache git make busybox-static dumb-init
|
||||
# Run Step
|
||||
#################
|
||||
|
||||
FROM gcr.io/distroless/base-debian11 as run
|
||||
LABEL maintainers="Ben Yanke <ben@benyanke.com>, Jörn Friedrich Dreyer <jfd@butonic.de>, Chris F Ravenscroft <chris@voilaweb.com>"
|
||||
FROM gcr.io/distroless/base-debian13 as run
|
||||
|
||||
LABEL maintainers="Ben Yanke <ben@benyanke.com>, Jörn Friedrich Dreyer <jfd@butonic.de>, Chris F Ravenscroft <chris@voilaweb.com>" \
|
||||
org.opencontainers.image.title="GLauth" \
|
||||
org.opencontainers.image.description="A simple LDAP server for development, home use, or CI pipelines" \
|
||||
org.opencontainers.image.authors="Ben Yanke, Jörn Friedrich Dreyer, Chris F Ravenscroft" \
|
||||
org.opencontainers.image.source="https://github.com/glauth/glauth" \
|
||||
org.opencontainers.image.licenses="MIT" \
|
||||
org.opencontainers.image.version="latest" \
|
||||
org.opencontainers.image.url="https://github.com/glauth/glauth"
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# Copy docker specific scripts from build container
|
||||
|
||||
@@ -10,8 +10,17 @@ RUN apk add --no-cache git make busybox-static dumb-init
|
||||
# Run Step
|
||||
#################
|
||||
|
||||
FROM gcr.io/distroless/base-debian11 as run
|
||||
LABEL maintainers="Ben Yanke <ben@benyanke.com>, Jörn Friedrich Dreyer <jfd@butonic.de>, Chris F Ravenscroft <chris@voilaweb.com>"
|
||||
FROM gcr.io/distroless/base-debian13 as run
|
||||
|
||||
LABEL maintainers="Ben Yanke <ben@benyanke.com>, Jörn Friedrich Dreyer <jfd@butonic.de>, Chris F Ravenscroft <chris@voilaweb.com>" \
|
||||
org.opencontainers.image.title="GLauth" \
|
||||
org.opencontainers.image.description="A simple LDAP server for development, home use, or CI pipelines" \
|
||||
org.opencontainers.image.authors="Ben Yanke, Jörn Friedrich Dreyer, Chris F Ravenscroft" \
|
||||
org.opencontainers.image.source="https://github.com/glauth/glauth" \
|
||||
org.opencontainers.image.licenses="MIT" \
|
||||
org.opencontainers.image.version="latest" \
|
||||
org.opencontainers.image.url="https://github.com/glauth/glauth"
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# Copy docker specific scripts from build container
|
||||
|
||||
+18
-4
@@ -143,20 +143,32 @@ func startService() {
|
||||
startConfigWatcher()
|
||||
|
||||
var err error
|
||||
var tlsConfig *tls.Config
|
||||
var starttlsConfig *tls.Config
|
||||
if c := activeConfig.LDAP; c.Enabled && c.TLS {
|
||||
// TODO check if tls params are string or bytes and change config accordingly
|
||||
tlsConfig, err = _tls.MakeTLS([]byte(c.TLSCert), []byte(c.TLSKey))
|
||||
starttlsConfig, err = _tls.MakeTLS([]byte(c.TLSCert), []byte(c.TLSKey), c.LegacyTLS)
|
||||
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("unable to configure TLS, proceeding without....StartTLS won't be supported")
|
||||
log.Warn().Err(err).Msg("unable to configure TLS for StartTLS: StartTLS won't be supported")
|
||||
}
|
||||
}
|
||||
|
||||
var ldapstlsConfig *tls.Config
|
||||
if c := activeConfig.LDAPS; c.Enabled {
|
||||
// TODO check if tls params are string or bytes and change config accordingly
|
||||
ldapstlsConfig, err = _tls.MakeTLS([]byte(c.Cert), []byte(c.Key), c.LegacyTLS)
|
||||
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("unable to configure TLS for LDAPS")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
s, err := server.NewServer(
|
||||
server.Logger(log),
|
||||
server.Config(activeConfig),
|
||||
server.TLSConfig(tlsConfig),
|
||||
server.StartTLSConfig(starttlsConfig),
|
||||
server.LDAPSTLSConfig(ldapstlsConfig),
|
||||
server.Monitor(monitor),
|
||||
server.Tracer(tracer),
|
||||
)
|
||||
@@ -224,6 +236,8 @@ func startConfigWatcher() {
|
||||
isChanged = true
|
||||
} else if event.Op&fsnotify.Remove == fsnotify.Remove { // vim edit file with rename/remove
|
||||
isChanged, isRemoved = true, true
|
||||
} else if event.Op&fsnotify.Create == fsnotify.Create { // only when watching a directory
|
||||
isChanged = true
|
||||
}
|
||||
case err := <-watcher.Errors:
|
||||
log.Error().Err(err).Msg("watcher error")
|
||||
|
||||
+296
-140
@@ -1,11 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/glauth/ldap"
|
||||
"github.com/pquerna/otp/totp"
|
||||
)
|
||||
|
||||
type testEnv struct {
|
||||
@@ -23,10 +32,31 @@ type testEnv struct {
|
||||
checkemployeetype string
|
||||
}
|
||||
|
||||
func glauthBinary() string {
|
||||
if binary := os.Getenv("GLAUTH_TEST_BINARY"); binary != "" {
|
||||
return binary
|
||||
}
|
||||
|
||||
qualified := filepath.Join("bin", runtime.GOOS+runtime.GOARCH, "glauth")
|
||||
if _, err := os.Stat(qualified); err == nil {
|
||||
return qualified
|
||||
}
|
||||
|
||||
distribution := filepath.Join("bin", "glauth-"+runtime.GOOS+"-"+runtime.GOARCH)
|
||||
if runtime.GOOS == "windows" {
|
||||
distribution += ".exe"
|
||||
}
|
||||
if _, err := os.Stat(distribution); err == nil {
|
||||
return distribution
|
||||
}
|
||||
|
||||
return "bin/glauth"
|
||||
}
|
||||
|
||||
func TestProperBuild(t *testing.T) {
|
||||
info, err := os.Stat("bin/glauth")
|
||||
info, err := os.Stat(glauthBinary())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
mode := uint32(info.Mode())
|
||||
if mode&0b001001001 == 0 {
|
||||
@@ -34,147 +64,229 @@ func TestProperBuild(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigBackendRejectsPasswordlessUserBinds(t *testing.T) {
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
port := listener.Addr().(*net.TCPAddr).Port
|
||||
listener.Close()
|
||||
|
||||
configPath := filepath.Join(t.TempDir(), "passwordless.cfg")
|
||||
config := fmt.Sprintf(`debug = false
|
||||
|
||||
[ldap]
|
||||
enabled = true
|
||||
listen = "127.0.0.1:%d"
|
||||
tls = false
|
||||
|
||||
[ldaps]
|
||||
enabled = false
|
||||
|
||||
[backend]
|
||||
datastore = "config"
|
||||
baseDN = "dc=glauth,dc=com"
|
||||
|
||||
[behaviors]
|
||||
LimitFailedBinds = false
|
||||
|
||||
[[groups]]
|
||||
name = "svc"
|
||||
gidnumber = 5501
|
||||
|
||||
[[users]]
|
||||
name = "nopass"
|
||||
uidnumber = 5001
|
||||
primarygroup = 5501
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "*"
|
||||
`, port)
|
||||
if err := os.WriteFile(configPath, []byte(config), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
svc := startSvc(t, fmt.Sprintf("127.0.0.1:%d", port), glauthBinary(), "-c", configPath)
|
||||
defer stopSvc(svc)
|
||||
|
||||
bindDN := "cn=nopass,dc=glauth,dc=com"
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
password string
|
||||
want string
|
||||
}{
|
||||
{name: "arbitrary password", password: "definitely-wrong", want: "exit status 49"},
|
||||
{name: "empty password", password: "", want: "exit status 53"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := runAndSelect(t, getFirst, "ldapsearch", "-LLL", "-H", fmt.Sprintf("ldap://127.0.0.1:%d", port), "-D", bindDN, "-w", tc.password, "-x", "-b", "dc=glauth,dc=com", "(cn=nopass)")
|
||||
if got != tc.want {
|
||||
t.Fatalf("bind result = %q, want %q", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func ldapOnlyConfig(t *testing.T, source string) string {
|
||||
t.Helper()
|
||||
data, err := os.ReadFile(source)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
contents := string(data)
|
||||
sectionMarker := "\n[ldaps]\n"
|
||||
markerStart := strings.Index(contents, sectionMarker)
|
||||
if markerStart < 0 {
|
||||
t.Fatalf("%s has no [ldaps] section", source)
|
||||
}
|
||||
start := markerStart + 1
|
||||
relEnd := strings.Index(contents[start+len("[ldaps]"):], "\n[")
|
||||
if relEnd < 0 {
|
||||
t.Fatalf("%s has no section after [ldaps]", source)
|
||||
}
|
||||
end := start + len("[ldaps]") + relEnd
|
||||
section := contents[start:end]
|
||||
sectionWithoutLDAPS := strings.Replace(section, "enabled = true", "enabled = false", 1)
|
||||
if section == sectionWithoutLDAPS {
|
||||
t.Fatalf("%s [ldaps] section does not enable LDAPS", source)
|
||||
}
|
||||
|
||||
contents = contents[:start] + sectionWithoutLDAPS + contents[end:]
|
||||
path := filepath.Join(t.TempDir(), filepath.Base(source))
|
||||
if err := os.WriteFile(path, []byte(contents), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func batteryOfTests(t *testing.T, env *testEnv) {
|
||||
// We aren't checking that "github.com/pquerna/otp/totp" creates correct values.
|
||||
// That's what their own tests are for.
|
||||
// We are checking that the totp code gets through glauth code successfully
|
||||
otpvalue, err := totp.GenerateCode("3hnvnk4ycv44glzigd6s25j4dougs3rk", time.Now())
|
||||
if err != nil {
|
||||
t.Fatal("Failed to generate totp code:", err)
|
||||
}
|
||||
|
||||
const addr = "ldap://127.0.0.1:3893"
|
||||
bind := func(dn, password string, rest ...string) []string {
|
||||
return append([]string{"-LLL", "-H", addr, "-D", dn, "-w", password, "-x"}, rest...)
|
||||
}
|
||||
svc := func(rest ...string) []string { return bind(env.svcdn, "mysecret", rest...) }
|
||||
otp := func(password string) []string {
|
||||
return bind(env.otpdn, password, "-bou=superheros,dc=glauth,dc=com", "cn=hackers")
|
||||
}
|
||||
|
||||
const (
|
||||
inSuperheros = "should find them in the 'superheros' group"
|
||||
listFromUser = "should get a list starting with the 'hackers' user"
|
||||
badCreds = "should get 'Invalid credentials(49)'"
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
Name string
|
||||
Path string
|
||||
Check func(t testing.TB)
|
||||
name string
|
||||
skip bool
|
||||
args []string
|
||||
line func(string) string
|
||||
want string
|
||||
msg string
|
||||
}{
|
||||
{
|
||||
Name: "searching for the 'hackers' user",
|
||||
Check: func(t testing.TB) {
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "cn=hackers")
|
||||
if got, want := out, env.expectedaccount; got != want {
|
||||
t.Fatalf("should find them in the 'superheros' group\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
},
|
||||
name: "searching for the 'hackers' user",
|
||||
args: svc("-bdc=glauth,dc=com", "cn=hackers"),
|
||||
want: env.expectedaccount,
|
||||
msg: inSuperheros,
|
||||
},
|
||||
{
|
||||
Name: "searching for the 'hackers' user without binding with a group",
|
||||
Check: func(t testing.TB) {
|
||||
if env.svcdnnogroup == "" {
|
||||
t.SkipNow()
|
||||
}
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdnnogroup, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "cn=hackers")
|
||||
if got, want := out, env.expectedaccount; got != want {
|
||||
t.Fatalf("should find them in the 'superheros' group\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
},
|
||||
name: "searching for the 'hackers' user without binding with a group",
|
||||
skip: env.svcdnnogroup == "",
|
||||
args: bind(env.svcdnnogroup, "mysecret", "-bdc=glauth,dc=com", "cn=hackers"),
|
||||
want: env.expectedaccount,
|
||||
msg: inSuperheros,
|
||||
},
|
||||
{
|
||||
Name: "searching for the 'hackers' user after binding using the account's UPN",
|
||||
Check: func(t testing.TB) {
|
||||
if !env.checkbindUPN {
|
||||
t.SkipNow()
|
||||
}
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", "serviceuser@example.com", "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "cn=hackers")
|
||||
if got, want := out, env.expectedaccount; got != want {
|
||||
t.Fatalf("should find them in the 'superheros' group\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
},
|
||||
name: "searching for the 'hackers' user after binding using the account's UPN",
|
||||
skip: !env.checkbindUPN,
|
||||
args: bind("serviceuser@example.com", "mysecret", "-bdc=glauth,dc=com", "cn=hackers"),
|
||||
want: env.expectedaccount,
|
||||
msg: inSuperheros,
|
||||
},
|
||||
{
|
||||
Name: "querying the root SDE",
|
||||
Check: func(t testing.TB) {
|
||||
out := doRunGetSecond(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-s", "base", "(objectclass=*)")
|
||||
if got, want := out, env.expectedinfo; got != want {
|
||||
t.Fatalf("should get some meta information\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
},
|
||||
name: "querying the root SDE",
|
||||
args: svc("-s", "base", "(objectclass=*)"),
|
||||
line: getSecond,
|
||||
want: env.expectedinfo,
|
||||
msg: "should get some meta information",
|
||||
},
|
||||
{
|
||||
Name: "querying the root SDE anonymously without authorizing in config file",
|
||||
Check: func(t testing.TB) {
|
||||
if !env.checkanonymousrootDSE {
|
||||
t.SkipNow()
|
||||
}
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-x", "-s", "base", "(objectclass=*)")
|
||||
if got, want := out, "exit status 50"; got != want {
|
||||
t.Fatalf("should get error 50\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
},
|
||||
name: "querying the root SDE anonymously without authorizing in config file",
|
||||
skip: !env.checkanonymousrootDSE,
|
||||
args: []string{"-LLL", "-H", addr, "-x", "-s", "base", "(objectclass=*)"},
|
||||
want: "exit status 50",
|
||||
msg: "should get error 50",
|
||||
},
|
||||
{
|
||||
Name: "enumerating posix groups",
|
||||
Check: func(t testing.TB) {
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "(objectclass=posixgroup)")
|
||||
if got, want := out, env.expectedgroup; got != want {
|
||||
t.Fatalf("should get a list starting with the 'superheros' group\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
},
|
||||
name: "enumerating posix groups",
|
||||
args: svc("-bdc=glauth,dc=com", "(objectclass=posixgroup)"),
|
||||
want: env.expectedgroup,
|
||||
msg: "should get a list starting with the 'superheros' group",
|
||||
},
|
||||
{
|
||||
Name: "searching for members of the 'superheros' group",
|
||||
Check: func(t testing.TB) {
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "(memberOf=ou=superheros,ou=groups,dc=glauth,dc=com)")
|
||||
if got, want := out, env.expectedfirstaccount; got != want {
|
||||
t.Fatalf("should get a list starting with the 'hackers' user\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
},
|
||||
name: "searching for members of the 'superheros' group",
|
||||
args: svc("-bdc=glauth,dc=com", "(memberOf=ou=superheros,ou=groups,dc=glauth,dc=com)"),
|
||||
want: env.expectedfirstaccount,
|
||||
msg: listFromUser,
|
||||
},
|
||||
{
|
||||
Name: "performing a complex search for members of 'superheros' group",
|
||||
Check: func(t testing.TB) {
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "(&(objectClass=*)(memberOf=ou=superheros,ou=groups,dc=glauth,dc=com))")
|
||||
if got, want := out, env.expectedfirstaccount; got != want {
|
||||
t.Fatalf("should get a list starting with the 'hackers' user\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
},
|
||||
name: "performing a complex search for members of 'superheros' group",
|
||||
args: svc("-bdc=glauth,dc=com", "(&(objectClass=*)(memberOf=ou=superheros,ou=groups,dc=glauth,dc=com))"),
|
||||
want: env.expectedfirstaccount,
|
||||
msg: listFromUser,
|
||||
},
|
||||
{
|
||||
Name: "searching for the 'hacker' user using a TOTP-enabled account",
|
||||
Check: func(t testing.TB) {
|
||||
if !env.checkTOTP {
|
||||
t.SkipNow()
|
||||
}
|
||||
otpvalue := doRunGetFirst(RD, "oathtool", "--totp", "-b", "-d", "6", "3hnvnk4ycv44glzigd6s25j4dougs3rk")
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.otpdn, "-w", "mysecret"+otpvalue, "-x", "-bou=superheros,dc=glauth,dc=com", "cn=hackers")
|
||||
if got, want := out, env.scopedaccount; got != want {
|
||||
t.Fatalf("should find them in in the 'superheros' group\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
},
|
||||
name: "searching for the 'hacker' user using a TOTP-enabled account",
|
||||
skip: !env.checkTOTP,
|
||||
args: otp("mysecret" + otpvalue),
|
||||
want: env.scopedaccount,
|
||||
msg: inSuperheros,
|
||||
},
|
||||
{
|
||||
Name: "searching for the 'hacker' user using a TOTP-enabled account and no value",
|
||||
Check: func(t testing.TB) {
|
||||
if !env.checkTOTP {
|
||||
t.SkipNow()
|
||||
}
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.otpdn, "-w", "mysecret", "-x", "-bou=superheros,dc=glauth,dc=com", "cn=hackers")
|
||||
if got, want := out, "exit status 49"; got != want {
|
||||
t.Fatalf("should get 'Invalid credentials(49)'\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
},
|
||||
name: "searching for the 'hacker' user using a TOTP-enabled account and no value",
|
||||
skip: !env.checkTOTP,
|
||||
args: otp("mysecret"),
|
||||
want: "exit status 49",
|
||||
msg: badCreds,
|
||||
},
|
||||
{
|
||||
Name: "searching for the 'hacker' user using a TOTP-enabled account and the wrong value",
|
||||
Check: func(t testing.TB) {
|
||||
if !env.checkTOTP {
|
||||
t.SkipNow()
|
||||
}
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.otpdn, "-w", "mysecret123456", "-x", "-bou=superheros,dc=glauth,dc=com", "cn=hackers")
|
||||
if got, want := out, "exit status 49"; got != want {
|
||||
t.Fatalf("should get 'Invalid credentials(49)'\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
},
|
||||
name: "searching for the 'hacker' user using a TOTP-enabled account and the wrong value",
|
||||
skip: !env.checkTOTP,
|
||||
args: otp("mysecret123456"),
|
||||
want: "exit status 49",
|
||||
msg: badCreds,
|
||||
},
|
||||
{
|
||||
Name: "searching for the 'hacker' user",
|
||||
Check: func(t testing.TB) {
|
||||
if env.checkemployeetype == "" {
|
||||
t.SkipNow()
|
||||
}
|
||||
out := doRunGetSecond(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", env.checkemployeetype, "employeetype")
|
||||
if got, want := out, "employeetype: Intern"; got != want {
|
||||
t.Fatalf("type should be 'Intern'\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
},
|
||||
name: "searching for the 'hacker' user",
|
||||
skip: env.checkemployeetype == "",
|
||||
args: svc("-bdc=glauth,dc=com", env.checkemployeetype, "employeetype"),
|
||||
line: getSecond,
|
||||
want: "employeetype: Intern",
|
||||
msg: "type should be 'Intern'",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
tc.Check(t)
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if tc.skip {
|
||||
t.SkipNow()
|
||||
}
|
||||
line := tc.line
|
||||
if line == nil {
|
||||
line = getFirst
|
||||
}
|
||||
if got := runAndSelect(t, line, "ldapsearch", tc.args...); got != tc.want {
|
||||
t.Fatalf("%s\ngot: %s\nwant: %s", tc.msg, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -195,14 +307,15 @@ func TestSampleSimple(t *testing.T) {
|
||||
checkemployeetype: "cn=hackers",
|
||||
}
|
||||
|
||||
svc := startSvc(SD, "bin/glauth", "-c", "sample-simple.cfg")
|
||||
svc := startSvc(t, "127.0.0.1:3893", glauthBinary(), "-c", ldapOnlyConfig(t, "sample-simple.cfg"))
|
||||
batteryOfTests(t, &env)
|
||||
stopSvc(svc)
|
||||
}
|
||||
|
||||
func TestSQLitePlugin(t *testing.T) {
|
||||
matchingLibrary := doRunGetFirst(RD, "ls", "bin/sqlite.so")
|
||||
if matchingLibrary != "bin/sqlite.so" {
|
||||
_, err := os.Stat("bin/sqlite.so")
|
||||
if err != nil {
|
||||
t.Log("Unable to find sqlite plugin")
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
@@ -221,14 +334,20 @@ func TestSQLitePlugin(t *testing.T) {
|
||||
checkemployeetype: "",
|
||||
}
|
||||
|
||||
svc := startSvc(SD, "bin/glauth", "-c", "pkg/plugins/glauth-sqlite/sample-database.cfg")
|
||||
svc := startSvc(t, "127.0.0.1:3893", glauthBinary(), "-c", "pkg/plugins/glauth-sqlite/sample-database.cfg")
|
||||
batteryOfTests(t, &env)
|
||||
stopSvc(svc)
|
||||
}
|
||||
|
||||
func TestLdapInjection(t *testing.T) {
|
||||
matchingContainers := doRunGetFirst(RD, "sh", "-c", "docker ps | grep ldap-service | wc -l")
|
||||
out, err := doRun("sh", "-c", "docker ps | grep ldap-service | wc -l")
|
||||
if err != nil {
|
||||
t.Log("Unable to find docker container:", out)
|
||||
t.SkipNow()
|
||||
}
|
||||
matchingContainers := getFirst(out)
|
||||
if matchingContainers != "1" {
|
||||
t.Log("Unable to find docker container:", out)
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
@@ -247,52 +366,89 @@ func TestLdapInjection(t *testing.T) {
|
||||
checkemployeetype: "",
|
||||
}
|
||||
|
||||
svc := startSvc(SD, "bin/glauth", "-c", "sample-ldap-injection.cfg")
|
||||
svc := startSvc(t, "127.0.0.1:3893", glauthBinary(), "-c", "sample-ldap-injection.cfg")
|
||||
batteryOfTests(t, &env)
|
||||
stopSvc(svc)
|
||||
}
|
||||
|
||||
// -----=============================================================================----
|
||||
|
||||
const SD = 3 // Start Delay
|
||||
const RD = 2 // Response Delay
|
||||
|
||||
func startSvc(delay time.Duration, name string, arg ...string) *exec.Cmd {
|
||||
cmd := exec.Command(name, arg...)
|
||||
cmd.Start()
|
||||
if delay != 0 {
|
||||
time.Sleep(time.Second * delay)
|
||||
func waitForPort(host string, timeout time.Duration) error {
|
||||
start := time.Now()
|
||||
for {
|
||||
conn, err := ldap.Dial("tcp", host)
|
||||
if err == nil {
|
||||
err = conn.Bind("_", "_")
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
return fmt.Errorf("Successfully authenticated to glauth. This should not happen")
|
||||
}
|
||||
e := &ldap.Error{}
|
||||
if errors.As(err, &e) && e.ResultCode == 49 {
|
||||
conn.Close()
|
||||
return nil
|
||||
}
|
||||
conn.Close()
|
||||
// We have an error, it's probably a connection error or an error saying that ldap isn't setup yet
|
||||
// try again
|
||||
}
|
||||
if time.Since(start) > timeout {
|
||||
return fmt.Errorf("timeout waiting for port %s: %w", host, err)
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func startSvc(t *testing.T, addr string, name string, arg ...string) *exec.Cmd {
|
||||
cmd := exec.Command(name, arg...)
|
||||
cmd.Stderr = t.Output()
|
||||
cmd.Stdout = t.Output()
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatal("Failed to start glauth:", err)
|
||||
}
|
||||
if err := waitForPort(addr, time.Second*2); err != nil {
|
||||
t.Fatal("Failed to wait for glauth to start:", err)
|
||||
}
|
||||
// the port is listening but the ldap server may not be accepting connections yet
|
||||
// TODO: make glauth listen as the last part of the initialization that way as soon as it listens it can process connections
|
||||
return cmd
|
||||
}
|
||||
|
||||
func stopSvc(svc *exec.Cmd) {
|
||||
svc.Process.Kill()
|
||||
svc.Process.Wait()
|
||||
}
|
||||
|
||||
func doRunGetFirst(delay time.Duration, name string, arg ...string) string {
|
||||
out := strings.SplitN(doRun(delay, name, arg...), "\n", 2)
|
||||
if out == nil || len(out) < 1 {
|
||||
func getFirst(output string) string {
|
||||
out := strings.SplitN(output, "\n", 2)
|
||||
if len(out) < 1 {
|
||||
return "*fail*"
|
||||
}
|
||||
return out[0]
|
||||
}
|
||||
|
||||
func doRunGetSecond(delay time.Duration, name string, arg ...string) string {
|
||||
out := strings.SplitN(doRun(delay, name, arg...), "\n", 3)
|
||||
if out == nil || len(out) < 2 {
|
||||
func getSecond(output string) string {
|
||||
out := strings.SplitN(output, "\n", 3)
|
||||
if len(out) < 2 {
|
||||
return "*fail*"
|
||||
}
|
||||
return out[1]
|
||||
}
|
||||
|
||||
func doRun(delay time.Duration, name string, arg ...string) string {
|
||||
out, err := exec.Command(name, arg...).Output()
|
||||
func doRun(name string, arg ...string) (string, error) {
|
||||
out, err := exec.Command(name, arg...).CombinedOutput()
|
||||
if err != nil {
|
||||
return string(bytes.TrimSpace(out)), err
|
||||
}
|
||||
|
||||
return string(bytes.TrimSpace(out)), nil
|
||||
}
|
||||
|
||||
func runAndSelect(t testing.TB, pick func(string) string, name string, arg ...string) string {
|
||||
t.Helper()
|
||||
out, err := doRun(name, arg...)
|
||||
if err != nil {
|
||||
t.Log(out)
|
||||
return err.Error()
|
||||
}
|
||||
if delay != 0 {
|
||||
time.Sleep(time.Second * delay)
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
return pick(out)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/glauth/glauth/v2
|
||||
|
||||
go 1.21
|
||||
go 1.25
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2
|
||||
@@ -8,7 +8,7 @@ require (
|
||||
github.com/arl/statsviz v0.6.0
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/glauth/ldap v0.0.0-20231210225823-b9bf4d1baf6e
|
||||
github.com/glauth/ldap v0.0.0-20240419171521-1f14f5c1b4ad
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.18.0
|
||||
@@ -34,7 +34,7 @@ require (
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||
github.com/go-logr/logr v1.3.0 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
|
||||
@@ -21,10 +21,10 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/glauth/ldap v0.0.0-20231210225823-b9bf4d1baf6e h1:ohe1O2DUo198delHsQvA8O+CgAPP8wv1SmSxLPlO9ao=
|
||||
github.com/glauth/ldap v0.0.0-20231210225823-b9bf4d1baf6e/go.mod h1:EHMcFuVSIs0huelHOhaGtj9IKMB/Dmxo+jDheipjtYA=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/glauth/ldap v0.0.0-20240419171521-1f14f5c1b4ad h1:2ERGXiofR9ISXg4evamCngs2EeuMhVJRuDwWGa6bS6s=
|
||||
github.com/glauth/ldap v0.0.0-20240419171521-1f14f5c1b4ad/go.mod h1:5ueZMujvJ5nuYPyBj6uQTPV4R1YshboRa/osaP4Emfs=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
|
||||
+47
-5
@@ -12,8 +12,29 @@ var (
|
||||
log zerolog.Logger
|
||||
)
|
||||
|
||||
var secureCipherSuites = []uint16{
|
||||
// TLS 1.3 cipher suites (automatically used when TLS 1.3 is negotiated)
|
||||
tls.TLS_AES_128_GCM_SHA256,
|
||||
tls.TLS_AES_256_GCM_SHA384,
|
||||
tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||
|
||||
// TLS 1.2 ECDHE cipher suites (Forward Secrecy)
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
|
||||
// Additional secure TLS 1.2 cipher suites (CBC with HMAC-SHA256)
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
// tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
// tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
|
||||
}
|
||||
|
||||
// MakeTLS generates a tls.Config
|
||||
func MakeTLS(clientCert, key []byte) (*tls.Config, error) {
|
||||
func MakeTLS(clientCert, key []byte, legacy bool) (*tls.Config, error) {
|
||||
if clientCert == nil && key == nil {
|
||||
return new(tls.Config), nil
|
||||
}
|
||||
@@ -55,11 +76,24 @@ func MakeTLS(clientCert, key []byte) (*tls.Config, error) {
|
||||
log.Debug().Interface("root.ca", rootCAs)
|
||||
log.Debug().Interface("certificates", []tls.Certificate{cert})
|
||||
|
||||
if legacy {
|
||||
return &tls.Config{
|
||||
RootCAs: rootCAs,
|
||||
MinVersion: tls.VersionTLS10,
|
||||
MaxVersion: tls.VersionTLS13,
|
||||
PreferServerCipherSuites: true,
|
||||
CipherSuites: nil,
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &tls.Config{
|
||||
RootCAs: rootCAs,
|
||||
MinVersion: tls.VersionTLS10,
|
||||
MaxVersion: tls.VersionTLS13,
|
||||
Certificates: []tls.Certificate{cert},
|
||||
RootCAs: rootCAs,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
MaxVersion: tls.VersionTLS13,
|
||||
PreferServerCipherSuites: true,
|
||||
CipherSuites: secureCipherSuites,
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -79,3 +113,11 @@ func DecodePEM(certPEM []byte) tls.Certificate {
|
||||
|
||||
return cert
|
||||
}
|
||||
|
||||
func CipherSuiteNames(suites []uint16) []string {
|
||||
var names []string
|
||||
for _, suite := range suites {
|
||||
names = append(names, tls.CipherSuiteName(suite))
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
+63
-25
@@ -179,11 +179,13 @@ func parseConfigFile(configFileLocation string, args map[string]interface{}) (*c
|
||||
// Patch with default values where not specified
|
||||
for i := range cfg.Backends {
|
||||
if cfg.Backends[i].NameFormat == "" {
|
||||
cfg.Backends[i].NameFormat = "cn"
|
||||
cfg.Backends[i].NameFormat = "cn,uid"
|
||||
}
|
||||
cfg.Backends[i].NameFormatAsArray = strings.Split(cfg.Backends[i].NameFormat, ",")
|
||||
if cfg.Backends[i].GroupFormat == "" {
|
||||
cfg.Backends[i].GroupFormat = "ou"
|
||||
cfg.Backends[i].GroupFormat = "ou,cn"
|
||||
}
|
||||
cfg.Backends[i].GroupFormatAsArray = strings.Split(cfg.Backends[i].GroupFormat, ",")
|
||||
if cfg.Backends[i].SSHKeyAttr == "" {
|
||||
cfg.Backends[i].SSHKeyAttr = "sshPublicKey"
|
||||
}
|
||||
@@ -221,9 +223,7 @@ func usersCustomAttributes(location string, config *config.Config) {
|
||||
x := make(map[string]interface{})
|
||||
|
||||
for _, attribute := range user.CustomAttributes {
|
||||
err := md.PrimitiveDecode(attribute, x)
|
||||
|
||||
fmt.Println(err, x)
|
||||
_ = md.PrimitiveDecode(attribute, x)
|
||||
|
||||
for k, v := range x {
|
||||
|
||||
@@ -386,13 +386,47 @@ func validateConfig(cfg *config.Config) (*config.Config, error) {
|
||||
|
||||
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.LDAPS.Cert == "" && cfg.LDAPS.CertPath != "" {
|
||||
byteData, err := os.ReadFile(cfg.LDAPS.CertPath)
|
||||
if err != nil {
|
||||
return cfg, fmt.Errorf("unable to read TLS certificate file")
|
||||
}
|
||||
cfg.LDAPS.Cert = string(byteData)
|
||||
}
|
||||
|
||||
if cfg.LDAPS.Key == "" && cfg.LDAPS.KeyPath != "" {
|
||||
byteData, err := os.ReadFile(cfg.LDAPS.KeyPath)
|
||||
if err != nil {
|
||||
return cfg, fmt.Errorf("unable to read TLS key file")
|
||||
}
|
||||
cfg.LDAPS.Key = string(byteData)
|
||||
}
|
||||
|
||||
// Ugly wart for backward compatibility
|
||||
// In the olden times, we would simply say "please read from this file"
|
||||
// This is now the role of the Path files.
|
||||
if !strings.HasPrefix(cfg.LDAPS.Cert, "-----") {
|
||||
byteData, err := os.ReadFile(cfg.LDAPS.Cert)
|
||||
if err != nil {
|
||||
return cfg, fmt.Errorf("unable to read TLS certificate file")
|
||||
}
|
||||
cfg.LDAPS.Cert = string(byteData)
|
||||
}
|
||||
if !strings.HasPrefix(cfg.LDAPS.Key, "-----") {
|
||||
byteData, err := os.ReadFile(cfg.LDAPS.Key)
|
||||
if err != nil {
|
||||
return cfg, fmt.Errorf("unable to read TLS key file")
|
||||
}
|
||||
cfg.LDAPS.Key = string(byteData)
|
||||
}
|
||||
|
||||
if cfg.LDAPS.Cert == "" || cfg.LDAPS.Key == "" {
|
||||
return cfg, fmt.Errorf("LDAPS was enabled but no certificate or key were specified: please disable LDAPS or use the 'cert'/'key' or 'certpath'/'keypath' options")
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.LDAP.Enabled {
|
||||
@@ -401,24 +435,27 @@ func validateConfig(cfg *config.Config) (*config.Config, error) {
|
||||
return cfg, fmt.Errorf("no LDAP bind address was specified: please disable LDAP or use the 'listen' option")
|
||||
}
|
||||
|
||||
if cfg.LDAP.TLS && cfg.LDAP.TLSCert == "" && cfg.LDAP.TLSCertPath != "" {
|
||||
byteData, err := os.ReadFile(cfg.LDAP.TLSCertPath)
|
||||
cfg.LDAP.TLSCert = string(byteData)
|
||||
if cfg.LDAP.TLS {
|
||||
if cfg.LDAP.TLSCert == "" && cfg.LDAP.TLSCertPath != "" {
|
||||
byteData, err := os.ReadFile(cfg.LDAP.TLSCertPath)
|
||||
if err != nil {
|
||||
return cfg, fmt.Errorf("unable to read TLS certificate file")
|
||||
}
|
||||
cfg.LDAP.TLSCert = string(byteData)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return cfg, fmt.Errorf("unable to read TLS certificate file")
|
||||
if cfg.LDAP.TLSKey == "" && cfg.LDAP.TLSKeyPath != "" {
|
||||
byteData, err := os.ReadFile(cfg.LDAP.TLSKeyPath)
|
||||
if err != nil {
|
||||
return cfg, fmt.Errorf("unable to read TLS key file")
|
||||
}
|
||||
cfg.LDAP.TLSKey = string(byteData)
|
||||
}
|
||||
|
||||
if cfg.LDAP.TLSCert == "" || cfg.LDAP.TLSKey == "" {
|
||||
return cfg, fmt.Errorf("StartTLS was enabled but no certificate or key were specified: please disable StartTLS or use the 'tlscert'/'tlskey' or 'tlscertpath'/'tlskeypath' options")
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.LDAP.TLS && cfg.LDAP.TLSKey == "" && cfg.LDAP.TLSKeyPath != "" {
|
||||
byteData, err := os.ReadFile(cfg.LDAP.TLSKeyPath)
|
||||
cfg.LDAP.TLSKey = string(byteData)
|
||||
|
||||
if err != nil {
|
||||
return cfg, fmt.Errorf("unable to read TLS key file")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//spew.Dump(cfg)
|
||||
@@ -430,8 +467,9 @@ func validateConfig(cfg *config.Config) (*config.Config, error) {
|
||||
case "ldap":
|
||||
case "owncloud":
|
||||
case "plugin":
|
||||
case "embed":
|
||||
default:
|
||||
return cfg, fmt.Errorf("invalid backend %s - must be 'config', 'ldap', 'owncloud' or 'plugin'", cfg.Backends[i].Datastore)
|
||||
return cfg, fmt.Errorf("invalid backend %s - must be 'config', 'ldap', 'owncloud', 'plugin' or 'embed", cfg.Backends[i].Datastore)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@ package version
|
||||
|
||||
// Version is the tag of the latest release, it gets changed by the ci
|
||||
// process when a release is happening
|
||||
const Version = "v2.3.1" // x-release-please-version
|
||||
const Version = "v2.5.2" // x-release-please-version
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
olcAccess: to dn.subtree="dc=glauth,dc=com"
|
||||
by dn.exact="cn=admin,dc=glauth,dc=com" manage
|
||||
by * break
|
||||
@@ -0,0 +1,7 @@
|
||||
dn: olcDatabase={1}mdb,cn=config
|
||||
changetype: modify
|
||||
delete: olcAccess
|
||||
-
|
||||
add: olcAccess
|
||||
olcAccess: to attrs=userPassword,shadowLastChange by self write by dn="cn=serviceuser,cn=svcaccts,ou=users,dc=glauth,dc=com" write by anonymous auth by * none
|
||||
olcAccess: to * by self write by dn="cn=serviceuser,cn=svcaccts,ou=users,dc=glauth,dc=com" write by users read by * none
|
||||
@@ -0,0 +1,4 @@
|
||||
dn: cn=serviceuser,cn=svcaccts,ou=users,dc=glauth,dc=com
|
||||
changetype: modify
|
||||
replace: userPassword
|
||||
userPassword: mysecret
|
||||
@@ -0,0 +1,3 @@
|
||||
TLS_REQCERT never
|
||||
URI ldaps://192.168.1.222
|
||||
BASE dc=test,dc=example,dc=com
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
for arg in "$@"; do
|
||||
if [[ "$arg" == *"="* ]]; then
|
||||
key="${arg%%=*}"
|
||||
value="${arg#*=}"
|
||||
eval "$key"="'$value'"
|
||||
else
|
||||
eval "$arg"=true
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$ipaddr" == "" ]]; then
|
||||
echo "Please provide ipaddr=<ip address> to create/use" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$ipmask" == "" ]]; then
|
||||
ipmask=24
|
||||
fi
|
||||
|
||||
eipaddr=$(echo "$ipaddr" | sed 's/\./\\./g')
|
||||
ifname=$(ip -brief addr show | awk "/$eipaddr\// {print \$1}")
|
||||
if [[ "$ifname" == "" ]]; then
|
||||
ping -c 1 $ipaddr &>/dev/null
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo "It looks like you specified an already allocated ip address." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$ifname" == "" ]]; then
|
||||
passed=0
|
||||
if [[ "$ipmask" == "" ]]; then
|
||||
passed=1
|
||||
fi
|
||||
if [[ "$iface" == "" ]]; then
|
||||
passed=2
|
||||
fi
|
||||
if [[ $passed -ne 0 ]]; then
|
||||
echo "To create an interface, specify ipaddr=<ip address> ipmask=<mask> iface=<interface name>" >&2
|
||||
echo "Example: ipaddr=192.168.1.222 ipmask=24 iface=en0" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Creating $ipaddr/$ipmask on interface $iface"
|
||||
sudo ip addr add $ipaddr/$ipmask dev $iface
|
||||
fi
|
||||
|
||||
if [[ "$domain" == "" ]]; then
|
||||
domain="TEST.EXAMPLE.COM"
|
||||
fi
|
||||
if [[ "$defaultdns" == "" ]]; then
|
||||
defaultdns="192.168.1.1"
|
||||
fi
|
||||
|
||||
echo "Running Samba AD, listening on $ipaddr for domain $domain"
|
||||
docker run -d
|
||||
--name ad \
|
||||
--hostname DC1 \
|
||||
--restart unless-stopped \
|
||||
-e REALM='TEST.LOCAL' \
|
||||
-e DOMAIN='TEST' \
|
||||
-e ADMIN_PASS='MySecret2026!' \
|
||||
-e DNS_FORWARDER='8.8.8.8' \
|
||||
-v dc_etc:/usr/local/samba/etc \
|
||||
-v dc_private:/usr/local/samba/private \
|
||||
-v dc_var:/usr/local/samba/var \
|
||||
-p $ipaddr:53:53 \
|
||||
-p $ipaddr:53:53/udp \
|
||||
-p $ipaddr:88:88 \
|
||||
-p $ipaddr:88:88/udp \
|
||||
-p $ipaddr:135:135 \
|
||||
-p $ipaddr:137-138:137-138/udp \
|
||||
-p $ipaddr:139:139 \
|
||||
-p $ipaddr:389:389 \
|
||||
-p $ipaddr:389:389/udp \
|
||||
-p $ipaddr:445:445 \
|
||||
-p $ipaddr:464:464 \
|
||||
-p $ipaddr:464:464/udp \
|
||||
-p $ipaddr:636:636 \
|
||||
-p $ipaddr:1024-1044:1024-1044 \
|
||||
-p $ipaddr:3268-3269:3268-3269 \
|
||||
diegogslomp/samba-ad-dc
|
||||
@@ -0,0 +1,33 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFtDCCA5ygAwIBAgIEhCJsaTANBgkqhkiG9w0BAQsFADB6MR0wGwYDVQQKExRT
|
||||
YW1iYSBBZG1pbmlzdHJhdGlvbjE3MDUGA1UECxMuU2FtYmEgLSB0ZW1wb3Jhcnkg
|
||||
YXV0b2dlbmVyYXRlZCBDQSBjZXJ0aWZpY2F0ZTEgMB4GA1UEAxMXVEVTVERDLnRl
|
||||
c3QuZXhhbXBsZS5jb20wHhcNMjYwMTE4MDAwMDA0WhcNMjcxMjE5MDAwMDA0WjB6
|
||||
MR0wGwYDVQQKExRTYW1iYSBBZG1pbmlzdHJhdGlvbjE3MDUGA1UECxMuU2FtYmEg
|
||||
LSB0ZW1wb3JhcnkgYXV0b2dlbmVyYXRlZCBDQSBjZXJ0aWZpY2F0ZTEgMB4GA1UE
|
||||
AxMXVEVTVERDLnRlc3QuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4IC
|
||||
DwAwggIKAoICAQDuYBt2jNruSR4xL0wI8Nx2uq+jQKpiViWzDPw4eivNrLXc8Bdr
|
||||
o9GqCo4uokPZiFS6jGez3holnMKF1bxU8uES2ma228zRIJVAHo0qU2O8zGoMtAuL
|
||||
Z+F9gZAVSQKUMp0Vl2yyNERY453Ne4B2Oz92G87YtNuDyNYzuK4778FiE5iXjvan
|
||||
CsAENXrspfxvn8QD5IloHPFoieFkfeaigDA6Zjtr2be+LhDbc2wn6YY7t8Z+5wxG
|
||||
UPUy8oYKQ5lyHSn6I7aezAJpJJHSN4eSg76smTMjUOAnLqJJHPurGF9zzq+maRg+
|
||||
M2runjqRcnZzQtkdZJayK8GQ39kEFpHnl1PueexFEIA5PxHaJEbapAiL7nP65bAy
|
||||
Ey6wiOVvuLgztf8CU6hT/Not3qVG5lCGiK1VC9W684X5vQcVnQXTAIY3Edt91u8C
|
||||
/wt4i5j58orVbcghAZMsU3FKjjqyvQMMSD2Pwgf6+m2mVQIAnZOrkErsQrltmFxO
|
||||
CPBbRY+5Yg8v2ZucMWfKNy4IEzm8LyddrZKAENbW73/bZtPCw7cJqeb6ZaWVzrqW
|
||||
BAgMiyrBYq/oGnXP6rHnTpB02LWTx8dvPPmEgvPhjecCRKroesYeKX38KWywGalA
|
||||
xXbs+Eo9ApGst7qhkenAzM3E7Fy56OoELUAKGQW9gtVEnn+8BofLhezUyQIDAQAB
|
||||
o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU
|
||||
ShGTEQzxmK0IrRMPiPYbRxfntYQwDQYJKoZIhvcNAQELBQADggIBAFxD6ShgnqwK
|
||||
6oMzXKZOXoVfVu8yFKhL9Z3ui+6Q/duckzLf873sMlxz9msLTjH1O2Pk9gaND9/l
|
||||
25t37BvVLuNTJKHvTfte53l8fQUrqCgOq2feARoXyCAA77bTUh+uSGsjZYGbSJx/
|
||||
M93fOtabElD9T3o3B0L2EJyL7LHZ3in9MOyVBBcCnOMqi+1eoQEnaoj5TRnE8eEu
|
||||
RuTs7Vu3lceZuCvH1mTRPis2D8bPCWa3aAI6w+LZoJNP3B1izo6Arjje7TBHBMGJ
|
||||
A65o9LP1+OeZ5EA+J3nIkU5rSHu+Pk5biBNEThsnkGjEhSTXZPCVgZRQJOgQyvnf
|
||||
LPWUOMdMLsnsO2oW3/BG6zTmbZw4CMzE8yZUtNZxph7+N1ukzYWZbSwDADxxi+dQ
|
||||
vFQk6tguKpxBCko+pWajgNcxG97MFQMMCfGfva7xdaqBSB4R8sdmQm+wknAYQrlm
|
||||
Jc6csvkJgoIHvyBn9yfSKYNvbjh8RUGA2TZMWrbBMdt51ivw6e1NM16OneGtELh+
|
||||
zGlgv0EfJPaf9IG+RWqvMuhGFF1DgqBi8l816XFy4g3SEFC2F0D6VAv9mQZdq8q1
|
||||
9y7B9vpcBQbNiHqJahIEwgp1JlHdoj281Ms2KixBf88O0y7zSMbyejxHtGv8IUVD
|
||||
+Nth1G9FpENenG73njJIh1dLLYyHvxnp
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,30 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFETCCAvmgAwIBAgIUHfUrLa/sMlGeT6VIZdkj73UjZfkwDQYJKoZIhvcNAQEL
|
||||
BQAwGDEWMBQGA1UEAwwNU2FtYmEgVGVzdCBDQTAeFw0yNjAxMTgwMTEzMzVaFw0z
|
||||
NjAxMTYwMTEzMzVaMBgxFjAUBgNVBAMMDVNhbWJhIFRlc3QgQ0EwggIiMA0GCSqG
|
||||
SIb3DQEBAQUAA4ICDwAwggIKAoICAQC464FD54YJ1rgi/5Z9DQrIbEzED8NhMjo/
|
||||
ndqD+0sfqkiYjQEqAh7Wpj+6r9D9AZ1wnTdF2Ee767OTwkSA2lG5aQAF9HDFzgIJ
|
||||
8yVbeqJIABOzKotLTl1wCNeRx+YeY69xcytzsQ6iNFyOVV/CbkbxtyJvDfxD9rC5
|
||||
Pwf1A7yBLz0H8U+kXBKC0SCDSW4+SAgmJQu3NQ7wd2eg+EBH/cPVhw0F97K3Yfb0
|
||||
c8wekQdqAMrvfo/jq81/JADUEmzi1lyQ34zX2ToBIeu42/oTBmJ8ZX2ZAFgUu0y1
|
||||
Kh+rmmYLhszPLfwB/28j1wizd/iVdpN0+ssJj3y+rEgKeXFfo1x7KSWprHUCJzZk
|
||||
+T7l59tEg7ZiSuRexgkc3Rf+4DYNq7JzCFnVnBvhC++fym29JRrriX0DOUYMGAS3
|
||||
dFBiLJT65kdLCwrgkCAZTbsNKTVkEQx3QYn58dUYMTmQXndg4AEG0VUa4s+oqOFu
|
||||
jokyeqVqp37CUm0MU9cKJVnkMqWkqI9JRTBe90xHSy5xrmB0N1zM/FbUTBvMCh6Z
|
||||
t7ZAiB3gB1LTXaDRWafJ+opYJtPkVhvAlmFjEwuYCYdUM8lz+i/MKK8hrAoztUvr
|
||||
C2n5qoDUX5Tm9VygefWVxzCvyzTy+FKEq+4aU8ye0zohCJ+UP1SMi3j98aGdVjBJ
|
||||
sI9KhOQNmQIDAQABo1MwUTAdBgNVHQ4EFgQUgFOZGLtmwudA8tVaKeLvowVQLMYw
|
||||
HwYDVR0jBBgwFoAUgFOZGLtmwudA8tVaKeLvowVQLMYwDwYDVR0TAQH/BAUwAwEB
|
||||
/zANBgkqhkiG9w0BAQsFAAOCAgEAPNmj5RCacwRbySYERx8XBlcV50DRFnFS51G0
|
||||
EEKObiOBxt/xboAt/bPw+6dVeqS0JILrgMJhHn7dufRTdnGK/z9VkRDrTh6FudAk
|
||||
16qdhglXzPrPzETGl1VHJxQEkqm+qkUeD4r9waUP5ZIXdsUrvGdnCaXgQRA3v2RD
|
||||
NDvNldmgCTScjTPM4ox0B2iL0P0Uo4pRzQTGuPmubCMv2B8EkBOPIQx/TO2svL6M
|
||||
oal/25v06+xUEyVAxWQPD4xolL8ksEjdqa7l5h/1YkpouWq+jaEDpWGzVqt/fLYI
|
||||
cdGUxSNcaueEmHB2QOklrTdDVvvczAE98ytDZ/zswj8cKKLhe2d3Z3ie40xx+4/x
|
||||
NprbosIGWrHnz0NbDlzXRvd8yk7ZWk4QFRycJb7Ve3Za3hPOlDeKgm5iCjafCS4G
|
||||
C1D8r+SwGjCjgsaL7J/zY4IHyxurRVBU+jZSRgy2WQlR/BQ2gSdVt3BhxsEPgbmu
|
||||
jKOiPltNkqPmUHoXFnosJyAblF8yjYkBPBpbM5luAUiKMYBkdkSA3oSJYK3WuIDl
|
||||
UNcKwcC9sJPu63ciPDtEZlgV5blvMr80PPyJZDofo+KHxWweKxoyXxQoIKCrP53D
|
||||
Av2KGBhIZlJUzNrj4LWMGbf+8BAGL+i1oyM77D/N8L589Y0x1mQMa/quQW+TJ8kH
|
||||
g2wgxQw=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQC464FD54YJ1rgi
|
||||
/5Z9DQrIbEzED8NhMjo/ndqD+0sfqkiYjQEqAh7Wpj+6r9D9AZ1wnTdF2Ee767OT
|
||||
wkSA2lG5aQAF9HDFzgIJ8yVbeqJIABOzKotLTl1wCNeRx+YeY69xcytzsQ6iNFyO
|
||||
VV/CbkbxtyJvDfxD9rC5Pwf1A7yBLz0H8U+kXBKC0SCDSW4+SAgmJQu3NQ7wd2eg
|
||||
+EBH/cPVhw0F97K3Yfb0c8wekQdqAMrvfo/jq81/JADUEmzi1lyQ34zX2ToBIeu4
|
||||
2/oTBmJ8ZX2ZAFgUu0y1Kh+rmmYLhszPLfwB/28j1wizd/iVdpN0+ssJj3y+rEgK
|
||||
eXFfo1x7KSWprHUCJzZk+T7l59tEg7ZiSuRexgkc3Rf+4DYNq7JzCFnVnBvhC++f
|
||||
ym29JRrriX0DOUYMGAS3dFBiLJT65kdLCwrgkCAZTbsNKTVkEQx3QYn58dUYMTmQ
|
||||
Xndg4AEG0VUa4s+oqOFujokyeqVqp37CUm0MU9cKJVnkMqWkqI9JRTBe90xHSy5x
|
||||
rmB0N1zM/FbUTBvMCh6Zt7ZAiB3gB1LTXaDRWafJ+opYJtPkVhvAlmFjEwuYCYdU
|
||||
M8lz+i/MKK8hrAoztUvrC2n5qoDUX5Tm9VygefWVxzCvyzTy+FKEq+4aU8ye0zoh
|
||||
CJ+UP1SMi3j98aGdVjBJsI9KhOQNmQIDAQABAoICADiu8APZn2u6pt0LjZytd8Wx
|
||||
9OIQlupfVB2WDJpyWDfPocspRdcTqVJQya43II+9g/42IvBE+NAw0u4TGZ6JzY1R
|
||||
E1G/+SWZHWThZG0jLUV2yJMeEm62CcxC2Zl1NiPQmMHk9MfU2VB9TnXO07isxxBm
|
||||
+6YhGlfW8ZsHKDBBwfGzgjjCDpCqLH5vDlFQG/v5Zo4SwwGYwQiTAXAFSOcIcZQt
|
||||
fp96qwyted669DHWcPhcX9iD6iV2z1oOtjZLo5bw8JT0AZ9UlgQs2q3iuEhGp7/L
|
||||
+HEC+vz34O6Th/u5K1iTLIrBZMNUAkRxMNiaiNd/7nloOv6mPi3Qb5DOYyNW1wzg
|
||||
TlF5fERqz2OeuFqjnEfoE/uFjvBN0bclenuFTCWDeSxEZqni9qjLhnqK4Iot7/D2
|
||||
P148t4UIT8h5l82Wiod05cenlnPfpWbRRdzVXy0NZe1BTf2U8LXRW1ANdJyfVSC6
|
||||
802KPP6noWnaFYfkhIIdD10ZlTd9vTwdcHEYfoJNNbXcwwI/OslGwPB1a+tp+rSd
|
||||
Vr2l8RoF0/28cjsRnDi7hoaMkNIqszXq1WA+d5zOvmOsfuy/32pfnm+rw8HafYNj
|
||||
SNK0t5sh0PCsHrRrCRfTaOIQbFveABcf0uxbM68xSMAIKH8rpTrp4lcEEO5Yrcrj
|
||||
qDFQ7rw+VnRR0Nu4eX+VAoIBAQDbWhvWkZssTNHR8Xs9OtaAiHktEgJbreaRPvCe
|
||||
GgfyuZ+aYj6M9ivY/z99AJqH4pueXWbZuyBHmPp39zvvN44WP6Yc+4EDyDpiRIxi
|
||||
CjofYzbOIMxnMqgXePPWHXv+UA1zaSu1HGF1kMMeU+cT4knQpkQNrbmioYIetOnU
|
||||
BzdKJt/LTK2KRvHn4VN6Tt49ZnIfrAo8UIOZPdpT6qumX/RENw0/66AU220bLtaP
|
||||
xxZNqipjf56wikkQiHbmP85ZW9dPTR2aUtJZkS7nmJhTuH782ES96soPjYREH/fZ
|
||||
K5TdWn7Se6NcdHgoG5W2voqP3GFUyvKxRNLSK/tjN1UGYKx/AoIBAQDX0LSDjqKX
|
||||
nn+kZBHVXmMtEWVu44CijdDvBIkri5rjO5QdEtgk15gxYXwakRiXgmpsDk/fyBEQ
|
||||
n7iIlao697vNh3cWbe9xuBBhccLcmkrt1qKSO9a1qjbh4lZ10F4SLsZrDz1dTWwf
|
||||
Xvlofl1oNbh9a39xDT9Pa6qt2tZLThianqjHXssMsaEQPNrWduJmM8um/OT+CHoJ
|
||||
6XTzX9SZrMOcevPdAWARdqgBohcfMD2LhMkY0HIkZGdP1/FYdAbHqjK+81pvtPYE
|
||||
JNRQLCgWMWqfUnnay6lMwN/0U7u0AHE4futYMqrn8gGl0JuGLZ6o1BDxpf2Thkfv
|
||||
KpDLDcyFMBnnAoIBAQDGfXvDZm8sXzde603B0oRYOyasCifoHi0OMg46gwO+OQqc
|
||||
AVro47o52n/w9aLgY8a+P3baf+Jt55h/BlJN3c/Ngjsm7s+IRJ/QXIdMEEUYMgdd
|
||||
74AHwmcpFmJP1s5lCruhPAI5GT13zU9DGll4PMTe8hWS6ymYaN3+O2d8XitCBnts
|
||||
1C4Veq1cTyHjA4tHtHSBBIUenxz2hmoSlcBOBXZcZBSxSt7JAOZyOqgd4uHj1aAV
|
||||
+V34EqqbM2GkaQipA9qKABZRl7CRLDdti0AMQl9nHazWzqeZ+008+FCnTDChPUqV
|
||||
3RuuhhZU9OE9RJ7OH3ZT7HJAxy6e0P3igieGZeSxAoIBAQCho2ogAmuavZaCTITi
|
||||
1w/WyGm9NN26YhczfaUzHRuDx7K2V8/VQDh3+7iUuUm+6GnGxq1iQRku50Gx4s6d
|
||||
3cAmPqid/vbQqHh0U10MFu+51QDsH68/Xt4+LmRtVwOuAKSRuNdQE1XA5URN1B8n
|
||||
lty36xSHWtGpLU2navkHa9WXsO9ea4wO1Nd6LuoEqQQGYhLM3iqwbS7iT51NTuAP
|
||||
vUrA34HYbFhEnCdXGczRijL+VRS85honUZ/jOCxGAJi3YK0jQWbhtZpxHhh6d6gK
|
||||
fzaJX59ulQ0XNHpIJ5K1TYN/OegQpdpFYIeFZomlZHOppTOPmbAjlvY3lhzeSFZ/
|
||||
BrjPAoIBAQCsJqoFNpJpJFT/gGL6xWS4O158E5GxYXl3raN/dfBMGZnbTr0nHrbC
|
||||
4vyrWaQFVsovea7SZnzfLdyesLuJ1FIUhsal166o/g6Vf5wgDn7LHZ202tt2DokV
|
||||
ECETQTA+VBTXysc7/dwsTohbee7Jg6xZ/qHSdP6nUan9xiC3pOx0G/vT5FaH9kBJ
|
||||
dJ2mQ93n/l1A6q9Hn5AFyoCqniOszVsQ357N9uxakQiX48JKKWG7+BneKtGzN2bu
|
||||
9ArlPSNscNDb4yF2fqksTHvIYbGuJ3icbObjWUKOCQbtpva4Rkrnoqqg5nHM0L+f
|
||||
3tGhl1yrO4/YIpQrp0OgMV1HDYXEOwwI
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -0,0 +1 @@
|
||||
59634A59EBDC029E7D479619E12C1868317CE811
|
||||
@@ -0,0 +1,17 @@
|
||||
[req]
|
||||
default_bits = 2048
|
||||
prompt = no
|
||||
default_md = sha256
|
||||
distinguished_name = dn
|
||||
req_extensions = req_ext
|
||||
|
||||
[dn]
|
||||
CN = TESTDC.test.example.com
|
||||
|
||||
[req_ext]
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = TESTDC.test.example.com
|
||||
DNS.2 = localdc.test.example.com
|
||||
IP.1 = 192.168.1.222
|
||||
@@ -0,0 +1,26 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEUDCCAjigAwIBAgIUWWNKWevcAp59R5YZ4SwYaDF86BEwDQYJKoZIhvcNAQEL
|
||||
BQAwGDEWMBQGA1UEAwwNU2FtYmEgVGVzdCBDQTAeFw0yNjAxMTgwMTE0MDRaFw0y
|
||||
ODA0MjIwMTE0MDRaMCIxIDAeBgNVBAMMF1RFU1REQy50ZXN0LmV4YW1wbGUuY29t
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA04ZWa1/UrP6eAkwvc2oL
|
||||
XCewax+5GWWhIj0XVgglblPNcPbK0dLTienfGnfEYxgrCl4o+H7c+SFYcJ/Sg5jw
|
||||
W4/POs+NWoo3ixj71TYWwK+7bUPt2RbNadCAxo4SejdB0zOjpTRn4dDIMZ0+wrg5
|
||||
Ah3jkTnPmwWjpvt280KkojUz+7mhdKFXBBmyjr/ftWU8C+1nyJt5/tVl7PE0VcOL
|
||||
MBok9djjHlj2aKLCw8Hj0CyL+yYFM4keWvYAguXYjVayMC2w8VffvjjO3Wd7/LWi
|
||||
AkDIAhnKyc1oJ8X4h9XK1irEw8lgWH93jyRx54eG32/C3HuNRQA9oQgJrfaK5YCJ
|
||||
kQIDAQABo4GHMIGEMEIGA1UdEQQ7MDmCF1RFU1REQy50ZXN0LmV4YW1wbGUuY29t
|
||||
ghhsb2NhbGRjLnRlc3QuZXhhbXBsZS5jb22HBMCoAd4wHQYDVR0OBBYEFJDd+Fmx
|
||||
vsrKEvBMXBwtBeFxjVjWMB8GA1UdIwQYMBaAFIBTmRi7ZsLnQPLVWini76MFUCzG
|
||||
MA0GCSqGSIb3DQEBCwUAA4ICAQBwS8/1iAFnopOui1V/qdRBsBTWcXdQS7x1PqVU
|
||||
nJkd+T5AYtLu3tJ+PBO9rfgDNVWVz37GB8CTgr4TpGOt7ruaVSZvw9uMRBxYeeit
|
||||
Kf11gssI2eTZESv8yqsa1DtLheQS0AQ7IVjq9wZSw+i3zQ8lQDvFoCWa8nv+C+YZ
|
||||
cfFSnhfXNRQIM+lxn6vrtZa/GgJh6MTF+vCfLvStveCsTvT7i/w0EzO8NFqGBrsX
|
||||
ldombU/WNketQXQEaLAU6NQVvJCjkC9Wgc+KcOQOV77U7CHi3lsc2TAoGlMuJ1lP
|
||||
zyQWJqHKkuWGODeBSd9FfjoZv81+OGPaCAh7bkOHwqAUjgEIL8SV1fX8i13HTkG5
|
||||
nhbN6XoiezjKixfH/EAQHiKhi6/7nzYltz6bJFHpIfBa097l7Yhx/o/HIdKDWV2S
|
||||
VnLIJMJpf/Qn80led1HQPeRWR1EC+fWhBzOg2mCJJpYkpqaOlHVujZ2c5JLckhxH
|
||||
WuIkicvR+LE07IjgZFWUwCXW/PkDDHtiJ0MD6RAiFzFbnuGGMadv3lUBJBUeKL3P
|
||||
PaDC/pue9AAgbWN9ndg8CTorDR2HHjjOyVTkum5bPL2vChuG3/mx+KHcgwDyEWt8
|
||||
cJTxXNaid928rR0aSPsSd4WNlCTDwXhUpClaVRaip/rcfrhJaaFUe7xWpQe8dkvO
|
||||
hiuYEQ==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICvDCCAaQCAQAwIjEgMB4GA1UEAwwXVEVTVERDLnRlc3QuZXhhbXBsZS5jb20w
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDThlZrX9Ss/p4CTC9zagtc
|
||||
J7BrH7kZZaEiPRdWCCVuU81w9srR0tOJ6d8ad8RjGCsKXij4ftz5IVhwn9KDmPBb
|
||||
j886z41aijeLGPvVNhbAr7ttQ+3ZFs1p0IDGjhJ6N0HTM6OlNGfh0MgxnT7CuDkC
|
||||
HeOROc+bBaOm+3bzQqSiNTP7uaF0oVcEGbKOv9+1ZTwL7WfIm3n+1WXs8TRVw4sw
|
||||
GiT12OMeWPZoosLDwePQLIv7JgUziR5a9gCC5diNVrIwLbDxV9++OM7dZ3v8taIC
|
||||
QMgCGcrJzWgnxfiH1crWKsTDyWBYf3ePJHHnh4bfb8Lce41FAD2hCAmt9orlgImR
|
||||
AgMBAAGgVTBTBgkqhkiG9w0BCQ4xRjBEMEIGA1UdEQQ7MDmCF1RFU1REQy50ZXN0
|
||||
LmV4YW1wbGUuY29tghhsb2NhbGRjLnRlc3QuZXhhbXBsZS5jb22HBMCoAd4wDQYJ
|
||||
KoZIhvcNAQELBQADggEBAAQGVIMVIWPbCG3blO8iNHetydfz8XWC3w9zYI03QQBv
|
||||
QdiAYZ9sI+9fxZdsBOOt+vSNyqlwuuQYmmMtMJbUezeOQ8Z4zhkWEzTI4ohhOlzV
|
||||
sq+7aE+PNKw8CYvOkL4ssVIDU99Hm4ppJbo6sRa9YAXPDU3z0UJhMiGZL7o11048
|
||||
W8VcWOzyNmEHYL6/+DXwXGiaLiJmMOJiwMPu8TZVnli4Q0k/j7KGdeh8s2MXLwi8
|
||||
NT0ppSxm+MUIm014OEMA/LOPwRVttHca1VkGR15MJQElakWSaaix533p7lpH3Q3Q
|
||||
tKZc7RIWeoXs5wyo7Tx7Tx7QdmNJIzA2gh4tRiQvm1o=
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDThlZrX9Ss/p4C
|
||||
TC9zagtcJ7BrH7kZZaEiPRdWCCVuU81w9srR0tOJ6d8ad8RjGCsKXij4ftz5IVhw
|
||||
n9KDmPBbj886z41aijeLGPvVNhbAr7ttQ+3ZFs1p0IDGjhJ6N0HTM6OlNGfh0Mgx
|
||||
nT7CuDkCHeOROc+bBaOm+3bzQqSiNTP7uaF0oVcEGbKOv9+1ZTwL7WfIm3n+1WXs
|
||||
8TRVw4swGiT12OMeWPZoosLDwePQLIv7JgUziR5a9gCC5diNVrIwLbDxV9++OM7d
|
||||
Z3v8taICQMgCGcrJzWgnxfiH1crWKsTDyWBYf3ePJHHnh4bfb8Lce41FAD2hCAmt
|
||||
9orlgImRAgMBAAECggEAFTygZ5EMWr6zjC/W9xj3Vq4bbciWaRYwNS5PWKa3ugR1
|
||||
lLkKcuzeVTM4YEFpuQ3qSv+vewd4VcDtzrI/zKY/F4pFjYuHZHGKaGTpP9D5kX3z
|
||||
QjdMOtFUbU0Zk2schXhlG6MhyR/iVXnZ55/4dvdPrfE58aY4EZc22jiBYE9ZanmL
|
||||
Nxqby63ntT/8ZqQpi5iu458TrWTAhOqF+0g5Aj8bWnk6xIcauvFuLORfAcg2kig6
|
||||
+/oUZiQ4U65gvBODckADjTtKmVUlBFFc22xsR5pSm08dI06Fc+1ZWSa3smyA/a+d
|
||||
iOx2Bb+wD8zQMq20pm3R7Kx/yaTLy+AubBzc7OYQMwKBgQDxrgQnMzjLpLEBe+kp
|
||||
8DKSUpy06G176Zoz65NP5BiY1S12YSqu9ydapalIKKH32yu6Gzw0JsTDa0z8glDH
|
||||
Z9VeB9ZM3q+jsPrg7pUUQKC+Yn/Msba5a4BLmdO61qAk1/zPsc/bEIxC6HBXuR5k
|
||||
7bYRivARdGtYRqkkvVKnn5uWqwKBgQDgDuhC4u9ZBx2QN+bDyT+Ppqd+nLLZemTF
|
||||
bJRRbfXsMRpgAvQl5f4mTxu/KqtKm/kKGY7F66B1XuSmZqAEsvVw0oM+0+rCDfdk
|
||||
Iir87ZsdpfoRA5x775zaa2GrW7/hhpvBrenD9vNwo6/G927EjMAhhPaOelxpf+dS
|
||||
fXBrTOKQswKBgQDC+pp9KbTzjo4DXX2SCSgzppSo2y+hHaj7pF33wVUa0DnaXK/7
|
||||
8of8eNRm0+lANsFDZz+d730lD8dPnK8hASNBtl7tK2dWT61HlRQO/8jvcDYc5W8+
|
||||
jn02AQexoAAZWWusAI9p9CLExBQGoeHkmWcfJhFic6M6198UNe2eMIVs+wKBgQCR
|
||||
D1W0KDSJ0R+ExYxz6ZFrMZxzToREZQdlAh0scyKqnkfTLVZgSv02VDVEmR15Gm4r
|
||||
6cwTu6+RDbS/PuYO5MYfG0u5m3370QsAG1b1lee9VD4IUew4GTPV/Xuoc6hRHNMu
|
||||
m6gjJXkKzg75pCxkuDOT//+Y0wONki9zqp7j+U0eHQKBgCkyJ/E+hNFlzqFHugaR
|
||||
ZC8ccttxs2ibTK4J861EvjA7zFkbnAHy2NfsrNPKm6t+TJOkeJ4XrST1vfS3aNJk
|
||||
swHRQz8tWwtnJCsvTF5nCXU7dDQPsyxwRHB39Eq9KtXp7F+PD7kaMk8DbYIzcQ55
|
||||
pe3uxsvKvS6R++L2eg3TfdSB
|
||||
-----END PRIVATE KEY-----
|
||||
+12
-4
@@ -9,8 +9,10 @@ type (
|
||||
Datastore string
|
||||
Insecure bool // For LDAP and owncloud backend only
|
||||
Servers []string // For LDAP and owncloud backend only
|
||||
NameFormat string
|
||||
GroupFormat string
|
||||
NameFormat string // e.g. cn, ou, uid, or a comma separated list of them
|
||||
NameFormatAsArray []string // we will explode NameFormat on commas
|
||||
GroupFormat string // e.g. cn, ou, gid, or a comma separated list of them
|
||||
GroupFormatAsArray []string // we will explode GroupFormat on commas
|
||||
SSHKeyAttr string
|
||||
UseGraphAPI bool // For ownCloud backend only
|
||||
Plugin string // Path to plugin library, for plugin backend only
|
||||
@@ -46,13 +48,18 @@ type (
|
||||
TLSKey string
|
||||
TLSCertPath string
|
||||
TLSKeyPath string
|
||||
LegacyTLS bool
|
||||
}
|
||||
|
||||
LDAPS struct {
|
||||
Enabled bool
|
||||
Listen string
|
||||
Cert string
|
||||
Key string
|
||||
// LDAPS TLS parameters
|
||||
Cert string
|
||||
Key string
|
||||
CertPath string
|
||||
KeyPath string
|
||||
LegacyTLS bool
|
||||
}
|
||||
|
||||
API struct {
|
||||
@@ -111,6 +118,7 @@ type (
|
||||
Name string
|
||||
UnixID int // TODO: remove after deprecating UnixID on User and Group
|
||||
GIDNumber int
|
||||
Capabilities []Capability
|
||||
IncludeGroups []int
|
||||
}
|
||||
|
||||
|
||||
+13
-10
@@ -183,8 +183,9 @@ func (h configHandler) FindPosixAccounts(ctx context.Context, hierarchy string)
|
||||
|
||||
for _, u := range h.cfg.Users {
|
||||
attrs := []*ldap.EntryAttribute{}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: h.backend.NameFormat, Values: []string{u.Name}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "uid", Values: []string{u.Name}})
|
||||
for _, nameAttr := range h.backend.NameFormatAsArray {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: nameAttr, Values: []string{u.Name}})
|
||||
}
|
||||
|
||||
if len(u.GivenName) > 0 {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "givenName", Values: []string{u.GivenName}})
|
||||
@@ -260,9 +261,9 @@ func (h configHandler) FindPosixAccounts(ctx context.Context, hierarchy string)
|
||||
}
|
||||
var dn string
|
||||
if hierarchy == "" {
|
||||
dn = fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(ctx, u.PrimaryGroup), h.backend.BaseDN)
|
||||
dn = fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormatAsArray[0], u.Name, h.backend.GroupFormatAsArray[0], h.getGroupName(ctx, u.PrimaryGroup), h.backend.BaseDN)
|
||||
} else {
|
||||
dn = fmt.Sprintf("%s=%s,%s=%s,%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(ctx, u.PrimaryGroup), hierarchy, h.backend.BaseDN)
|
||||
dn = fmt.Sprintf("%s=%s,%s=%s,%s,%s", h.backend.NameFormatAsArray[0], u.Name, h.backend.GroupFormatAsArray[0], h.getGroupName(ctx, u.PrimaryGroup), hierarchy, h.backend.BaseDN)
|
||||
}
|
||||
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
|
||||
}
|
||||
@@ -280,8 +281,10 @@ func (h configHandler) FindPosixGroups(ctx context.Context, hierarchy string) (e
|
||||
|
||||
for _, g := range h.cfg.Groups {
|
||||
attrs := []*ldap.EntryAttribute{}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: h.backend.GroupFormat, Values: []string{g.Name}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "uid", Values: []string{g.Name}})
|
||||
for _, groupAttr := range h.backend.GroupFormatAsArray {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: groupAttr, Values: []string{g.Name}})
|
||||
}
|
||||
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "description", Values: []string{fmt.Sprintf("%s", g.Name)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "gidNumber", Values: []string{fmt.Sprintf("%d", g.GIDNumber)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "uniqueMember", Values: h.getGroupMemberDNs(ctx, g.GIDNumber)})
|
||||
@@ -291,7 +294,7 @@ func (h configHandler) FindPosixGroups(ctx context.Context, hierarchy string) (e
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberUid", Values: h.getGroupMemberIDs(ctx, g.GIDNumber)})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixGroup", "top"}})
|
||||
}
|
||||
dn := fmt.Sprintf("%s=%s,%s,%s", h.backend.GroupFormat, g.Name, hierarchy, h.backend.BaseDN)
|
||||
dn := fmt.Sprintf("%s=%s,%s,%s", h.backend.GroupFormatAsArray[0], g.Name, hierarchy, h.backend.BaseDN)
|
||||
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
|
||||
}
|
||||
|
||||
@@ -320,12 +323,12 @@ func (h configHandler) getGroupMemberDNs(ctx context.Context, 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,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(ctx, u.PrimaryGroup), insertOuUsers, h.backend.BaseDN)
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s%s,%s", h.backend.NameFormatAsArray[0], u.Name, h.backend.GroupFormatAsArray[0], h.getGroupName(ctx, u.PrimaryGroup), insertOuUsers, h.backend.BaseDN)
|
||||
members[dn] = true
|
||||
} else {
|
||||
for _, othergid := range u.OtherGroups {
|
||||
if othergid == gid {
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(ctx, u.PrimaryGroup), insertOuUsers, h.backend.BaseDN)
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s%s,%s", h.backend.NameFormatAsArray[0], u.Name, h.backend.GroupFormatAsArray[0], h.getGroupName(ctx, u.PrimaryGroup), insertOuUsers, h.backend.BaseDN)
|
||||
members[dn] = true
|
||||
}
|
||||
}
|
||||
@@ -408,7 +411,7 @@ func (h configHandler) getGroupDNs(ctx context.Context, gids []int) []string {
|
||||
for _, gid := range gids {
|
||||
for _, g := range h.cfg.Groups {
|
||||
if g.GIDNumber == gid {
|
||||
dn := fmt.Sprintf("%s=%s,ou=groups,%s", h.backend.GroupFormat, g.Name, h.backend.BaseDN)
|
||||
dn := fmt.Sprintf("%s=%s,ou=groups,%s", h.backend.GroupFormatAsArray[0], g.Name, h.backend.BaseDN)
|
||||
groups[dn] = true
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/glauth/glauth/v2/pkg/config"
|
||||
"github.com/glauth/ldap"
|
||||
)
|
||||
|
||||
type disabledBindTestMonitor struct{}
|
||||
|
||||
func (disabledBindTestMonitor) SetResponseTimeMetric(map[string]string, float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (disabledBindTestMonitor) SetLDAPMetric(map[string]string, float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestConfigBackendDisabledUserCannotBind(t *testing.T) {
|
||||
pw := "correct horse"
|
||||
raw, err := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.MinCost)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hexHash := hex.EncodeToString(raw)
|
||||
|
||||
cfg := &config.Config{}
|
||||
cfg.Users = []config.User{
|
||||
{Name: "activeuser", PrimaryGroup: 5501, PassBcrypt: hexHash, Disabled: false},
|
||||
{Name: "disableduser", PrimaryGroup: 5501, PassBcrypt: hexHash, Disabled: true},
|
||||
}
|
||||
|
||||
logger := zerolog.Nop()
|
||||
tracer := noop.NewTracerProvider().Tracer("f001")
|
||||
helper := NewLDAPOpsHelper(tracer)
|
||||
h := configHandler{
|
||||
backend: config.Backend{
|
||||
BaseDN: "dc=example,dc=com",
|
||||
NameFormatAsArray: []string{"cn"},
|
||||
GroupFormatAsArray: []string{"ou"},
|
||||
},
|
||||
log: &logger,
|
||||
cfg: cfg,
|
||||
ldohelper: helper,
|
||||
monitor: disabledBindTestMonitor{},
|
||||
tracer: tracer,
|
||||
}
|
||||
_, server := net.Pipe()
|
||||
defer server.Close()
|
||||
|
||||
bind := func(dn, pass string) ldap.LDAPResultCode {
|
||||
code, _ := h.Bind(dn, pass, server)
|
||||
return code
|
||||
}
|
||||
|
||||
if code := bind("cn=activeuser,dc=example,dc=com", pw); code != ldap.LDAPResultSuccess {
|
||||
t.Fatalf("active user with correct pw should bind, got %d", code)
|
||||
}
|
||||
if code := bind("cn=disableduser,dc=example,dc=com", pw); code != ldap.LDAPResultInvalidCredentials {
|
||||
t.Errorf("disabled user must be rejected; got code %d, want InvalidCredentials(49)", code)
|
||||
}
|
||||
}
|
||||
+30
-4
@@ -117,10 +117,12 @@ func (h ldapHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (result ld
|
||||
lowerBindDN := strings.ToLower(bindDN)
|
||||
baseDN := strings.ToLower("," + h.backend.BaseDN)
|
||||
parts := strings.Split(strings.TrimSuffix(lowerBindDN, baseDN), ",")
|
||||
userName := strings.TrimPrefix(parts[0], h.backend.NameFormat+"=")
|
||||
userName := strings.TrimPrefix(parts[0], h.backend.NameFormatAsArray[0]+"=")
|
||||
|
||||
validotp := false
|
||||
|
||||
// Local records augment proxy authentication with OTP/YubiKey checks;
|
||||
// password authentication remains delegated to the upstream LDAP server.
|
||||
// Find the user
|
||||
// We are going to go through all backends and ask
|
||||
// until we find our user or die of boredom.
|
||||
@@ -210,6 +212,20 @@ func (h ldapHandler) Search(boundDN string, searchReq ldap.SearchRequest, conn n
|
||||
stats.Frontend.Add("search_ldapSession_errors", 1)
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, nil
|
||||
}
|
||||
/*
|
||||
delete_idx := -1
|
||||
pagingSize := uint32(0)
|
||||
for idx, control := range searchReq.Controls {
|
||||
if control.GetControlType() == ldap.ControlTypePaging {
|
||||
fmt.Println(control.GetControlType())
|
||||
pagingSize = control.(*ldap.ControlPaging).PagingSize
|
||||
delete_idx = idx
|
||||
}
|
||||
}
|
||||
if delete_idx >= 0 {
|
||||
searchReq.Controls = append(searchReq.Controls[:delete_idx], searchReq.Controls[delete_idx+1:]...)
|
||||
}
|
||||
*/
|
||||
search := ldap.NewSearchRequest(
|
||||
searchReq.BaseDN,
|
||||
searchReq.Scope,
|
||||
@@ -223,6 +239,15 @@ func (h ldapHandler) Search(boundDN string, searchReq ldap.SearchRequest, conn n
|
||||
)
|
||||
|
||||
h.log.Debug().Interface("request", search).Msg("Search request to backend")
|
||||
/*
|
||||
var sr *ldap.SearchResult
|
||||
if pagingSize > 0 {
|
||||
fmt.Printf("Searching with page size == %d\n", pagingSize)
|
||||
sr, err = s.ldap.SearchWithPaging(search, pagingSize)
|
||||
} else {
|
||||
sr, err = s.ldap.Search(search)
|
||||
}
|
||||
*/
|
||||
sr, err := s.ldap.Search(search)
|
||||
h.log.Debug().Interface("result", sr).Msg("Backend Search result")
|
||||
|
||||
@@ -286,9 +311,10 @@ func (h ldapHandler) Search(boundDN string, searchReq ldap.SearchRequest, conn n
|
||||
}
|
||||
|
||||
ssr := ldap.ServerSearchResult{
|
||||
Entries: sr.Entries,
|
||||
Referrals: sr.Referrals,
|
||||
Controls: sr.Controls,
|
||||
Entries: sr.Entries,
|
||||
Referrals: sr.Referrals,
|
||||
Controls: sr.Controls,
|
||||
ResultCode: ldap.LDAPResultSuccess,
|
||||
}
|
||||
h.log.Debug().Interface("result", ssr).Msg("Frontend Search result")
|
||||
if err != nil {
|
||||
|
||||
@@ -65,6 +65,10 @@ func NewLDAPOpsHelper(tracer trace.Tracer) LDAPOpsHelper {
|
||||
return helper
|
||||
}
|
||||
|
||||
func isUnauthenticatedSimpleBind(bindDN, bindSimplePw string) bool {
|
||||
return bindDN != "" && bindSimplePw == ""
|
||||
}
|
||||
|
||||
func (l LDAPOpsHelper) Bind(ctx context.Context, h LDAPOpsHandler, bindDN, bindSimplePw string, conn net.Conn) (resultCode ldap.LDAPResultCode, err error) {
|
||||
ctx, span := l.tracer.Start(ctx, "handler.LDAPOpsHelper.Bind")
|
||||
defer span.End()
|
||||
@@ -86,11 +90,24 @@ func (l LDAPOpsHelper) Bind(ctx context.Context, h LDAPOpsHandler, bindDN, bindS
|
||||
return ldap.LDAPResultSuccess, nil
|
||||
}
|
||||
|
||||
// A non-empty DN with an empty password is LDAP's unauthenticated bind
|
||||
// mechanism. It must not establish the named user's authorization state.
|
||||
if isUnauthenticatedSimpleBind(bindDN, bindSimplePw) {
|
||||
h.GetLog().Info().Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("unauthenticated bind rejected")
|
||||
return ldap.LDAPResultUnwillingToPerform, nil
|
||||
}
|
||||
|
||||
user, ldapcode := l.findUser(ctx, h, bindDN, true /* checkGroup */)
|
||||
if ldapcode != ldap.LDAPResultSuccess {
|
||||
return ldapcode, nil
|
||||
}
|
||||
|
||||
// a disabled account must never authenticate, on any backend
|
||||
if user.Disabled {
|
||||
h.GetLog().Info().Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("Bind attempt on disabled account")
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
|
||||
validotp := false
|
||||
|
||||
if len(user.Yubikey) == 0 && len(user.OTPSecret) == 0 {
|
||||
@@ -175,6 +192,15 @@ func (l LDAPOpsHelper) Bind(ctx context.Context, h LDAPOpsHandler, bindDN, bindS
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
|
||||
// Reaching this point without a configured primary password verifier must
|
||||
// not be treated as successful authentication. App-password matches and
|
||||
// custom authentication return above when they succeed.
|
||||
if user.PassBcrypt == "" && user.PassSHA256 == "" {
|
||||
h.GetLog().Info().Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("invalid credentials")
|
||||
l.maybePutInTimeout(ctx, h, conn, true)
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
|
||||
// Now, check the pasword hash
|
||||
if user.PassBcrypt != "" {
|
||||
decoded, err := hex.DecodeString(user.PassBcrypt)
|
||||
@@ -359,6 +385,7 @@ func (l LDAPOpsHelper) searchMaybeRootDSEQuery(ctx context.Context, h LDAPOpsHan
|
||||
// unfortunately, objectClass is not to be included so we will respect that
|
||||
// attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"*"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "supportedSASLMechanisms", Values: []string{}})
|
||||
//attrs = append(attrs, &ldap.EntryAttribute{Name: "supportedSASLMechanisms", Values: []string{"GSSAPI", "PLAIN", "EXTERNAL"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "supportedLDAPVersion", Values: []string{"3"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "supportedControl", Values: []string{}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "supportedCapabilities", Values: []string{}})
|
||||
@@ -659,10 +686,10 @@ func (l LDAPOpsHelper) findUser(ctx context.Context, h LDAPOpsHandler, bindDN st
|
||||
groupName := ""
|
||||
userName := ""
|
||||
if len(parts) == 1 {
|
||||
userName = strings.TrimPrefix(parts[0], h.GetBackend().NameFormat+"=")
|
||||
userName = strings.TrimPrefix(parts[0], h.GetBackend().NameFormatAsArray[0]+"=")
|
||||
} else if len(parts) == 2 || (len(parts) == 3 && parts[2] == "ou=users") {
|
||||
userName = strings.TrimPrefix(parts[0], h.GetBackend().NameFormat+"=")
|
||||
groupName = strings.TrimPrefix(parts[1], h.GetBackend().GroupFormat+"=")
|
||||
userName = strings.TrimPrefix(parts[0], h.GetBackend().NameFormatAsArray[0]+"=")
|
||||
groupName = strings.TrimPrefix(parts[1], h.GetBackend().GroupFormatAsArray[0]+"=")
|
||||
} else {
|
||||
h.GetLog().Info().Str("binddn", bindDN).Int("numparts", len(parts)).Msg("BindDN should have only one or two parts")
|
||||
for _, part := range parts {
|
||||
@@ -702,6 +729,7 @@ func (l LDAPOpsHelper) findUser(ctx context.Context, h LDAPOpsHandler, bindDN st
|
||||
}
|
||||
|
||||
func (l LDAPOpsHelper) checkCapability(ctx context.Context, user config.User, action string, objects []string) bool {
|
||||
// User-level?
|
||||
for _, capability := range user.Capabilities {
|
||||
if capability.Action == action {
|
||||
for _, object := range objects {
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/GeertJohan/yubigo"
|
||||
"github.com/rs/zerolog"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/glauth/glauth/v2/pkg/config"
|
||||
"github.com/glauth/ldap"
|
||||
)
|
||||
|
||||
type bindTestHandler struct {
|
||||
backend config.Backend
|
||||
cfg config.Config
|
||||
log zerolog.Logger
|
||||
user config.User
|
||||
}
|
||||
|
||||
func (h *bindTestHandler) GetBackend() config.Backend { return h.backend }
|
||||
func (h *bindTestHandler) GetLog() *zerolog.Logger { return &h.log }
|
||||
func (h *bindTestHandler) GetCfg() *config.Config { return &h.cfg }
|
||||
func (h *bindTestHandler) GetYubikeyAuth() *yubigo.YubiAuth {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *bindTestHandler) FindUser(_ context.Context, userName string, _ bool) (bool, config.User, error) {
|
||||
if userName == h.user.Name {
|
||||
return true, h.user, nil
|
||||
}
|
||||
return false, config.User{}, nil
|
||||
}
|
||||
|
||||
func (h *bindTestHandler) FindGroup(context.Context, string) (bool, config.Group, error) {
|
||||
return false, config.Group{}, nil
|
||||
}
|
||||
|
||||
func (h *bindTestHandler) FindPosixAccounts(context.Context, string) ([]*ldap.Entry, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (h *bindTestHandler) FindPosixGroups(context.Context, string) ([]*ldap.Entry, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func sha256Password(password string) string {
|
||||
hash := sha256.Sum256([]byte(password))
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
func TestLDAPOpsHelperBindRejectsUnauthenticatedAndPasswordlessBinds(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
password string
|
||||
user config.User
|
||||
wantResult ldap.LDAPResultCode
|
||||
}{
|
||||
{
|
||||
name: "named empty password is unauthenticated bind",
|
||||
password: "",
|
||||
wantResult: ldap.LDAPResultUnwillingToPerform,
|
||||
},
|
||||
{
|
||||
name: "arbitrary password without verifier",
|
||||
password: "definitely-wrong",
|
||||
wantResult: ldap.LDAPResultInvalidCredentials,
|
||||
},
|
||||
{
|
||||
name: "failed app password does not fall through",
|
||||
user: config.User{
|
||||
Name: "nopass",
|
||||
PassAppSHA256: []string{sha256Password("expected")},
|
||||
},
|
||||
password: "definitely-wrong",
|
||||
wantResult: ldap.LDAPResultInvalidCredentials,
|
||||
},
|
||||
{
|
||||
name: "matching primary password succeeds",
|
||||
user: config.User{
|
||||
Name: "nopass",
|
||||
PassSHA256: sha256Password("correct"),
|
||||
},
|
||||
password: "correct",
|
||||
wantResult: ldap.LDAPResultSuccess,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
user := tt.user
|
||||
if user.Name == "" {
|
||||
user.Name = "nopass"
|
||||
}
|
||||
h := &bindTestHandler{
|
||||
backend: config.Backend{
|
||||
BaseDN: "dc=glauth,dc=com",
|
||||
NameFormatAsArray: []string{"cn"},
|
||||
GroupFormatAsArray: []string{"ou"},
|
||||
},
|
||||
user: user,
|
||||
log: zerolog.Nop(),
|
||||
}
|
||||
conn, peer := net.Pipe()
|
||||
defer conn.Close()
|
||||
defer peer.Close()
|
||||
|
||||
helper := NewLDAPOpsHelper(trace.NewNoopTracerProvider().Tracer("bind-test"))
|
||||
got, err := helper.Bind(context.Background(), h, "cn=nopass,dc=glauth,dc=com", tt.password, conn)
|
||||
if err != nil {
|
||||
t.Fatalf("Bind returned error: %v", err)
|
||||
}
|
||||
if got != tt.wantResult {
|
||||
t.Fatalf("Bind result = %v, want %v", got, tt.wantResult)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLDAPOpsHelperBindAllowsAnonymousBind(t *testing.T) {
|
||||
h := &bindTestHandler{
|
||||
backend: config.Backend{BaseDN: "dc=glauth,dc=com"},
|
||||
log: zerolog.Nop(),
|
||||
}
|
||||
conn, peer := net.Pipe()
|
||||
defer conn.Close()
|
||||
defer peer.Close()
|
||||
|
||||
helper := NewLDAPOpsHelper(trace.NewNoopTracerProvider().Tracer("bind-test"))
|
||||
got, err := helper.Bind(context.Background(), h, "", "", conn)
|
||||
if err != nil {
|
||||
t.Fatalf("Bind returned error: %v", err)
|
||||
}
|
||||
if got != ldap.LDAPResultSuccess {
|
||||
t.Fatalf("Bind result = %v, want %v", got, ldap.LDAPResultSuccess)
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,9 @@ func (h ownCloudHandler) Search(bindDN string, searchReq ldap.SearchRequest, con
|
||||
}
|
||||
for _, g := range groups {
|
||||
attrs := []*ldap.EntryAttribute{}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: h.backend.GroupFormat, Values: []string{*g.ID}})
|
||||
for _, groupAttr := range h.backend.GroupFormatAsArray {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: groupAttr, Values: []string{*g.ID}})
|
||||
}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "description", Values: []string{fmt.Sprintf("%s from ownCloud", *g.ID)}})
|
||||
// attrs = append(attrs, &ldap.EntryAttribute{"gidNumber", []string{fmt.Sprintf("%d", g.GIDNumber)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixGroup"}})
|
||||
@@ -179,7 +181,7 @@ func (h ownCloudHandler) Search(bindDN string, searchReq ldap.SearchRequest, con
|
||||
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberUid", Values: members})
|
||||
}
|
||||
dn := fmt.Sprintf("%s=%s,ou=groups,%s", h.backend.GroupFormat, *g.ID, h.backend.BaseDN)
|
||||
dn := fmt.Sprintf("%s=%s,ou=groups,%s", h.backend.GroupFormatAsArray[0], *g.ID, h.backend.BaseDN)
|
||||
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
|
||||
}
|
||||
case "posixaccount", "":
|
||||
@@ -197,8 +199,9 @@ func (h ownCloudHandler) Search(bindDN string, searchReq ldap.SearchRequest, con
|
||||
}
|
||||
for _, u := range users {
|
||||
attrs := []*ldap.EntryAttribute{}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: h.backend.NameFormat, Values: []string{*u.ID}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "uid", Values: []string{*u.ID}})
|
||||
for _, nameAttr := range h.backend.NameFormatAsArray {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: nameAttr, Values: []string{*u.ID}})
|
||||
}
|
||||
if u.DisplayName != nil {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "givenName", Values: []string{*u.DisplayName}})
|
||||
}
|
||||
@@ -209,7 +212,7 @@ func (h ownCloudHandler) Search(bindDN string, searchReq ldap.SearchRequest, con
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixAccount"}})
|
||||
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "description", Values: []string{fmt.Sprintf("%s from ownCloud", *u.ID)}})
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormat, *u.ID, h.backend.GroupFormat, "users", h.backend.BaseDN)
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormatAsArray[0], *u.ID, h.backend.GroupFormatAsArray[0], "users", h.backend.BaseDN)
|
||||
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func NewDatabaseHandler(sqlBackend SqlBackend, opts ...handler.Option) handler.H
|
||||
sqlBackend.CreateSchema(db)
|
||||
sqlBackend.MigrateSchema(db, ColumnExists)
|
||||
|
||||
options.Logger.Info().Msg("Database (" + sqlBackend.GetDriverName() + "::" + options.Backend.Database + ") Plugin: Ready")
|
||||
options.Logger.Debug().Msg("Database (" + sqlBackend.GetDriverName() + "::" + options.Backend.Database + ") Plugin: Ready")
|
||||
|
||||
return handler
|
||||
}
|
||||
@@ -454,13 +454,13 @@ func (h databaseHandler) getGroupMemberDNs(ctx context.Context, gid int) []strin
|
||||
return []string{}
|
||||
}
|
||||
if u.PrimaryGroup == gid {
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(ctx, u.PrimaryGroup), insertOuUsers, h.backend.BaseDN)
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s%s,%s", h.backend.NameFormatAsArray[0], u.Name, h.backend.GroupFormatAsArray[0], h.getGroupName(ctx, u.PrimaryGroup), insertOuUsers, h.backend.BaseDN)
|
||||
members[dn] = true
|
||||
} else {
|
||||
u.OtherGroups = h.commaListToIntTable(ctx, otherGroups)
|
||||
for _, othergid := range u.OtherGroups {
|
||||
if othergid == gid {
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(ctx, u.PrimaryGroup), insertOuUsers, h.backend.BaseDN)
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s%s,%s", h.backend.NameFormatAsArray[0], u.Name, h.backend.GroupFormatAsArray[0], h.getGroupName(ctx, u.PrimaryGroup), insertOuUsers, h.backend.BaseDN)
|
||||
members[dn] = true
|
||||
}
|
||||
}
|
||||
@@ -562,7 +562,7 @@ func (h databaseHandler) getGroupDNs(ctx context.Context, gids []int) []string {
|
||||
for _, gid := range gids {
|
||||
for _, g := range h.MemGroups {
|
||||
if g.GIDNumber == gid {
|
||||
dn := fmt.Sprintf("%s=%s,ou=groups,%s", h.backend.GroupFormat, g.Name, h.backend.BaseDN)
|
||||
dn := fmt.Sprintf("%s=%s,ou=groups,%s", h.backend.GroupFormatAsArray[0], g.Name, h.backend.BaseDN)
|
||||
groups[dn] = true
|
||||
}
|
||||
|
||||
@@ -609,7 +609,9 @@ func (h databaseHandler) getGroup(ctx context.Context, hierarchy string, g confi
|
||||
asGroupOfUniqueNames := hierarchy == "ou=groups"
|
||||
|
||||
attrs := []*ldap.EntryAttribute{}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: h.backend.GroupFormat, Values: []string{g.Name}})
|
||||
for _, groupAttr := range h.backend.GroupFormatAsArray {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: groupAttr, Values: []string{g.Name}})
|
||||
}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "description", Values: []string{fmt.Sprintf("%s via LDAP", g.Name)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "gidNumber", Values: []string{fmt.Sprintf("%d", g.GIDNumber)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "uniqueMember", Values: h.getGroupMemberDNs(ctx, g.GIDNumber)})
|
||||
@@ -619,7 +621,7 @@ func (h databaseHandler) getGroup(ctx context.Context, hierarchy string, g confi
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberUid", Values: h.getGroupMemberIDs(ctx, g.GIDNumber)})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixGroup", "top"}})
|
||||
}
|
||||
dn := fmt.Sprintf("%s=%s,ou=groups,%s", h.backend.GroupFormat, g.Name, h.backend.BaseDN)
|
||||
dn := fmt.Sprintf("%s=%s,ou=groups,%s", h.backend.GroupFormatAsArray[0], g.Name, h.backend.BaseDN)
|
||||
return &ldap.Entry{DN: dn, Attributes: attrs}
|
||||
}
|
||||
|
||||
@@ -628,8 +630,9 @@ func (h databaseHandler) getAccount(ctx context.Context, hierarchy string, u con
|
||||
defer span.End()
|
||||
|
||||
attrs := []*ldap.EntryAttribute{}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: h.backend.NameFormat, Values: []string{u.Name}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "uid", Values: []string{u.Name}})
|
||||
for _, nameAttr := range h.backend.NameFormatAsArray {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: nameAttr, Values: []string{u.Name}})
|
||||
}
|
||||
|
||||
if len(u.GivenName) > 0 {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "givenName", Values: []string{u.GivenName}})
|
||||
@@ -676,9 +679,9 @@ func (h databaseHandler) getAccount(ctx context.Context, hierarchy string, u con
|
||||
}
|
||||
var dn string
|
||||
if hierarchy == "" {
|
||||
dn = fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(ctx, u.PrimaryGroup), h.backend.BaseDN)
|
||||
dn = fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormatAsArray[0], u.Name, h.backend.GroupFormatAsArray[0], h.getGroupName(ctx, u.PrimaryGroup), h.backend.BaseDN)
|
||||
} else {
|
||||
dn = fmt.Sprintf("%s=%s,%s=%s,%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(ctx, u.PrimaryGroup), hierarchy, h.backend.BaseDN)
|
||||
dn = fmt.Sprintf("%s=%s,%s=%s,%s,%s", h.backend.NameFormatAsArray[0], u.Name, h.backend.GroupFormatAsArray[0], h.getGroupName(ctx, u.PrimaryGroup), hierarchy, h.backend.BaseDN)
|
||||
}
|
||||
return &ldap.Entry{DN: dn, Attributes: attrs}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
release-glauth-bogus:
|
||||
@echo Ignoring bogus plugin
|
||||
Submodule v2/pkg/plugins/glauth-mysql updated: 8379828f13...8e7354c160
Submodule v2/pkg/plugins/glauth-pam updated: 0e6e4240ef...cfe63488a5
Submodule v2/pkg/plugins/glauth-postgres updated: 61fa8ee872...013ae4c4de
Submodule v2/pkg/plugins/glauth-sqlite updated: e8b3b6a8b0...f34a4dea13
@@ -0,0 +1,13 @@
|
||||
//go:build !(embedsqlite || embedmysql)
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/glauth/glauth/v2/pkg/handler"
|
||||
)
|
||||
|
||||
func NewEmbed(opts ...handler.Option) (handler.Handler, error) {
|
||||
return nil, errors.New("GLAuth was not built with support for an embedded plugin")
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
//go:build embedsqlite
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/glauth/glauth/v2/pkg/embed"
|
||||
"github.com/glauth/glauth/v2/pkg/handler"
|
||||
)
|
||||
|
||||
func NewEmbed(opts ...handler.Option) (handler.Handler, error) {
|
||||
return embed.NewSQLiteHandler(opts...), nil
|
||||
}
|
||||
@@ -16,12 +16,13 @@ type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Logger zerolog.Logger
|
||||
Config *config.Config
|
||||
TLSConfig *tls.Config
|
||||
Monitor monitoring.MonitorInterface
|
||||
Tracer trace.Tracer
|
||||
Context context.Context
|
||||
Logger zerolog.Logger
|
||||
Config *config.Config
|
||||
StartTLSConfig *tls.Config
|
||||
LDAPSTLSConfig *tls.Config
|
||||
Monitor monitoring.MonitorInterface
|
||||
Tracer trace.Tracer
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
@@ -56,10 +57,17 @@ func Context(val context.Context) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// TLSConfig provides a function to set the TLS config option.
|
||||
func TLSConfig(val *tls.Config) Option {
|
||||
// TLSConfig provides a function to set the StartTLS config option.
|
||||
func StartTLSConfig(val *tls.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.TLSConfig = val
|
||||
o.StartTLSConfig = val
|
||||
}
|
||||
}
|
||||
|
||||
// TLSConfig provides a function to set the LDAPSTls config option.
|
||||
func LDAPSTLSConfig(val *tls.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.LDAPSTLSConfig = val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+41
-11
@@ -1,10 +1,13 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"plugin"
|
||||
|
||||
_tls "github.com/glauth/glauth/v2/internal/tls"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
@@ -20,9 +23,10 @@ type LdapSvc struct {
|
||||
yubiAuth *yubigo.YubiAuth
|
||||
l *ldap.Server
|
||||
|
||||
monitor monitoring.MonitorInterface
|
||||
tracer trace.Tracer
|
||||
log zerolog.Logger
|
||||
ldapstls *tls.Config
|
||||
monitor monitoring.MonitorInterface
|
||||
tracer trace.Tracer
|
||||
log zerolog.Logger
|
||||
}
|
||||
|
||||
func NewServer(opts ...Option) (*LdapSvc, error) {
|
||||
@@ -96,9 +100,22 @@ func NewServer(opts ...Option) (*LdapSvc, error) {
|
||||
s.l = ldap.NewServer()
|
||||
s.l.EnforceLDAP = true
|
||||
|
||||
if tlsConfig := options.TLSConfig; tlsConfig != nil {
|
||||
if tlsConfig := options.StartTLSConfig; tlsConfig != nil {
|
||||
s.l.TLSConfig = tlsConfig
|
||||
s.log.Info().Interface("tls.certificates", tlsConfig.Certificates).Msg("enabling LDAP over TLS")
|
||||
s.log.Info().
|
||||
Str("tls.min_version", tls.VersionName(tlsConfig.MinVersion)).
|
||||
Str("tls.max_version", tls.VersionName(tlsConfig.MaxVersion)).
|
||||
Interface("tls.cipher_suites", _tls.CipherSuiteNames(tlsConfig.CipherSuites)).
|
||||
Msg("enabling LDAP over TLS")
|
||||
}
|
||||
|
||||
if tlsConfig := options.LDAPSTLSConfig; tlsConfig != nil {
|
||||
s.ldapstls = tlsConfig
|
||||
s.log.Info().
|
||||
Str("tls.min_version", tls.VersionName(tlsConfig.MinVersion)).
|
||||
Str("tls.max_version", tls.VersionName(tlsConfig.MaxVersion)).
|
||||
Interface("tls.cipher_suites", _tls.CipherSuiteNames(tlsConfig.CipherSuites)).
|
||||
Msg("enabling LDAPS")
|
||||
}
|
||||
|
||||
for i, backend := range s.c.Backends {
|
||||
@@ -133,7 +150,7 @@ func NewServer(opts ...Option) (*LdapSvc, error) {
|
||||
case "plugin":
|
||||
plug, err := plugin.Open(backend.Plugin)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("Unable to load specified backend plugin: %s", err))
|
||||
return nil, fmt.Errorf("unable to load specified backend plugin: %s", err)
|
||||
}
|
||||
nph, err := plug.Lookup(backend.PluginHandler)
|
||||
if err != nil {
|
||||
@@ -155,6 +172,19 @@ func NewServer(opts ...Option) (*LdapSvc, error) {
|
||||
handler.Monitor(s.monitor),
|
||||
handler.Tracer(s.tracer),
|
||||
)
|
||||
case "embed":
|
||||
h, err = NewEmbed(
|
||||
handler.Backend(backend),
|
||||
handler.Logger(&s.log),
|
||||
handler.Config(s.c),
|
||||
handler.YubiAuth(s.yubiAuth),
|
||||
handler.LDAPHelper(loh),
|
||||
handler.Monitor(s.monitor),
|
||||
handler.Tracer(s.tracer),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported backend %s - must be one of 'config', 'ldap','owncloud' or 'plugin'", backend.Datastore)
|
||||
}
|
||||
@@ -186,11 +216,11 @@ func (s *LdapSvc) ListenAndServe() error {
|
||||
// ListenAndServeTLS listens on the TCP network address s.c.LDAPS.Listen
|
||||
func (s *LdapSvc) ListenAndServeTLS() error {
|
||||
s.log.Info().Str("address", s.c.LDAPS.Listen).Msg("LDAPS server listening")
|
||||
return s.l.ListenAndServeTLS(
|
||||
s.c.LDAPS.Listen,
|
||||
s.c.LDAPS.Cert,
|
||||
s.c.LDAPS.Key,
|
||||
)
|
||||
listener, err := tls.Listen("tcp", s.c.LDAPS.Listen, s.ldapstls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.l.Serve(listener)
|
||||
}
|
||||
|
||||
// Shutdown ends listeners by sending true to the ldap serves quit channel
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#################
|
||||
# glauth.conf
|
||||
|
||||
#################
|
||||
# General configuration.
|
||||
#debug = true
|
||||
syslog = true
|
||||
|
||||
#################
|
||||
# Server configuration.
|
||||
[ldap]
|
||||
enabled = true
|
||||
listen = "0.0.0.0:3893"
|
||||
|
||||
[ldaps]
|
||||
enabled = false
|
||||
listen = "0.0.0.0:636"
|
||||
cert = "glauth.crt"
|
||||
key = "glauth.key"
|
||||
|
||||
#################
|
||||
# The backend section controls the data store.
|
||||
[[backends]]
|
||||
datastore = "ldap"
|
||||
servers = [ "ldap://192.168.1.222:389" ]
|
||||
|
||||
[[backends]]
|
||||
datastore = "config"
|
||||
|
||||
[[users]]
|
||||
name = "otpuser"
|
||||
otpsecret = "3hnvnk4ycv44glzigd6s25j4dougs3rk"
|
||||
|
||||
#################
|
||||
# Enable and configure the optional REST API here.
|
||||
[api]
|
||||
enabled = false
|
||||
tls = true
|
||||
listen = "localhost:5555"
|
||||
cert = "glauth.crt"
|
||||
key = "glauth.key"
|
||||
|
||||
+17
-7
@@ -3,7 +3,7 @@
|
||||
|
||||
#################
|
||||
# General configuration.
|
||||
debug = true
|
||||
debug = false
|
||||
# syslog = true
|
||||
# structuredlog = true
|
||||
#
|
||||
@@ -28,7 +28,7 @@ debug = true
|
||||
[ldaps]
|
||||
# to enable ldaps generate a certificate, eg. with:
|
||||
# openssl req -x509 -newkey rsa:4096 -keyout glauth.key -out glauth.crt -days 365 -nodes -subj '/CN=`hostname`'
|
||||
enabled = false
|
||||
enabled = true
|
||||
listen = "0.0.0.0:3894"
|
||||
cert = "glauth.crt"
|
||||
key = "glauth.key"
|
||||
@@ -48,12 +48,10 @@ debug = true
|
||||
[backend]
|
||||
datastore = "config"
|
||||
baseDN = "dc=glauth,dc=com"
|
||||
nameformat = "cn"
|
||||
groupformat = "ou"
|
||||
|
||||
# If you are using a client that requires reading the root DSE first
|
||||
# such as SSSD
|
||||
# anonymousdse = true
|
||||
anonymousdse = false
|
||||
|
||||
## Configure dn format to use structures like
|
||||
## "uid=serviceuser,cn=svcaccts,$BASEDN" instead of "cn=serviceuser,ou=svcaccts,$BASEDN"
|
||||
@@ -147,6 +145,11 @@ debug = true
|
||||
# passappbcrypt = "243261243130244B62463462656F7265504F762E794F324957746D656541326B4B46596275674A79336A476845764B616D65446169784E41384F4432" # dogood
|
||||
otpsecret = "3hnvnk4ycv44glzigd6s25j4dougs3rk"
|
||||
|
||||
[[users]]
|
||||
name = "danger"
|
||||
uidnumber = 5006
|
||||
primarygroup = 5503
|
||||
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
|
||||
|
||||
#################
|
||||
# The groups section contains a hardcoded list of valid users.
|
||||
@@ -159,9 +162,16 @@ debug = true
|
||||
gidnumber = 5502
|
||||
|
||||
[[groups]]
|
||||
name = "vpn"
|
||||
name = "umbrella"
|
||||
gidnumber = 5503
|
||||
includegroups = [ 5501 ]
|
||||
includegroups = [ 5504 ]
|
||||
|
||||
[[groups]]
|
||||
name = "danger"
|
||||
gidnumber = 5504
|
||||
[[groups.capabilities]]
|
||||
action = "search"
|
||||
object = "dc=glauth,dc=com"
|
||||
|
||||
#################
|
||||
# Enable and configure the optional REST API here.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
dn: ou=superheros,ou=users,dc=glauth,dc=com
|
||||
ou: superheros
|
||||
uid: superheros
|
||||
cn: superheros
|
||||
description: superheros
|
||||
gidNumber: 5501
|
||||
uniqueMember: cn=alexdoe,ou=superheros,ou=users,dc=glauth,dc=com
|
||||
@@ -20,7 +20,7 @@ objectClass: top
|
||||
|
||||
dn: ou=svcaccts,ou=users,dc=glauth,dc=com
|
||||
ou: svcaccts
|
||||
uid: svcaccts
|
||||
cn: svcaccts
|
||||
description: svcaccts
|
||||
gidNumber: 5502
|
||||
uniqueMember: cn=serviceuser,ou=svcaccts,ou=users,dc=glauth,dc=com
|
||||
@@ -30,7 +30,7 @@ objectClass: top
|
||||
|
||||
dn: ou=vpnaccess,ou=users,dc=glauth,dc=com
|
||||
ou: vpnaccess
|
||||
uid: vpnaccess
|
||||
cn: vpnaccess
|
||||
description: vpnaccess
|
||||
gidNumber: 5503
|
||||
uniqueMember: cn=alexdoe,ou=superheros,ou=users,dc=glauth,dc=com
|
||||
@@ -50,7 +50,7 @@ objectClass: top
|
||||
|
||||
dn: ou=allaccs,ou=users,dc=glauth,dc=com
|
||||
ou: allaccs
|
||||
uid: allaccs
|
||||
cn: allaccs
|
||||
description: allaccs
|
||||
gidNumber: 5504
|
||||
uniqueMember: cn=alexdoe,ou=superheros,ou=users,dc=glauth,dc=com
|
||||
@@ -72,7 +72,7 @@ objectClass: top
|
||||
|
||||
dn: ou=mailadmin,ou=users,dc=glauth,dc=com
|
||||
ou: mailadmin
|
||||
uid: mailadmin
|
||||
cn: mailadmin
|
||||
description: mailadmin
|
||||
gidNumber: 5505
|
||||
uniqueMember: cn=alexdoe,ou=superheros,ou=users,dc=glauth,dc=com
|
||||
@@ -88,7 +88,7 @@ objectClass: top
|
||||
|
||||
dn: ou=webmail,ou=users,dc=glauth,dc=com
|
||||
ou: webmail
|
||||
uid: webmail
|
||||
cn: webmail
|
||||
description: webmail
|
||||
gidNumber: 5506
|
||||
objectClass: posixGroup
|
||||
@@ -96,7 +96,7 @@ objectClass: top
|
||||
|
||||
dn: ou=fulltime,ou=users,dc=glauth,dc=com
|
||||
ou: fulltime
|
||||
uid: fulltime
|
||||
cn: fulltime
|
||||
description: fulltime
|
||||
gidNumber: 5507
|
||||
uniqueMember: cn=alexdoe,ou=superheros,ou=users,dc=glauth,dc=com
|
||||
|
||||
@@ -24,6 +24,22 @@ echo "Fixing CI_BUILD_DIR"
|
||||
|
||||
# This script requires that "$CI_BUILD_DIR" is set to the repo root
|
||||
|
||||
GOOS="$(go env GOOS)"
|
||||
GOARCH="$(go env GOARCH)"
|
||||
if [[ "$GOARCH" = "arm" ]]; then
|
||||
DIST_ARCH="arm-$(go env GOARM)"
|
||||
else
|
||||
DIST_ARCH="$GOARCH"
|
||||
fi
|
||||
GLAUTH_BINARY="$CI_BUILD_DIR/bin/$GOOS$GOARCH/glauth"
|
||||
if [[ ! -x "$GLAUTH_BINARY" ]]; then
|
||||
GLAUTH_BINARY="$CI_BUILD_DIR/bin/glauth-$GOOS-$DIST_ARCH"
|
||||
fi
|
||||
if [[ ! -x "$GLAUTH_BINARY" ]]; then
|
||||
echo "Unable to find a runnable GLAuth binary: $GLAUTH_BINARY"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure ldap utils are installed (for example - when running this outside of CI)
|
||||
if [[ ! `which ldapsearch` ]]; then
|
||||
sudo apt-get -qq update && sudo apt-get -qq install -y ldap-utils || exit 1;
|
||||
@@ -33,14 +49,14 @@ fi
|
||||
echo "";
|
||||
echo ""
|
||||
echo "Version string of tested binary:"
|
||||
"$CI_BUILD_DIR/bin/linuxamd64/glauth" --version
|
||||
"$GLAUTH_BINARY" --version
|
||||
echo ""
|
||||
|
||||
# Start in background, capture PID
|
||||
"$CI_BUILD_DIR/bin/linuxamd64/glauth" -c "$CI_BUILD_DIR/scripts/ci/test-config.cfg" &> /dev/null &
|
||||
"$GLAUTH_BINARY" -c "$CI_BUILD_DIR/scripts/ci/test-config.cfg" &> /dev/null &
|
||||
|
||||
# Use this instead to see glauth logs while running
|
||||
# "$CI_BUILD_DIR/bin/linuxamd64/glauth" -c "$CI_BUILD_DIR/scripts/ci/test-config.cfg" &
|
||||
# "$CI_BUILD_DIR/bin/$(go env GOOS)$(go env GOARCH)/glauth" -c "$CI_BUILD_DIR/scripts/ci/test-config.cfg" &
|
||||
glauthPid="$!"
|
||||
|
||||
echo "Running glauth at PID=$glauthPid"
|
||||
|
||||
Reference in New Issue
Block a user