mirror of
https://github.com/glauth/glauth.git
synced 2026-10-27 06:26:59 +00:00
Compare commits
186 Commits
v2.1.0-RC1
..
v2.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
| eea7bfbb4f | |||
| b498736cfd | |||
| f8dee8da87 | |||
| c4a020a4f5 | |||
| e92008680c | |||
| 8f972db4cf | |||
| e9a4a5623a | |||
| 8e042199a2 | |||
| c0ed90822f | |||
| a5ecc26e06 | |||
| a90f37b23b | |||
| 8476506b8c | |||
| 262b5061ca | |||
| a2c151b0d0 | |||
| 42c448f3c7 | |||
| ed52a91ec4 | |||
| 6d161b7033 | |||
| 5eafb3bc15 | |||
| bc29d321bf | |||
| 540631cc9d | |||
| 3c3ac7e148 | |||
| 14bb205f90 | |||
| 10613f37df | |||
| 56ccb881b5 | |||
| 837da62607 | |||
| 0bf3d4a82a | |||
| f5bb80e024 | |||
| 1a37396109 | |||
| d178281474 | |||
| a0d1069841 | |||
| 67d77324a3 | |||
| 01bdb7148e | |||
| f692394942 | |||
| 46e49b6976 | |||
| 52a0adcbcf | |||
| 2ca5312696 | |||
| 6ee78cd881 | |||
| 1c2b23c00f | |||
| d05630f66b | |||
| 0cf04037a2 | |||
| 7174aba5ab | |||
| 5ddbd4f610 | |||
| 0b6cfcb1b4 | |||
| f3342fa709 | |||
| 545188e5f2 | |||
| c90cbfe5fb | |||
| 1721e5701d | |||
| ebdeae7073 | |||
| 94fe62cfe1 | |||
| f9addbc2c5 | |||
| 5fe8aca852 | |||
| 24455e3988 | |||
| 3ba8e11132 | |||
| accf15410c | |||
| 8739fd6e4f | |||
| 97118ff186 | |||
| 3943e7dc21 | |||
| 114b36f677 | |||
| 0904a24564 | |||
| 498cad48b1 | |||
| 19fa5045ee | |||
| 08a89a9861 | |||
| a4450d4558 | |||
| 0b7910c03c | |||
| 944d602077 | |||
| 596d28efbd | |||
| 6a58f35b14 | |||
| 7f925565a6 | |||
| f5ea3d93bf | |||
| 34a6b30b8f | |||
| 9bbad511cb | |||
| 7cef07931c | |||
| 37fa74892e | |||
| efbd64504d | |||
| 1920c4324f | |||
| 7e7a5f59d1 | |||
| 83f4305e36 | |||
| b45259d567 | |||
| 3f9f67375f | |||
| cb761e8e70 | |||
| 7c3314a504 | |||
| 690917e4e0 | |||
| 0ccf4926ff | |||
| 598a688214 | |||
| 0e7769ff84 | |||
| 6500bbaf36 | |||
| e92197ac08 | |||
| 65385fb82d | |||
| aadf150fec | |||
| 122c86c446 | |||
| 0d03b226b6 | |||
| f41b5ee19e | |||
| aad166b3ec | |||
| 6b14714eb4 | |||
| 3db5c5e54a | |||
| 1a78b51174 | |||
| 9865de68ac | |||
| 32750d2abd | |||
| 021324c775 | |||
| edaddca4e7 | |||
| 1af3cf4f69 | |||
| 3e2e24cadb | |||
| 249388fdd1 | |||
| 45651645e7 | |||
| 42ff05f141 | |||
| 598d746ee8 | |||
| 6800da8bf8 | |||
| 57ee724556 | |||
| ea4cd57233 | |||
| 5d79fd9b03 | |||
| 385cc335ca | |||
| 851c21df04 | |||
| ceefc66dbb | |||
| e26219c2f4 | |||
| 2222a791e4 | |||
| eef3a5c6a0 | |||
| 1299a6d002 | |||
| 95ac9708ed | |||
| 363cd76fa2 | |||
| 82bc471735 | |||
| e1e586f464 | |||
| 31fbd790e8 | |||
| ef3151c287 | |||
| de3d1cec1c | |||
| 7faac6df69 | |||
| e86d6000d9 | |||
| c7098406d0 | |||
| 911e933020 | |||
| 53eddd67c9 | |||
| c2360259af | |||
| 7e49d25591 | |||
| 7267d7b129 | |||
| 0e8937c61e | |||
| fd85683ff0 | |||
| 2b45aefd74 | |||
| 05c537dabd | |||
| 8c8bc06c3b | |||
| fed91e4aef | |||
| 7be05404e1 | |||
| af9062f1a1 | |||
| aed137909a | |||
| 846f0509d6 | |||
| 06afbdcee5 | |||
| 2e0f7f7335 | |||
| b07cd1f256 | |||
| 2ccb8ce02e | |||
| 5dfbbb2f4b | |||
| 3a30656c18 | |||
| 3f53bb983a | |||
| 3b7a548389 | |||
| 0ec6f15957 | |||
| 8223029345 | |||
| 7c7934aea3 | |||
| 9c9294485b | |||
| 4cf38893ac | |||
| 674a6bdbf4 | |||
| 7b082dccb8 | |||
| 10b0191818 | |||
| 53ddd3c4f4 | |||
| 2065fc39f4 | |||
| e2724eadc2 | |||
| 9c197cc35e | |||
| 25a1648706 | |||
| cbb84b8641 | |||
| 2eb2215ae4 | |||
| 225f1b441b | |||
| bf5ddba2be | |||
| fcf784ecf0 | |||
| 07763f52e6 | |||
| 262465d63b | |||
| f28f1a115b | |||
| f4ceb0c3d4 | |||
| 9fb9c87625 | |||
| faed82ad44 | |||
| fd3ac7e4bb | |||
| 4f029234b2 | |||
| d6ba7d0cbc | |||
| 4d42af8aac | |||
| fda48f5524 | |||
| b7cfc11f5c | |||
| bbb381a68e | |||
| 28136302b7 | |||
| 4e600736f2 | |||
| 2d4f5d5476 | |||
| 29cf021694 | |||
| 592fdf4e79 |
@@ -0,0 +1,54 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/go
|
||||
{
|
||||
"name": "glauth",
|
||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||
"image": "mcr.microsoft.com/devcontainers/go:0-1.19-bullseye",
|
||||
|
||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||
"features": {
|
||||
"docker-in-docker": {
|
||||
"version": "latest",
|
||||
"moby": true
|
||||
},
|
||||
"ghcr.io/rocker-org/devcontainer-features/apt-packages:1": {
|
||||
"packages": "oathtool,ldap-utils"
|
||||
}
|
||||
},
|
||||
|
||||
// Configure tool-specific properties.
|
||||
"customizations": {
|
||||
// Configure properties specific to VS Code.
|
||||
"vscode": {
|
||||
"settings": {},
|
||||
"extensions": [
|
||||
"golang.go",
|
||||
"ms-azuretools.vscode-docker"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"containerEnv": {
|
||||
"REPO": "glauth"
|
||||
},
|
||||
|
||||
"capAdd": ["SYS_PTRACE"],
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [9000],
|
||||
|
||||
// Use 'portsAttributes' to set default properties for specific forwarded ports.
|
||||
// More info: https://containers.dev/implementors/json_reference/#port-attributes
|
||||
"portsAttributes": {
|
||||
"3893": {
|
||||
"label": "LDAP",
|
||||
"onAutoForward": "notify"
|
||||
},
|
||||
"5555": {
|
||||
"label": "Web UI",
|
||||
"onAutoForward": "openBrowser"
|
||||
}
|
||||
},
|
||||
|
||||
"postCreateCommand": "cd v2 && make setup"
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
# \todo Eliminate duplication when/if Dependabot supports YAML anchors
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
labels:
|
||||
- dependencies
|
||||
- actions
|
||||
schedule:
|
||||
day: sunday
|
||||
interval: weekly
|
||||
- package-ecosystem: gomod
|
||||
directory: /v2
|
||||
labels:
|
||||
- dependencies
|
||||
- go
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: friday
|
||||
@@ -4,24 +4,24 @@ on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- master
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.16.x, 1.17.x]
|
||||
go-version: [1.21.x]
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Updating packages database
|
||||
run: sudo apt-get -qq update
|
||||
- name: Install CLI Dependencies
|
||||
run: sudo apt-get install -y ldap-utils oathtool
|
||||
run: sudo apt-get install -y ldap-utils oathtool libpam0g-dev
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: Fetch Go dependencies
|
||||
working-directory: ./v2
|
||||
run: go get -t -v ./...
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
run: diff -u <(echo -n) <(gofmt -d ./)
|
||||
- name: Full compile test
|
||||
working-directory: ./v2
|
||||
run: make all
|
||||
run: make fast
|
||||
- name: Output hashes to the build log
|
||||
working-directory: ./v2
|
||||
run: cat bin/*/glauth.sha256
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: "46 1 * * 3"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ javascript ]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-and-quality
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
@@ -0,0 +1,22 @@
|
||||
# This workflow was added by CodeSee. Learn more at https://codesee.io/
|
||||
# This is v2.0 of this workflow file
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
name: CodeSee
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
codesee:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
name: Analyze the repo with CodeSee
|
||||
steps:
|
||||
- uses: Codesee-io/codesee-action@v2
|
||||
with:
|
||||
codesee-token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }}
|
||||
@@ -1,44 +0,0 @@
|
||||
name: docker
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
- edited
|
||||
|
||||
jobs:
|
||||
build-push:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
env:
|
||||
BUILDX_VERSION: v0.4.1
|
||||
DOCKER_CLI_EXPERIMENTAL: enabled
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: set up multiarch builder
|
||||
working-directory: ./v2
|
||||
run: |
|
||||
set -ex
|
||||
curl -sSL https://get.docker.com | bash
|
||||
mkdir -p ~/.docker/cli-plugins
|
||||
curl -sSLo ~/.docker/cli-plugins/buildx https://github.com/docker/buildx/releases/download/$BUILDX_VERSION/buildx-$BUILDX_VERSION.linux-amd64
|
||||
chmod +x ~/.docker/cli-plugins/buildx
|
||||
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
docker buildx create --name multiarch
|
||||
docker buildx use multiarch
|
||||
docker buildx inspect --bootstrap
|
||||
curl -sSL https://gist.githubusercontent.com/ArturKlauser/0f0293c62f5626df0261ac994d8a46af/raw/33ef25b8a93f15310f0a4013deca3f8d20fc00a7/check-qemu-binfmt.sh | bash
|
||||
- name: log in github registry
|
||||
run: docker login --username "${{ github.actor }}" --password "${{ secrets.GITHUB_TOKEN }}" docker.pkg.github.com
|
||||
- name: build multiarch images
|
||||
working-directory: ./v2
|
||||
run: docker buildx build -f Dockerfile-standalone -t docker.pkg.github.com/$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]'):$(echo ${{ github.ref }} | tr / -) --platform linux/amd64,linux/arm64,linux/arm/v7 .
|
||||
- name: push multiarch images
|
||||
working-directory: ./v2
|
||||
if: github.event.release.tag_name
|
||||
run: docker buildx build --push -f Dockerfile-standalone -t docker.pkg.github.com/$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]'):${{ github.event.release.tag_name }} --platform linux/amd64,linux/arm64,linux/arm/v7 .
|
||||
@@ -0,0 +1,27 @@
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
name: release-please
|
||||
|
||||
jobs:
|
||||
release-please:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: google-github-actions/release-please-action@v3
|
||||
with:
|
||||
release-type: go
|
||||
package-name: GLAuth
|
||||
path: v2
|
||||
default-branch: master
|
||||
pull-request-title-pattern: "ci: release ${version}"
|
||||
token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
|
||||
extra-files: |
|
||||
v2/internal/version/const.go
|
||||
+18
-15
@@ -1,18 +1,21 @@
|
||||
# Ignore default config file for testing
|
||||
glauth.cfg
|
||||
|
||||
# Ignore the built binary during testing
|
||||
glauth
|
||||
|
||||
# Ignore generated certs in this folder
|
||||
certs/
|
||||
|
||||
# Built binaries
|
||||
bin
|
||||
|
||||
.*.swp
|
||||
v2/certs/
|
||||
v2/bin
|
||||
v2/docker/assets/linux
|
||||
v2/gl.db
|
||||
v2/buildlogs
|
||||
.vscode/
|
||||
|
||||
certs/
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
### Go Patch ###
|
||||
/vendor/
|
||||
/Godeps/
|
||||
|
||||
# Various
|
||||
go.work.sum
|
||||
|
||||
# gomocks auto-generated
|
||||
**/mock_*.go
|
||||
|
||||
# Local files
|
||||
local/
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
[submodule "v2/pkg/plugins/glauth-sqlite"]
|
||||
path = v2/pkg/plugins/glauth-sqlite
|
||||
url = git@github.com:glauth/glauth-sqlite
|
||||
[submodule "v2/pkg/plugins/glauth-mysql"]
|
||||
path = v2/pkg/plugins/glauth-mysql
|
||||
url = git@github.com:glauth/glauth-mysql
|
||||
[submodule "v2/pkg/plugins/glauth-postgres"]
|
||||
path = v2/pkg/plugins/glauth-postgres
|
||||
url = git@github.com:glauth/glauth-postgres
|
||||
[submodule "v2/pkg/plugins/glauth-pam"]
|
||||
path = v2/pkg/plugins/glauth-pam
|
||||
url = git@github.com:glauth/glauth-pam
|
||||
@@ -1,5 +0,0 @@
|
||||
# Contributor Code of Conduct
|
||||
|
||||
This project adheres to No Code of Conduct. We are all adults. We accept anyone's contributions. Nothing else matters.
|
||||
|
||||
For more information please visit the [No Code of Conduct](https://github.com/domgetter/NCoC) homepage.
|
||||
@@ -1,64 +0,0 @@
|
||||
#################
|
||||
# Build Step
|
||||
#################
|
||||
|
||||
FROM golang:buster as build
|
||||
LABEL maintainers="Ben Yanke <ben@benyanke.com>, Jörn Friedrich Dreyer <jfd@butonic.de>, Chris F Ravenscroft <chris@voilaweb.com>"
|
||||
|
||||
# Setup work env
|
||||
RUN mkdir /app /tmp/gocode
|
||||
ADD . /app/
|
||||
WORKDIR /app
|
||||
|
||||
# Required envs for GO
|
||||
ENV GOPATH=/tmp/gocode
|
||||
ENV GOOS=linux
|
||||
ENV GOARCH=amd64
|
||||
ENV GO_ENABLED=1
|
||||
|
||||
# Only needed for alpine builds // also: busybox
|
||||
RUN apt -qq update && apt -qq install -y git make build-essential busybox-static dumb-init
|
||||
|
||||
# Install deps
|
||||
RUN go get -d -v ./...
|
||||
|
||||
# Build and copy final result
|
||||
RUN uname -a
|
||||
RUN uname -m
|
||||
RUN bash -c '[[ $(uname -m) == x86_64 ]] && { make linux64 && cp ./bin/glauth64 /app/glauth; export PLUGIN_OS=linux PLUGIN_ARCH=amd64; make bin/sqlite.so bin/postgres.so bin/mysql.so && cp ./bin/*.so /app/; } || { true; }'
|
||||
RUN bash -c '[[ $(uname -m) == aarch64 ]] && { make linuxarm64 && cp ./bin/glauth-arm64 /app/glauth; export PLUGIN_OS=linux PLUGIN_ARCH=arm64; make bin/sqlite.so bin/postgres.so bin/mysql.so && cp ./bin/*.so /app/; } || { true; }'
|
||||
|
||||
# Check glauth works
|
||||
RUN /app/glauth --version
|
||||
|
||||
#################
|
||||
# Run Step
|
||||
#################
|
||||
|
||||
FROM gcr.io/distroless/base-debian10 as run
|
||||
LABEL maintainers="Ben Yanke <ben@benyanke.com>, Jörn Friedrich Dreyer <jfd@butonic.de>, Chris F Ravenscroft <chris@voilaweb.com>"
|
||||
|
||||
# Copy binary from build container
|
||||
COPY --from=build /app/glauth /app/glauth
|
||||
COPY --from=build /app/*.so /app/
|
||||
|
||||
# Copy docker specific scripts from build container
|
||||
COPY --from=build /app/scripts/docker/start-plugins.sh /app/docker/
|
||||
COPY --from=build /app/scripts/docker/default-config-plugins.cfg /app/docker/
|
||||
COPY --from=build /app/scripts/docker/gl.db /app/docker/
|
||||
|
||||
# Just what we need
|
||||
COPY --from=build /usr/bin/dumb-init /usr/bin/dumb-init
|
||||
COPY --from=build /bin/busybox /bin/sh
|
||||
COPY --from=build /bin/busybox /bin/ln
|
||||
COPY --from=build /bin/busybox /bin/rm
|
||||
# Debug env: COPY --from=build /bin/busybox /bin/busybox
|
||||
RUN ln /bin/sh /usr/bin/cp && ln /bin/sh /usr/bin/mkdir && rm /bin/ln /bin/rm
|
||||
|
||||
# Install init
|
||||
|
||||
# Expose web and LDAP ports
|
||||
EXPOSE 389 636 5555
|
||||
|
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
||||
CMD ["/bin/sh", "/app/docker/start-plugins.sh"]
|
||||
@@ -1,61 +0,0 @@
|
||||
#################
|
||||
# Build Step
|
||||
#################
|
||||
|
||||
FROM golang:alpine as build
|
||||
LABEL maintainers="Ben Yanke <ben@benyanke.com>, Jörn Friedrich Dreyer <jfd@butonic.de>, Chris F Ravenscroft <chris@voilaweb.com>"
|
||||
|
||||
# Setup work env
|
||||
RUN mkdir /app /tmp/gocode
|
||||
ADD . /app/
|
||||
WORKDIR /app
|
||||
|
||||
# Required envs for GO
|
||||
ENV GOPATH=/tmp/gocode
|
||||
ENV GOOS=linux
|
||||
ENV GOARCH=amd64
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
# Only needed for alpine builds // also: busybox
|
||||
RUN apk add --no-cache git make busybox-static dumb-init
|
||||
|
||||
# Install deps
|
||||
RUN go get -d -v ./...
|
||||
|
||||
# Build and copy final result -- wrong file BTW
|
||||
RUN uname -a
|
||||
RUN if [ $(uname -m) == x86_64 ]; then make linux64 && cp ./bin/glauth64 /app/glauth; fi
|
||||
RUN if [ $(uname -m) == aarch64 ]; then make linuxarm64 && cp ./bin/glauth-arm64 /app/glauth; fi
|
||||
RUN if [ $(uname -m) == armv7l ]; then make linuxarm32 && cp ./bin/glauth-arm32 /app/glauth; fi
|
||||
|
||||
# Check glauth works
|
||||
RUN /app/glauth --version
|
||||
|
||||
#################
|
||||
# Run Step
|
||||
#################
|
||||
|
||||
FROM gcr.io/distroless/base-debian10 as run
|
||||
LABEL maintainers="Ben Yanke <ben@benyanke.com>, Jörn Friedrich Dreyer <jfd@butonic.de>, Chris F Ravenscroft <chris@voilaweb.com>"
|
||||
|
||||
# Copy binary from build container
|
||||
COPY --from=build /app/glauth /app/glauth
|
||||
|
||||
# Copy docker specific scripts from build container
|
||||
COPY --from=build /app/scripts/docker/start-standalone.sh /app/docker/
|
||||
COPY --from=build /app/scripts/docker/default-config-standalone.cfg /app/docker/
|
||||
|
||||
# Just what we need
|
||||
COPY --from=build /usr/bin/dumb-init /usr/bin/dumb-init
|
||||
COPY --from=build /bin/busybox.static /bin/sh
|
||||
COPY --from=build /bin/busybox.static /bin/ln
|
||||
COPY --from=build /bin/busybox.static /bin/rm
|
||||
RUN ln /bin/sh /usr/bin/cp && ln /bin/sh /usr/bin/mkdir && rm /bin/ln /bin/rm
|
||||
|
||||
# Install init
|
||||
|
||||
# Expose web and LDAP ports
|
||||
EXPOSE 389 636 5555
|
||||
|
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
||||
CMD ["/bin/sh", "/app/docker/start-standalone.sh"]
|
||||
@@ -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,95 +0,0 @@
|
||||
# This is the V1 Makefile.
|
||||
# If you need to build a V1 target, remove the next line.
|
||||
exit 0
|
||||
|
||||
GIT_COMMIT=$(shell git rev-list -1 HEAD )
|
||||
BUILD_TIME=$(shell date -u +%Y%m%d_%H%M%SZ)
|
||||
GIT_CLEAN=$(shell git status | grep -E "working (tree|directory) clean" | wc -l | sed 's/^[ ]*//')
|
||||
|
||||
# Last git tag
|
||||
LAST_GIT_TAG=$(shell git describe --abbrev=0 --tags 2> /dev/null)
|
||||
|
||||
# this=1 if the current commit is the tagged commit (ie, if this is a release build)
|
||||
GIT_IS_TAG_COMMIT=$(shell git describe --abbrev=0 --tags > /dev/null 2> /dev/null && echo "1" || echo "0")
|
||||
|
||||
# Used when a tag isn't available
|
||||
GIT_BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
|
||||
|
||||
# Build variables
|
||||
BUILD_VARS=-s -w -X main.GitCommit=${GIT_COMMIT} -X main.GitBranch=${GIT_BRANCH} -X main.BuildTime=${BUILD_TIME} -X main.GitClean=${GIT_CLEAN} -X main.LastGitTag=${LAST_GIT_TAG} -X main.GitTagIsCommit=${GIT_IS_TAG_COMMIT}
|
||||
BUILD_FILES=glauth.go
|
||||
TRIM_FLAGS=-gcflags "all=-trimpath=${PWD}" -asmflags "all=-trimpath=${PWD}"
|
||||
|
||||
# Plugins
|
||||
include pkg/plugins/Makefile
|
||||
|
||||
#####################
|
||||
# High level commands
|
||||
#####################
|
||||
|
||||
# Build and run - used for development
|
||||
run: setup devrun
|
||||
|
||||
# Run the integration test on linux64 (eventually allow the binary to be set)
|
||||
test: runtest
|
||||
|
||||
# Run build process for all binaries
|
||||
all: setup binaries verify
|
||||
|
||||
# Run build process for only linux64
|
||||
fast: setup linux64 verify
|
||||
|
||||
# list of binary formats to build
|
||||
binaries: linux32 linux64 linuxarm32 linuxarm64 darwin64 win32 win64
|
||||
|
||||
# Setup commands to always run
|
||||
setup: getdeps format
|
||||
|
||||
#####################
|
||||
# Subcommands
|
||||
#####################
|
||||
|
||||
# Run integration test
|
||||
runtest:
|
||||
./scripts/ci/integration-test.sh cleanup
|
||||
|
||||
# Get all dependencies
|
||||
getdeps:
|
||||
go get -d ./...
|
||||
|
||||
updatetest:
|
||||
./scripts/ci/integration-test.sh
|
||||
|
||||
format:
|
||||
go fmt
|
||||
|
||||
devrun:
|
||||
go run ${BUILD_FILES} -c sample-simple.cfg
|
||||
|
||||
linux32:
|
||||
GOOS=linux GOARCH=386 go build ${TRIM_FLAGS} -ldflags "${BUILD_VARS}" -o bin/glauth32 ${BUILD_FILES} && cd bin && sha256sum glauth32 > glauth32.sha256
|
||||
|
||||
linux64:
|
||||
GOOS=linux GOARCH=amd64 go build ${TRIM_FLAGS} -ldflags "${BUILD_VARS}" -o bin/glauth64 ${BUILD_FILES} && cd bin && sha256sum glauth64 > glauth64.sha256
|
||||
|
||||
linuxarm32:
|
||||
GOOS=linux GOARCH=arm go build ${TRIM_FLAGS} -ldflags "${BUILD_VARS}" -o bin/glauth-arm32 ${BUILD_FILES} && cd bin && sha256sum glauth-arm32 > glauth-arm32.sha256
|
||||
|
||||
linuxarm64:
|
||||
GOOS=linux GOARCH=arm64 go build ${TRIM_FLAGS} -ldflags "${BUILD_VARS}" -o bin/glauth-arm64 ${BUILD_FILES} && cd bin && sha256sum glauth-arm64 > glauth-arm64.sha256
|
||||
|
||||
darwin64:
|
||||
GOOS=darwin GOARCH=amd64 go build ${TRIM_FLAGS} -ldflags "${BUILD_VARS}" -o bin/glauthOSX ${BUILD_FILES} && cd bin && sha256sum glauthOSX > glauthOSX.sha256
|
||||
|
||||
darwinarm64:
|
||||
GOOS=darwin GOARCH=arm64 go build ${TRIM_FLAGS} -ldflags "${BUILD_VARS}" -o bin/glauthOSX-arm64 ${BUILD_FILES} && cd bin && sha256sum glauthOSX-arm64 > glauthOSX-arm64.sha256
|
||||
|
||||
win32:
|
||||
GOOS=windows GOARCH=386 go build ${TRIM_FLAGS} -ldflags "${BUILD_VARS}" -o bin/glauth-win32 ${BUILD_FILES} && cd bin && sha256sum glauth-win32 > glauth-win32.sha256
|
||||
|
||||
win64:
|
||||
GOOS=windows GOARCH=amd64 go build ${TRIM_FLAGS} -ldflags "${BUILD_VARS}" -o bin/glauth-win64 ${BUILD_FILES} && cd bin && sha256sum glauth-win64 > glauth-win64.sha256
|
||||
|
||||
|
||||
verify:
|
||||
cd bin && sha256sum *.sha256 -c && cd ../;
|
||||
@@ -7,9 +7,7 @@ Go-lang LDAP Authentication (GLAuth) is a secure, easy-to-use, LDAP server w/ co
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
* Centrally manage accounts across your infrastructure
|
||||
@@ -29,7 +27,7 @@ Use it to centralize account management across your Linux servers, your OSX mach
|
||||
This quickstart is a great way to try out GLAuth in a non-production environment. *Be warned that you should take the extra steps to setup SSL (TLS) for production use!*
|
||||
|
||||
1. Download a precompiled binary from the [releases](https://github.com/glauth/glauth/releases) page.
|
||||
2. Download the [example config file](https://github.com/glauth/glauth/blob/master/sample-simple.cfg).
|
||||
2. Download the [example config file](https://github.com/glauth/glauth/blob/master/v2/sample-simple.cfg).
|
||||
3. Start the GLAuth server, referencing the path to the desired config file with `-c`.
|
||||
- `./glauth64 -c sample-simple.cfg`
|
||||
4. Test with traditional LDAP tools
|
||||
@@ -39,15 +37,15 @@ This quickstart is a great way to try out GLAuth in a non-production environment
|
||||
|
||||
Note - makefile uses git data to inject build-time variables. For best results, run in the context of the git repo.
|
||||
|
||||
*make all* - run build binaries for platforms
|
||||
### Documentation
|
||||
|
||||
*make fast* - run build for only linux 64 bit
|
||||
<h4 align="center">:point_right: The latest version of GLauth's documentation is available at https://glauth.github.io/ :point_left:</h4>
|
||||
|
||||
*make run* - wrapper for the 'go run' command, setting up the needed tooling
|
||||
<hr>
|
||||
|
||||
*make plugins* - build additional (SQL) plugin backends
|
||||
### Quickstart
|
||||
|
||||
*make test* - run the integration test on linux64 binary
|
||||
Get started in three short [steps](https://glauth.github.io/docs/quickstart.html)
|
||||
|
||||
### Usage:
|
||||
```
|
||||
@@ -96,250 +94,20 @@ Here's a sample config wth hardcoded users and groups:
|
||||
name = "superheros"
|
||||
gidnumber = 5501
|
||||
```
|
||||
To create the password SHA hash, use this command: `echo -n "mysecret" | openssl dgst -sha256`
|
||||
|
||||
Instead of a local configuration file, GLAuth can fetch its configuration from S3. This is an easy way to ensure redundant GLAuth servers are always in-sync.
|
||||
```unix
|
||||
glauth -c s3://bucketname/glauth.cfg
|
||||
```
|
||||
In order to use S3, you must set your AWS credentials. Either:
|
||||
|
||||
1. set the -K and -S command-line flags **OR**
|
||||
2. set the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables.
|
||||
|
||||
More configuration options are documented here: https://github.com/glauth/glauth/blob/master/sample-simple.cfg
|
||||
|
||||
### Chaining backends
|
||||
|
||||
This can be used, for instance, to inject support for Two Factor Authentication for backends that do not support the feature natively:
|
||||
|
||||
```
|
||||
[[backends]]
|
||||
datastore = "ldap"
|
||||
servers = ["ldap:s//localhost:390"]
|
||||
|
||||
[[backends]]
|
||||
datastore = "config"
|
||||
|
||||
...
|
||||
|
||||
[[users]]
|
||||
name = "hackers"
|
||||
otpsecret = "................"
|
||||
```
|
||||
|
||||
|
||||
### Required Fields
|
||||
* Name
|
||||
* The user's username
|
||||
* ou
|
||||
* ID of the user's primary group
|
||||
* uidnumber
|
||||
* The user's unix user id
|
||||
* sshPublicKey
|
||||
* Specify an array of public keys
|
||||
|
||||
### Optional Fields
|
||||
* otherGroups
|
||||
* Array of IDs of groups the user is a member of.
|
||||
* Example: [5501, 5002]
|
||||
* default = blank
|
||||
* givenname
|
||||
* First name
|
||||
* Example: John
|
||||
* default = blank
|
||||
* sn
|
||||
* Last name
|
||||
* Example: Doe
|
||||
* default = blank
|
||||
* disabled
|
||||
* Specify if account is active.
|
||||
* Set to 'true' (without quotes) to make the LDAP entry add 'AccountStatus = inactive'
|
||||
* default = false (active)
|
||||
* mail
|
||||
* Specify an email
|
||||
* example: jdoe@example.com
|
||||
* default = blank
|
||||
* loginshell
|
||||
* Specify a different login shell for the user
|
||||
* Example: /bin/sh, or /sbin/nologin
|
||||
* default = /bin/bash
|
||||
* homedirectory
|
||||
* Specify an overridden home directory for the user
|
||||
* Example: /home/itadmin
|
||||
* default = /home/[username]
|
||||
* otpsecret
|
||||
* Specify OTP secret used to validate OTP passcode
|
||||
* Example: 3hnvnk4ycv44glzigd6s25j4dougs3rk
|
||||
* default = blank
|
||||
* passappbcrypt
|
||||
* Specify an array of app passwords which can also succesfully bind - these bypass the OTP check. Hash the same way as password.
|
||||
* Example: ["c32255dbf6fd6b64883ec8801f793bccfa2a860f2b1ae1315cd95cdac1338efa","4939efa7c87095dacb5e7e8b8cfb3a660fa1f5edcc9108f6d7ec20ea4d6b3a88"]
|
||||
* default = blank
|
||||
* passappsha256
|
||||
* Specify an array of app passwords which can also succesfully bind - these bypass the OTP check. Hash the same way as password.
|
||||
* Example: ["c32255dbf6fd6b64883ec8801f793bccfa2a860f2b1ae1315cd95cdac1338efa","4939efa7c87095dacb5e7e8b8cfb3a660fa1f5edcc9108f6d7ec20ea4d6b3a88"]
|
||||
* default = blank
|
||||
* yubikey
|
||||
* Specify Yubikey ID for maching Yubikey OTP against the user
|
||||
* Example: cccjgjgkhcbb
|
||||
* default = blank
|
||||
|
||||
### Capabilities
|
||||
|
||||
Introduced in 2.1.0, this feature continues improving the intrinsic security model of GLAuth.
|
||||
|
||||
While some level of access control is already enforced when using an LDAP backend, Capabilities are now part of the Config and Database backends.
|
||||
|
||||
Currently, one capability is recognized: "search" -- here is how to configure it in a Config yaml file:
|
||||
|
||||
```
|
||||
...
|
||||
[behaviors]
|
||||
# Ignore all capabilities restrictions, for instance allowing every user to perform a search
|
||||
IgnoreCapabilities = false
|
||||
...
|
||||
[[users]]
|
||||
name = "hackers"
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "ou=superheros,dc=glauth,dc=com"
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "ou=someotherdn,dc=glauth,dc=com"
|
||||
...
|
||||
[[users]]
|
||||
name = "serviceuser"
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "*"
|
||||
...
|
||||
```
|
||||
For backward compatibility, you can set `IgnoreCapabilities` to "true"
|
||||
|
||||
If you are using a Database backend, check the plugins README for configuration information.
|
||||
|
||||
### OpenSSH keys:
|
||||
GLAuth can store a user's SSH authorized keys. Add one or more keys per user as shown above, then setup the goklp helper: https://github.com/appliedtrust/goklp
|
||||
|
||||
### Strong Passwords
|
||||
If you are currently using sha256 passwords (`passsha256` or `passappsha256`) moving to strong, salted paswords is recommended. Simply switch to `passbcrypt` and/or `passappbcrypt` password types. Currently (2021) 2<sup>12</sup> is a reasonably good value, depending our your server's CPU.
|
||||
|
||||
### Two Factor Authentication
|
||||
GLAuth can be configured to accept OTP tokens as appended to a users password. Support is added for both **TOTP tokens** (often known by it's most prominent implementation, "Google Authenticator") and **Yubikey OTP tokens**.
|
||||
|
||||
When using 2FA, append the 2FA code to the end of the password when authenticating. For example, if your password is "monkey" and your otp is "123456", enter "monkey123456" as your password.
|
||||
|
||||
#### TOTP Configuration
|
||||
To enable TOTP authentication on a user, you can use a tool [like this](https://freeotp.github.io/qrcode.html) to generate a QR code (pick 'Timeout' and optionally let it generate a random secret for you), which can be scanned and used with the [Google Authenticator](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en) app. To enable TOTP authentication, configure the `otpsecret` for the user with the TOTP secret.
|
||||
|
||||
#### App Passwords
|
||||
Additionally, you can specify an array of password hashes using the `passappsha256` for app passwords. These are not OTP validated, and are hashed in the same way as a password. This allows you to generate a long random string to be used in software which requires the ability to authenticate.
|
||||
|
||||
However, app passwords can be used without OTP as well.
|
||||
|
||||
#### Yubikey Configuration
|
||||
For Yubikey OTP token authentication, first [configure your Yubikey](https://www.yubico.com/products/services-software/personalization-tools/yubikey-otp/). After this, make sure to [request a `Client ID` and `Secret key` pair](https://upgrade.yubico.com/getapikey/).
|
||||
|
||||
Now configure the `yubikeyclientid` and `yubikeysecret` fields in the general section in the configuration file.
|
||||
|
||||
To enable Yubikey OTP authentication for a user, you must specify their Yubikey ID on the users `yubikey` field. The Yubikey ID is the first 12 characters of the Yubikey OTP, as explained in the below chart.
|
||||
|
||||

|
||||
|
||||
When a user has been configured with either one of the OTP options, the OTP authentication is required for the user. If both are configured, either one will work.
|
||||
More configuration options are documented [here](https://glauth.github.io/docs/file.html) and in this [sample file](https://github.com/glauth/glauth/blob/master/v2/sample-simple.cfg)
|
||||
|
||||
### Backends:
|
||||
For advanced users, GLAuth supports pluggable backends. Currently, it can use a local file, S3 or an existing LDAP infrastructure. In the future, we hope to have backends that support Mongo, SQL, and other datastores.
|
||||
|
||||
For advanced users, GLAuth supports pluggable backends. Currently, it can use a local file, S3 or an existing LDAP infrastructure. Through the use of optional plugins, you can connect SQL databases, PAM, and other datastores.
|
||||
|
||||
```toml
|
||||
[backend]
|
||||
datastore = "ldap"
|
||||
servers = [ "ldaps://server1:636", "ldaps://server2:636" ]
|
||||
```
|
||||
|
||||
### Production:
|
||||
Any of the architectures above will work for production. Just remember:
|
||||
|
||||
* Always use legit SSL certs for production!
|
||||
|
||||
# Other Architectures
|
||||
A small note about other architectures: while I expect the code is, for the most part, system-independent, there is not a good (and free) CI system which can be easily used to continuously test releases on ARM, BSD, Linux-32bit, and Windows. As such, all of the non-linux-64bit packages are provided as is. The extent of testing on these packages consists solely of cross-compiling for these architectures from a linux 64 bit system.
|
||||
|
||||
We will accept PRs which fix bugs on these platforms, but be aware these binaries will not be tested regularly, and instead are provided for the convenience of those who feel comfortable with this.
|
||||
|
||||
### Building:
|
||||
|
||||
# Logging
|
||||
- using logr with increasing verbosity
|
||||
- 0 you always want to see this
|
||||
- 1 common logging that you might *possibly* want to turn off (error)
|
||||
- 2 warn
|
||||
- 3 notice
|
||||
- 4 info
|
||||
- 6 debug
|
||||
- 8 trace
|
||||
- 10 I would like to performance test your log collection stack
|
||||
- errors really are errors that cannot be handled or returned
|
||||
- returning a proper LDAP error code is handling an error
|
||||
|
||||
# Testing
|
||||
|
||||
Of course, a core set of tests is being run by Github Actions CI. However, when developing new features/refactoring, a more comprehensive regression testing suite is needed.
|
||||
|
||||
You can run `go test` to execute the tests found in `glauth_test.go` -- better, if it is installed, you can run [goconvey](https://github.com/smartystreets/goconvey)
|
||||
|
||||
Since some tests cover TOTP, you will first need to install `oathtool` in your environment.
|
||||
|
||||
You also must create a symbolink link called `glauth` to make it easy for the test framework to find the executable. For instance:
|
||||
|
||||
```
|
||||
cd bin
|
||||
ln -s glauth64 glauth
|
||||
```
|
||||
|
||||
In order to test GLAuth against an LDAP backend, you will need docker. Run this command:
|
||||
```
|
||||
docker run \
|
||||
--rm \
|
||||
-d \
|
||||
-p 389:389 \
|
||||
--name openldap-service \
|
||||
--hostname ldap-service \
|
||||
--env LDAP_ORGANISATION="GLauth" \
|
||||
--env LDAP_DOMAIN="glauth.com" \
|
||||
--env LDAP_ADMIN_PASSWORD="password" \
|
||||
--env LDAP_CONFIG_PASSWORD="password" \
|
||||
--env LDAP_BASE_DN="dc=glauth,dc=com" \
|
||||
-v $PWD/misc/openldap/config:/etc/ldap/slapd.d \
|
||||
-v $PWD/misc/openldap/db:/var/lib/ldap \
|
||||
osixia/openldap:latest
|
||||
```
|
||||
|
||||
Refer to [this page](https://github.com/glauth/glauth/wiki/Quick-openldap-setup-to-test-LDAP-backend) for a somewhat more in-depth overview of testing with OpenLDAP.
|
||||
|
||||
# Compatibility
|
||||
|
||||
While our stated goal for GLAuth is to provide the simplest possible authentication server, we keep finding an increasing number of client appliances that are asking fairly "existential" questions of the server. We have been working on providing answers these clients will find satisfactory.
|
||||
|
||||
### Root DSE
|
||||
|
||||
RFC 4512: "An LDAP server SHALL provide information about itself and other information that is specific to each server. This is represented as a group of attributes located in the root DSE"
|
||||
|
||||
Test: `ldapsearch -LLL -H ldap://localhost:3893 -D cn=serviceuser,ou=svcaccts,dc=glauth,dc=com -w mysecret -x -s base "(objectclass=*)"`
|
||||
|
||||
### Subschema Discovery
|
||||
|
||||
RFC 4512: "To read schema attributes from the subschema (sub)entry, clients MUST issue a Search operation [RFC4511] where baseObject is the DN of the subschema (sub)entry..."
|
||||
|
||||
Test: `ldapsearch -LLL -o ldif-wrap=no -H ldap://localhost:3893 -D cn=serviceuser,ou=svcaccts,dc=glauth,dc=com -w mysecret -x -bcn=schema -s base`
|
||||
|
||||
By default, this query will return a very minimal schema (~5 objects) -- you can ask GLAuth to return more comprehensive schemas by unpacking, in the `schema/` directory, the OpenLDAP or FreeIPA schema archives found in the `assets/` directory.
|
||||
|
||||
### LDAP Backend: "1.1" attribute
|
||||
|
||||
RFC 4511: "A list containing only the OID "1.1" indicates that no attributes are to be returned."
|
||||
|
||||
## Stargazers over time
|
||||
# Stargazers over time
|
||||
|
||||
[](https://starchart.cc/glauth/glauth)
|
||||
|
||||
|
||||
@@ -1,810 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/GeertJohan/yubigo"
|
||||
"github.com/arl/statsviz"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
docopt "github.com/docopt/docopt-go"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/glauth/glauth/pkg/config"
|
||||
"github.com/glauth/glauth/pkg/frontend"
|
||||
gologgingr "github.com/glauth/glauth/pkg/gologgingr"
|
||||
"github.com/glauth/glauth/pkg/server"
|
||||
"github.com/glauth/glauth/pkg/stats"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/hydronica/toml"
|
||||
"github.com/jinzhu/copier"
|
||||
logging "github.com/op/go-logging"
|
||||
"gopkg.in/amz.v3/aws"
|
||||
"gopkg.in/amz.v3/s3"
|
||||
)
|
||||
|
||||
// Set with buildtime vars
|
||||
var LastGitTag string
|
||||
var BuildTime string
|
||||
var GitCommit string
|
||||
var GitClean string
|
||||
var GitBranch string
|
||||
var GitTagIsCommit string
|
||||
|
||||
const programName = "glauth"
|
||||
|
||||
var usage = `glauth: securely expose your LDAP for external auth
|
||||
|
||||
Usage:
|
||||
glauth [options] -c <file|s3 url>
|
||||
glauth -h --help
|
||||
glauth --version
|
||||
|
||||
Options:
|
||||
-c, --config <file> Config file.
|
||||
-K <aws_key_id> AWS Key ID.
|
||||
-S <aws_secret_key> AWS Secret Key.
|
||||
-r <aws_region> AWS Region [default: us-east-1].
|
||||
--ldap <address> Listen address for the LDAP server.
|
||||
--ldaps <address> Listen address for the LDAPS server.
|
||||
--ldaps-cert <cert-file> Path to cert file for the LDAPS server.
|
||||
--ldaps-key <key-file> Path to key file for the LDAPS server.
|
||||
-h, --help Show this screen.
|
||||
--version Show version.
|
||||
`
|
||||
|
||||
var (
|
||||
log logr.Logger
|
||||
args map[string]interface{}
|
||||
stderr *logging.LogBackend
|
||||
yubiAuth *yubigo.YubiAuth
|
||||
|
||||
activeConfig = &config.Config{}
|
||||
)
|
||||
|
||||
// Reads builtime vars and returns a full string containing info about
|
||||
// the currently running version of the software. Primarily used by the
|
||||
// --version flag at runtime.
|
||||
func getVersionString() string {
|
||||
|
||||
var versionstr string
|
||||
|
||||
versionstr = "GLauth"
|
||||
|
||||
// Notate the git context of the build
|
||||
switch {
|
||||
// If a release, use the tag
|
||||
case GitClean == "1" && GitTagIsCommit == "1":
|
||||
versionstr += " " + LastGitTag + "\n\n"
|
||||
|
||||
// If this branch had a tag before, mention the branch and the tag to give a rough idea of the base version
|
||||
case len(GitBranch) > 1 && len(LastGitTag) > 1:
|
||||
versionstr += "\nNon-release build from branch " + GitBranch + ", based on tag " + LastGitTag + "\n\n"
|
||||
|
||||
// If no previous tag specified, just mention the branch
|
||||
case len(GitBranch) > 1:
|
||||
versionstr += "\nNon-release build from branch " + GitBranch + "\n\n"
|
||||
|
||||
// Fallback message, if all else fails
|
||||
default:
|
||||
versionstr += "\nNon-release build\n\n"
|
||||
}
|
||||
|
||||
// Include build time
|
||||
if len(BuildTime) > 1 {
|
||||
versionstr += "Build time: " + BuildTime + "\n"
|
||||
}
|
||||
|
||||
// Add commit hash
|
||||
if GitClean == "1" && len(GitCommit) > 1 {
|
||||
versionstr += "Commit: " + GitCommit + "\n"
|
||||
}
|
||||
|
||||
return versionstr
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
stderr = initLogging()
|
||||
log.V(6).Info("AP start")
|
||||
|
||||
if err := parseArgs(); err != nil {
|
||||
log.Error(err, "Could not parse command-line arguments")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := doConfig(); err != nil {
|
||||
log.Error(err, "Configuration file error")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
startService()
|
||||
}
|
||||
|
||||
func startService() {
|
||||
// stats
|
||||
stats.General.Set("version", stats.Stringer(LastGitTag))
|
||||
|
||||
// web API
|
||||
if activeConfig.API.Enabled {
|
||||
log.V(6).Info("Web API enabled")
|
||||
|
||||
if activeConfig.API.Internals {
|
||||
statsviz.Register(
|
||||
http.DefaultServeMux,
|
||||
statsviz.Root("/internals"),
|
||||
statsviz.SendFrequency(1000*time.Millisecond),
|
||||
)
|
||||
}
|
||||
|
||||
go frontend.RunAPI(
|
||||
frontend.Logger(log),
|
||||
frontend.Config(&activeConfig.API),
|
||||
)
|
||||
}
|
||||
|
||||
startConfigWatcher()
|
||||
|
||||
s, err := server.NewServer(
|
||||
server.Logger(log),
|
||||
server.Config(activeConfig),
|
||||
)
|
||||
if err != nil {
|
||||
log.Error(err, "Could not create server")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if activeConfig.LDAP.Enabled {
|
||||
// Don't block if also starting a LDAPS server afterwards
|
||||
shouldBlock := !activeConfig.LDAPS.Enabled
|
||||
|
||||
if shouldBlock {
|
||||
if err := s.ListenAndServe(); err != nil {
|
||||
log.Error(err, "Could not start LDAP server")
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
go func() {
|
||||
if err := s.ListenAndServe(); err != nil {
|
||||
log.Error(err, "Could not start LDAP server")
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
if activeConfig.LDAPS.Enabled {
|
||||
// Always block here
|
||||
if err := s.ListenAndServeTLS(); err != nil {
|
||||
log.Error(err, "Could not start LDAPS server")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
log.V(0).Info("AP exit")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func startConfigWatcher() {
|
||||
configFileLocation := getConfigLocation()
|
||||
if !activeConfig.WatchConfig || strings.HasPrefix(configFileLocation, "s3://") {
|
||||
return
|
||||
}
|
||||
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
log.Error(err, "Could not start config-watcher")
|
||||
return
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
go func() {
|
||||
isChanged, isRemoved := false, false
|
||||
for {
|
||||
select {
|
||||
case event := <-watcher.Events:
|
||||
log.V(6).Info("watcher got event", "e", event.Op.String())
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
isChanged = true
|
||||
} else if event.Op&fsnotify.Remove == fsnotify.Remove { // vim edit file with rename/remove
|
||||
isChanged, isRemoved = true, true
|
||||
}
|
||||
case err := <-watcher.Errors:
|
||||
log.Error(err, "Error!")
|
||||
case <-ticker.C:
|
||||
// wakeup, try finding removed config
|
||||
}
|
||||
if _, err := os.Stat(configFileLocation); !os.IsNotExist(err) && (isRemoved || isChanged) {
|
||||
if isRemoved {
|
||||
log.V(6).Info("rewatching config", "file", configFileLocation)
|
||||
watcher.Add(configFileLocation) // overwrite
|
||||
isChanged, isRemoved = true, false
|
||||
}
|
||||
if isChanged {
|
||||
if err := doConfig(); err != nil {
|
||||
log.V(2).Info("Could not reload config. Holding on to old config", "error", err.Error())
|
||||
} else {
|
||||
log.V(3).Info("Config was reloaded")
|
||||
}
|
||||
isChanged = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
watcher.Add(configFileLocation)
|
||||
}
|
||||
|
||||
func parseArgs() error {
|
||||
var err error
|
||||
|
||||
if args, err = docopt.Parse(usage, nil, true, getVersionString(), false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getConfigLocation() string {
|
||||
return args["--config"].(string)
|
||||
}
|
||||
|
||||
func parseConfigFile(configFileLocation string) (*config.Config, error) {
|
||||
cfg := config.Config{}
|
||||
// setup defaults
|
||||
cfg.LDAP.Enabled = false
|
||||
cfg.LDAPS.Enabled = true
|
||||
|
||||
// parse the config file
|
||||
if strings.HasPrefix(configFileLocation, "s3://") {
|
||||
if _, present := aws.Regions[args["-r"].(string)]; present == false {
|
||||
return &cfg, fmt.Errorf("Invalid AWS region: %s", args["-r"])
|
||||
}
|
||||
region := aws.Regions[args["-r"].(string)]
|
||||
auth, err := aws.EnvAuth()
|
||||
if err != nil {
|
||||
if args["-K"] == nil || args["-S"] == nil {
|
||||
return &cfg, fmt.Errorf("AWS credentials not found: must use -K and -S flags, or set these env vars:\n\texport AWS_ACCESS_KEY_ID=\"AAA...\"\n\texport AWS_SECRET_ACCESS_KEY=\"BBBB...\"\n")
|
||||
}
|
||||
auth = aws.Auth{
|
||||
AccessKey: args["-K"].(string),
|
||||
SecretKey: args["-S"].(string),
|
||||
}
|
||||
}
|
||||
// parse S3 url
|
||||
s3url := strings.TrimPrefix(configFileLocation, "s3://")
|
||||
parts := strings.SplitN(s3url, "/", 2)
|
||||
if len(parts) != 2 {
|
||||
return &cfg, fmt.Errorf("Invalid S3 URL: %s", s3url)
|
||||
}
|
||||
b, err := s3.New(auth, region).Bucket(parts[0])
|
||||
if err != nil {
|
||||
return &cfg, err
|
||||
}
|
||||
tomlData, err := b.Get(parts[1])
|
||||
if err != nil {
|
||||
return &cfg, err
|
||||
}
|
||||
if _, err := toml.Decode(string(tomlData), &cfg); err != nil {
|
||||
return &cfg, err
|
||||
}
|
||||
} else { // local config file
|
||||
fInfo, err := os.Stat(configFileLocation)
|
||||
if err != nil {
|
||||
return &cfg, fmt.Errorf("Non-existent config path: %s", configFileLocation)
|
||||
}
|
||||
if fInfo.IsDir() { // multiple files in a directory
|
||||
rawCfgStruct := make(map[string]interface{})
|
||||
|
||||
// To keep things simple, we are not going to use the default values built in Cfg
|
||||
// so far (LDAP.Enabled, LDAPS.Enabled, etc) so do not forget to specify them!
|
||||
/*
|
||||
sourcebuf := new(bytes.Buffer)
|
||||
err = toml.NewEncoder(sourcebuf).Encode(cfg)
|
||||
if err != nil {
|
||||
return &cfg, err
|
||||
}
|
||||
var initialRawCfgStruct interface{}
|
||||
if err := toml.Unmarshal(sourcebuf.Bytes(), &initialRawCfgStruct); err != nil {
|
||||
return &cfg, err
|
||||
}
|
||||
if err = mergeConfigs(&rawCfgStruct, initialRawCfgStruct); err != nil {
|
||||
return &cfg, err
|
||||
}
|
||||
*/
|
||||
|
||||
files, _ := ioutil.ReadDir(configFileLocation)
|
||||
for _, f := range files {
|
||||
canonicalName := filepath.Join(configFileLocation, f.Name())
|
||||
|
||||
bs, _ := ioutil.ReadFile(canonicalName)
|
||||
var curRawCfgStruct interface{}
|
||||
if err := toml.Unmarshal(bs, &curRawCfgStruct); err != nil {
|
||||
return &cfg, err
|
||||
}
|
||||
if err = mergeConfigs(&rawCfgStruct, curRawCfgStruct); err != nil {
|
||||
return &cfg, err
|
||||
}
|
||||
}
|
||||
|
||||
destbuf := new(bytes.Buffer)
|
||||
err = toml.NewEncoder(destbuf).Encode(rawCfgStruct)
|
||||
if err != nil {
|
||||
return &cfg, err
|
||||
}
|
||||
fmt.Println(destbuf.String())
|
||||
merged := config.Config{}
|
||||
if _, err := toml.Decode(destbuf.String(), &merged); err != nil {
|
||||
return &cfg, err
|
||||
}
|
||||
return &merged, nil
|
||||
|
||||
} else {
|
||||
if _, err := toml.DecodeFile(configFileLocation, &cfg); err != nil {
|
||||
return &cfg, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Backward Compability
|
||||
if cfg.Backend.Datastore != "" {
|
||||
if cfg.Backends != nil {
|
||||
return &cfg, fmt.Errorf("You cannot specify both [Backend] and [[Backends]] directives in the same configuration ")
|
||||
} else {
|
||||
cfg.Backends = append(cfg.Backends, cfg.Backend)
|
||||
}
|
||||
}
|
||||
|
||||
// Patch with default values where not specified
|
||||
for i := range cfg.Backends {
|
||||
if cfg.Backends[i].NameFormat == "" {
|
||||
cfg.Backends[i].NameFormat = "cn"
|
||||
}
|
||||
if cfg.Backends[i].GroupFormat == "" {
|
||||
cfg.Backends[i].GroupFormat = "ou"
|
||||
}
|
||||
if cfg.Backends[i].SSHKeyAttr == "" {
|
||||
cfg.Backends[i].SSHKeyAttr = "sshPublicKey"
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
func mergeConfigs(config1 interface{}, config2 interface{}) error {
|
||||
var merger func(int, string, interface{}, interface{}) error
|
||||
merger = func(depth int, keyName string, cfg1 interface{}, cfg2 interface{}) error {
|
||||
//fmt.Println(strings.Repeat(" ", depth), "Handling element: ", keyName, " for: ", cfg2)
|
||||
switch element2 := cfg2.(type) {
|
||||
case map[string]interface{}:
|
||||
//fmt.Println(strings.Repeat(" ", depth), " - A map")
|
||||
element2, ok := cfg2.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("Config source: %s is not a map", keyName)
|
||||
}
|
||||
element1, ok := cfg1.(*map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("Config dest: %s is not a map", keyName)
|
||||
}
|
||||
for k, _ := range element2 {
|
||||
//fmt.Println(strings.Repeat(" ", depth), " - key: ", k)
|
||||
_, ok := (*element1)[k]
|
||||
if !ok {
|
||||
(*element1)[k] = element2[k]
|
||||
} else {
|
||||
//fmt.Println(strings.Repeat(" ", depth), " - merging: ", element2[k])
|
||||
asanarrayptr, ok := (*element1)[k].([]map[string]interface{})
|
||||
if ok {
|
||||
if err := merger(depth+1, k, &asanarrayptr, element2[k]); err != nil {
|
||||
return err
|
||||
}
|
||||
(*element1)[k] = asanarrayptr
|
||||
} else {
|
||||
asamapptr, ok := (*element1)[k].(map[string]interface{})
|
||||
if ok {
|
||||
if err := merger(depth+1, k, &asamapptr, element2[k]); err != nil {
|
||||
return err
|
||||
}
|
||||
(*element1)[k] = asamapptr
|
||||
} else {
|
||||
return fmt.Errorf("Config dest: %s does not make a valid map/array ptr", keyName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case []map[string]interface{}:
|
||||
//fmt.Println(strings.Repeat(" ", depth), " - An array")
|
||||
element2, ok := cfg2.([]map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("Config source: %s is not a map array", keyName)
|
||||
}
|
||||
//fmt.Println(strings.Repeat(" ", depth), " - element2: ", element2)
|
||||
element1, ok := cfg1.(*[]map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("Config dest: %s is not a map array", keyName)
|
||||
}
|
||||
//fmt.Println(strings.Repeat(" ", depth), " - element1: ", element1)
|
||||
for index, _ := range element2 {
|
||||
*element1 = append(*element1, element2[index])
|
||||
}
|
||||
case string:
|
||||
//fmt.Println(strings.Repeat(" ", depth), " - A string")
|
||||
element2, ok := cfg2.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("Config: %s is not a string", keyName)
|
||||
}
|
||||
case bool:
|
||||
//fmt.Println(strings.Repeat(" ", depth), " - A boolean")
|
||||
element2, ok := cfg2.(bool)
|
||||
if !ok {
|
||||
return fmt.Errorf("Config: %s is not a boolean value", keyName)
|
||||
}
|
||||
case float64:
|
||||
//fmt.Println(strings.Repeat(" ", depth), " - A float64")
|
||||
element2, ok := cfg2.(float64)
|
||||
if !ok {
|
||||
return fmt.Errorf("Config: %s is not a float64 value", keyName)
|
||||
}
|
||||
case nil:
|
||||
//fmt.Println(strings.Repeat(" ", depth), " - Nil")
|
||||
default:
|
||||
log.V(2).Info("Unknown element type found in configuration file. Ignoring.", "type", reflect.TypeOf(element2))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
err := merger(0, "TOP", config1, config2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeConfigsO(config1 interface{}, config2 interface{}) (interface{}, error) {
|
||||
var merger func(int, string, interface{}, interface{}) (interface{}, error)
|
||||
merger = func(depth int, keyName string, cfg1 interface{}, cfg2 interface{}) (interface{}, error) {
|
||||
var returnElement interface{}
|
||||
fmt.Println(strings.Repeat(" ", depth), "Handling element: ", keyName, " for: ", cfg1)
|
||||
switch element1 := cfg1.(type) {
|
||||
case map[string]interface{}:
|
||||
element2, ok := cfg2.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Config: %s is not a map", keyName)
|
||||
}
|
||||
for k, value2 := range element2 {
|
||||
value1, ok := element1[k]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Config: %s not found in at least one config file", k)
|
||||
}
|
||||
merged, err := merger(depth+1, k, value1, value2)
|
||||
if err != nil {
|
||||
return merged, err
|
||||
}
|
||||
element1[k] = merged
|
||||
}
|
||||
returnElement = element1
|
||||
case string:
|
||||
element2, ok := cfg2.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Config: %s is not a string", keyName)
|
||||
}
|
||||
if element2 != "" {
|
||||
element1 = element2
|
||||
}
|
||||
returnElement = element1
|
||||
case bool:
|
||||
element2, ok := cfg2.(bool)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Config: %s is not a boolean value", keyName)
|
||||
}
|
||||
if element2 {
|
||||
element1 = true
|
||||
}
|
||||
returnElement = element1
|
||||
case float64:
|
||||
element2, ok := cfg2.(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Config: %s is not a float64 value", keyName)
|
||||
}
|
||||
if element2 != 0 {
|
||||
element1 = element2
|
||||
}
|
||||
returnElement = element1
|
||||
case nil:
|
||||
if cfg2 == nil {
|
||||
returnElement = nil
|
||||
} else {
|
||||
element2, ok := cfg2.(map[string]interface{})
|
||||
if ok {
|
||||
returnElement = element2
|
||||
} else {
|
||||
element3, ok := cfg2.([]interface{})
|
||||
if ok {
|
||||
returnElement = element3
|
||||
} else {
|
||||
log.V(2).Info("Unexpected interface type for an assignment. Ignoring.")
|
||||
returnElement = element1
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
log.V(2).Info("Unknown element type found in configuration file. Ignoring.", "type", reflect.TypeOf(element1))
|
||||
returnElement = element1
|
||||
}
|
||||
fmt.Println(strings.Repeat(" ", depth), "Done with element: ", keyName, " for: ", returnElement)
|
||||
return returnElement, nil
|
||||
}
|
||||
|
||||
/*
|
||||
flattenedConfig1, _ := json.Marshal(config1)
|
||||
var jsonConfig1 interface{}
|
||||
//json.Unmarshal(flattenedConfig1, &jsonConfig1)
|
||||
d1 := json.NewDecoder(bytes.NewReader(flattenedConfig1))
|
||||
d1.UseNumber()
|
||||
if err := d1.Decode(&jsonConfig1); err != nil {
|
||||
return config1, err
|
||||
}
|
||||
|
||||
flattenedConfig2, _ := json.Marshal(config2)
|
||||
var jsonConfig2 interface{}
|
||||
//json.Unmarshal(flattenedConfig2, &jsonConfig2)
|
||||
//spew.Dump(jsonConfig2)
|
||||
d2 := json.NewDecoder(bytes.NewReader(flattenedConfig2))
|
||||
d2.UseNumber()
|
||||
if err := d2.Decode(&jsonConfig2); err != nil {
|
||||
return config1, err
|
||||
}
|
||||
*/
|
||||
|
||||
blocks, err := merger(0, "TOP", config1, config2)
|
||||
if err != nil {
|
||||
return config1, err
|
||||
}
|
||||
fmt.Println("--- OUTPUT ---")
|
||||
spew.Dump(blocks)
|
||||
fmt.Println("--- NICE EH ---")
|
||||
//
|
||||
//
|
||||
// TODO: return blocks... we will convert back to toml when we are done merging!
|
||||
|
||||
//
|
||||
buf := new(bytes.Buffer)
|
||||
err = toml.NewEncoder(buf).Encode(blocks)
|
||||
if err != nil {
|
||||
return config1, err
|
||||
}
|
||||
fmt.Println(buf.String())
|
||||
merged := config.Config{}
|
||||
if _, err := toml.Decode(buf.String(), &merged); err != nil {
|
||||
return config1, err
|
||||
}
|
||||
return merged, nil
|
||||
}
|
||||
|
||||
func handleArgs(cfg config.Config) (*config.Config, error) {
|
||||
// LDAP flags
|
||||
if ldap, ok := args["--ldap"].(string); ok && ldap != "" {
|
||||
cfg.LDAP.Enabled = true
|
||||
cfg.LDAP.Listen = ldap
|
||||
}
|
||||
|
||||
// LDAPS flags
|
||||
if ldaps, ok := args["--ldaps"].(string); ok && ldaps != "" {
|
||||
cfg.LDAPS.Enabled = true
|
||||
cfg.LDAPS.Listen = ldaps
|
||||
}
|
||||
if ldapsCert, ok := args["--ldaps-cert"].(string); ok && ldapsCert != "" {
|
||||
cfg.LDAPS.Cert = ldapsCert
|
||||
}
|
||||
if ldapsKey, ok := args["--ldaps-key"].(string); ok && ldapsKey != "" {
|
||||
cfg.LDAPS.Key = ldapsKey
|
||||
}
|
||||
|
||||
// Backward Compability
|
||||
if cfg.Backend.Datastore != "" {
|
||||
if cfg.Backends != nil {
|
||||
return &cfg, fmt.Errorf("You cannot specify both [Backend] and [[Backends]] directives in the same configuration ")
|
||||
} else {
|
||||
cfg.Backends = append(cfg.Backends, cfg.Backend)
|
||||
}
|
||||
}
|
||||
spew.Dump(cfg.Backends)
|
||||
|
||||
// Patch with default values where not specified
|
||||
for i := range cfg.Backends {
|
||||
if cfg.Backends[i].NameFormat == "" {
|
||||
cfg.Backends[i].NameFormat = "cn"
|
||||
}
|
||||
if cfg.Backends[i].GroupFormat == "" {
|
||||
cfg.Backends[i].GroupFormat = "ou"
|
||||
}
|
||||
if cfg.Backends[i].SSHKeyAttr == "" {
|
||||
cfg.Backends[i].SSHKeyAttr = "sshPublicKey"
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
func handleLegacyConfig(cfg config.Config) (*config.Config, error) {
|
||||
if len(cfg.Frontend.Listen) > 0 && (len(cfg.LDAP.Listen) > 0 || len(cfg.LDAPS.Listen) > 0) {
|
||||
// Both old server-config and new - dont allow
|
||||
return &cfg, fmt.Errorf("Both old and new server-config in use - please remove old format ([frontend]) and migrate to new format ([ldap], [ldaps])")
|
||||
}
|
||||
|
||||
if len(cfg.Frontend.Listen) > 0 {
|
||||
// We're going with old format - parse it into new
|
||||
log.V(2).Info("Config [frontend] is deprecated - please move to [ldap] and [ldaps] as-per documentation")
|
||||
|
||||
cfg.LDAP.Enabled = !cfg.Frontend.TLS
|
||||
cfg.LDAPS.Enabled = cfg.Frontend.TLS
|
||||
|
||||
if cfg.Frontend.TLS {
|
||||
cfg.LDAPS.Listen = cfg.Frontend.Listen
|
||||
} else {
|
||||
cfg.LDAP.Listen = cfg.Frontend.Listen
|
||||
}
|
||||
|
||||
if len(cfg.Frontend.Cert) > 0 {
|
||||
cfg.LDAPS.Cert = cfg.Frontend.Cert
|
||||
}
|
||||
if len(cfg.Frontend.Key) > 0 {
|
||||
cfg.LDAPS.Key = cfg.Frontend.Key
|
||||
}
|
||||
}
|
||||
return &cfg, nil
|
||||
}
|
||||
func validateConfig(cfg config.Config) (*config.Config, error) {
|
||||
|
||||
if !cfg.LDAP.Enabled && !cfg.LDAPS.Enabled {
|
||||
return &cfg, fmt.Errorf("No server configuration found: please provide either LDAP or LDAPS configuration")
|
||||
}
|
||||
|
||||
if cfg.LDAPS.Enabled {
|
||||
// LDAPS enabled - verify requirements (cert, key, listen)
|
||||
if len(cfg.LDAPS.Cert) == 0 || len(cfg.LDAPS.Key) == 0 {
|
||||
return &cfg, fmt.Errorf("LDAPS was enabled but no certificate or key were specified: please disable LDAPS or use the 'cert' and 'key' options")
|
||||
}
|
||||
|
||||
if len(cfg.LDAPS.Listen) == 0 {
|
||||
return &cfg, fmt.Errorf("No LDAPS bind address was specified: please disable LDAPS or use the 'listen' option")
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.LDAP.Enabled {
|
||||
// LDAP enabled - verify listen
|
||||
if len(cfg.LDAP.Listen) == 0 {
|
||||
return &cfg, fmt.Errorf("No LDAP bind address was specified: please disable LDAP or use the 'listen' option")
|
||||
}
|
||||
}
|
||||
|
||||
//spew.Dump(cfg)
|
||||
for i := range cfg.Backends {
|
||||
switch cfg.Backends[i].Datastore {
|
||||
case "":
|
||||
cfg.Backends[i].Datastore = "config"
|
||||
case "config":
|
||||
case "ldap":
|
||||
case "owncloud":
|
||||
case "plugin":
|
||||
default:
|
||||
return &cfg, fmt.Errorf("invalid backend %s - must be 'config', 'ldap', 'owncloud' or 'plugin'", cfg.Backends[i].Datastore)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove after deprecating UnixID on User and Group
|
||||
for _, user := range cfg.Users {
|
||||
if user.UnixID != 0 {
|
||||
user.UIDNumber = user.UnixID
|
||||
log.V(2).Info(fmt.Sprintf("User '%s': 'unixid' is deprecated - please move to 'uidnumber' as per documentation", user.Name))
|
||||
}
|
||||
}
|
||||
for _, group := range cfg.Groups {
|
||||
if group.UnixID != 0 {
|
||||
group.GIDNumber = group.UnixID
|
||||
log.V(2).Info(fmt.Sprintf("Group '%s': 'unixid' is deprecated - please move to 'gidnumber' as per documentation", group.Name))
|
||||
}
|
||||
}
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// doConfig reads the cli flags and config file
|
||||
func doConfig() error {
|
||||
// Parse config-file into config{} struct
|
||||
cfg, err := parseConfigFile(getConfigLocation())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Handle parsed flags
|
||||
cfg, err = handleArgs(*cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Handle parsing of legacy [frontend] section into [ldap] and/or [ldaps] sections
|
||||
cfg, err = handleLegacyConfig(*cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg, err = validateConfig(*cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Before greenlighting new config entirely, lets make sure the yubiauth works - in case they changed
|
||||
if activeConfig.YubikeyClientID != cfg.YubikeyClientID || activeConfig.YubikeySecret != cfg.YubikeySecret {
|
||||
if len(cfg.YubikeyClientID) > 0 && len(cfg.YubikeySecret) > 0 {
|
||||
_yubiAuth, err := yubigo.NewYubiAuth(cfg.YubikeyClientID, cfg.YubikeySecret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// No errors, override
|
||||
yubiAuth = _yubiAuth
|
||||
}
|
||||
}
|
||||
|
||||
// All config is validated and alright, copy to ativeConfig
|
||||
if err := copier.Copy(activeConfig, cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Handle logging settings for new config
|
||||
// - we do this last to make sure we only respect a fully validated config
|
||||
stderr = initLogging()
|
||||
|
||||
if activeConfig.Debug {
|
||||
logging.SetLevel(logging.DEBUG, programName)
|
||||
log.V(6).Info("Debugging enabled")
|
||||
}
|
||||
if activeConfig.Syslog {
|
||||
enableSyslog(stderr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// initLogging sets up logging to stderr
|
||||
func initLogging() *logging.LogBackend {
|
||||
|
||||
l := logging.MustGetLogger(programName)
|
||||
l.ExtraCalldepth = 2 // add extra call depth for the logr wrapper
|
||||
|
||||
log = gologgingr.New(
|
||||
gologgingr.Logger(l),
|
||||
)
|
||||
gologgingr.SetVerbosity(10) // do not filter by verbosity. glauth uses the go-logging lib to filter the levels
|
||||
|
||||
format := "%{color}%{time:15:04:05.000000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}"
|
||||
logBackend := logging.NewLogBackend(os.Stderr, "", 0)
|
||||
|
||||
logging.SetBackend(logBackend)
|
||||
logging.SetLevel(logging.NOTICE, programName)
|
||||
logging.SetFormatter(logging.MustStringFormatter(format))
|
||||
return logBackend
|
||||
}
|
||||
|
||||
// enableSyslog turns on syslog and turns off color
|
||||
func enableSyslog(stderrBackend *logging.LogBackend) {
|
||||
format := "%{time:15:04:05.000000} %{shortfunc} ▶ %{level:.4s} %{id:03x} %{message}"
|
||||
logging.SetFormatter(logging.MustStringFormatter(format))
|
||||
syslogBackend, err := logging.NewSyslogBackend("")
|
||||
if err != nil {
|
||||
log.Error(err, "Could not create new syslog backend")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
logging.SetBackend(stderrBackend, syslogBackend)
|
||||
|
||||
log.V(6).Info("Syslog enabled")
|
||||
}
|
||||
-223
@@ -1,223 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
type testEnv struct {
|
||||
checkanonymousrootDSE bool
|
||||
checkTOTP bool
|
||||
checkbindUPN bool
|
||||
svcdn string
|
||||
svcdnnogroup string
|
||||
otpdn string
|
||||
expectedinfo string
|
||||
expectedaccount string
|
||||
expectedfirstaccount string
|
||||
expectedgroup string
|
||||
}
|
||||
|
||||
func TestIntegerStuff(t *testing.T) {
|
||||
|
||||
Convey("Testing sample-simple local file-based LDAP server", t, func() {
|
||||
svc := startSvc(SD, "bin/glauth", "-c", "sample-simple.cfg")
|
||||
batteryOfTests(
|
||||
t,
|
||||
svc, testEnv{
|
||||
checkanonymousrootDSE: true,
|
||||
checkTOTP: true,
|
||||
checkbindUPN: true,
|
||||
expectedinfo: "supportedLDAPVersion: 3",
|
||||
svcdn: "cn=serviceuser,ou=svcaccts,dc=glauth,dc=com",
|
||||
svcdnnogroup: "cn=serviceuser,dc=glauth,dc=com",
|
||||
otpdn: "cn=otpuser,ou=superheros,dc=glauth,dc=com",
|
||||
expectedaccount: "dn: cn=hackers,ou=superheros,dc=glauth,dc=com",
|
||||
expectedfirstaccount: "dn: cn=hackers,ou=superheros,dc=glauth,dc=com",
|
||||
expectedgroup: "dn: cn=superheros,ou=groups,dc=glauth,dc=com",
|
||||
})
|
||||
stopSvc(svc)
|
||||
})
|
||||
|
||||
matchingLibrary := doRunGetFirst(RD, "ls", "bin/sqlite.so")
|
||||
if matchingLibrary == "bin/sqlite.so" {
|
||||
Convey("Testing sample-database LDAP server", t, func() {
|
||||
svc := startSvc(SD, "bin/glauth", "-c", "pkg/plugins/sample-database.cfg")
|
||||
batteryOfTests(
|
||||
t,
|
||||
svc, testEnv{
|
||||
checkanonymousrootDSE: true,
|
||||
checkTOTP: false,
|
||||
checkbindUPN: true,
|
||||
expectedinfo: "supportedLDAPVersion: 3",
|
||||
svcdn: "cn=serviceuser,ou=svcaccts,dc=glauth,dc=com",
|
||||
svcdnnogroup: "cn=serviceuser,dc=glauth,dc=com",
|
||||
otpdn: "cn=otpuser,ou=superheros,dc=glauth,dc=com",
|
||||
expectedaccount: "dn: cn=hackers,ou=superheros,dc=glauth,dc=com",
|
||||
expectedfirstaccount: "dn: cn=hackers,ou=superheros,dc=glauth,dc=com",
|
||||
expectedgroup: "dn: cn=superheros,ou=groups,dc=glauth,dc=com",
|
||||
})
|
||||
stopSvc(svc)
|
||||
})
|
||||
}
|
||||
|
||||
matchingContainers := doRunGetFirst(RD, "sh", "-c", "docker ps | grep openldap-service | wc -l")
|
||||
if matchingContainers == "1" {
|
||||
Convey("Testing sample-simple local LDAP server", t, func() {
|
||||
svc := startSvc(SD, "bin/glauth", "-c", "sample-ldap-injection.cfg")
|
||||
batteryOfTests(
|
||||
t,
|
||||
svc, testEnv{
|
||||
checkanonymousrootDSE: false,
|
||||
checkTOTP: true,
|
||||
checkbindUPN: false,
|
||||
expectedinfo: "objectClass: top",
|
||||
svcdn: "cn=serviceuser,cn=svcaccts,ou=users,dc=glauth,dc=com",
|
||||
svcdnnogroup: "", // ignore
|
||||
otpdn: "cn=otpuser,cn=superheros,ou=users,dc=glauth,dc=com",
|
||||
expectedaccount: "dn: cn=hackers,cn=superheros,ou=users,dc=glauth,dc=com",
|
||||
expectedfirstaccount: "dn: cn=johndoe,cn=superheros,ou=users,dc=glauth,dc=com",
|
||||
expectedgroup: "dn: cn=superheros,ou=users,dc=glauth,dc=com",
|
||||
})
|
||||
stopSvc(svc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func batteryOfTests(t *testing.T, svc *exec.Cmd, env testEnv) {
|
||||
Convey("When searching for the 'hackers' user", func() {
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "cn=hackers")
|
||||
Convey("We should find them in the 'superheros' group", func() {
|
||||
So(out, ShouldEqual, env.expectedaccount)
|
||||
})
|
||||
})
|
||||
|
||||
if env.svcdnnogroup != "" {
|
||||
Convey("When searching for the 'hackers' user without binding with a group", func() {
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdnnogroup, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "cn=hackers")
|
||||
Convey("We should find them in the 'superheros' group", func() {
|
||||
So(out, ShouldEqual, env.expectedaccount)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if env.checkbindUPN {
|
||||
Convey("When searching for the 'hackers' user after binding using the account's UPN", func() {
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", "serviceuser@example.com", "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "cn=hackers")
|
||||
Convey("We should find them in the 'superheros' group", func() {
|
||||
So(out, ShouldEqual, env.expectedaccount)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Convey("When querying the root SDE", func() {
|
||||
out := doRunGetSecond(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-s", "base", "(objectclass=*)")
|
||||
Convey("We should get some meta information", func() {
|
||||
So(out, ShouldEqual, env.expectedinfo)
|
||||
})
|
||||
})
|
||||
|
||||
if env.checkanonymousrootDSE {
|
||||
Convey("When querying the root SDE anonymously without authorizing in config file", func() {
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-x", "-s", "base", "(objectclass=*)")
|
||||
Convey("We should get error 50", func() {
|
||||
So(out, ShouldEqual, "exit status 50")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Convey("When enumerating posix groups", func() {
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "(objectclass=posixgroup)")
|
||||
Convey("We should get a list starting with the 'superheros' group", func() {
|
||||
So(out, ShouldEqual, env.expectedgroup)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When searching for members of the 'superheros' group", func() {
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "(memberOf=cn=superheros,ou=groups,dc=glauth,dc=com)")
|
||||
Convey("We should get a list starting with the 'hackers' user", func() {
|
||||
So(out, ShouldEqual, env.expectedfirstaccount)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When performing a complex search for members of 'superheros' group", func() {
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.svcdn, "-w", "mysecret", "-x", "-bdc=glauth,dc=com", "(&(objectClass=*)(memberOf=cn=superheros,ou=groups,dc=glauth,dc=com))")
|
||||
Convey("We should get a list starting with the 'hackers' user", func() {
|
||||
So(out, ShouldEqual, env.expectedfirstaccount)
|
||||
})
|
||||
})
|
||||
|
||||
if env.checkTOTP {
|
||||
Convey("When searching for the 'hacker' user using a TOTP-enabled account", func() {
|
||||
otpvalue := doRunGetFirst(RD, "oathtool", "--totp", "-b", "-d", "6", "3hnvnk4ycv44glzigd6s25j4dougs3rk")
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.otpdn, "-w", "mysecret"+otpvalue, "-x", "-bou=superheros,dc=glauth,dc=com", "cn=hackers")
|
||||
Convey("We should find them in in the 'superheros' group", func() {
|
||||
So(out, ShouldEqual, env.expectedaccount)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When searching for the 'hacker' user using a TOTP-enabled account and no value", func() {
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.otpdn, "-w", "mysecret", "-x", "-bou=superheros,dc=glauth,dc=com", "cn=hackers")
|
||||
Convey("We should get 'Invalid credentials(49)'", func() {
|
||||
So(out, ShouldEqual, "exit status 49")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When searching for the 'hacker' user using a TOTP-enabled account and the wrong value", func() {
|
||||
out := doRunGetFirst(RD, "ldapsearch", "-LLL", "-H", "ldap://localhost:3893", "-D", env.otpdn, "-w", "mysecret123456", "-x", "-bou=superheros,dc=glauth,dc=com", "cn=hackers")
|
||||
Convey("We should get 'Invalid credentials(49)'", func() {
|
||||
So(out, ShouldEqual, "exit status 49")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// -----=============================================================================----
|
||||
|
||||
const SD = 3 // Start Delay
|
||||
const RD = 2 // Response Delay
|
||||
|
||||
func startSvc(delay time.Duration, name string, arg ...string) *exec.Cmd {
|
||||
cmd := exec.Command(name, arg...)
|
||||
cmd.Start()
|
||||
if delay != 0 {
|
||||
time.Sleep(time.Second * delay)
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func stopSvc(svc *exec.Cmd) {
|
||||
svc.Process.Kill()
|
||||
}
|
||||
|
||||
func doRunGetFirst(delay time.Duration, name string, arg ...string) string {
|
||||
out := strings.SplitN(doRun(delay, name, arg...), "\n", 2)
|
||||
if out == nil || len(out) < 1 {
|
||||
return "*fail*"
|
||||
}
|
||||
return out[0]
|
||||
}
|
||||
|
||||
func doRunGetSecond(delay time.Duration, name string, arg ...string) string {
|
||||
out := strings.SplitN(doRun(delay, name, arg...), "\n", 3)
|
||||
if out == nil || len(out) < 2 {
|
||||
return "*fail*"
|
||||
}
|
||||
return out[1]
|
||||
}
|
||||
|
||||
func doRun(delay time.Duration, name string, arg ...string) string {
|
||||
out, err := exec.Command(name, arg...).Output()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
if delay != 0 {
|
||||
time.Sleep(time.Second * delay)
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
module github.com/glauth/glauth
|
||||
|
||||
go 1.16
|
||||
|
||||
replace github.com/hydronica/toml => ./toml
|
||||
|
||||
require (
|
||||
github.com/GeertJohan/yubigo v0.0.0-20190917122436-175bc097e60e
|
||||
github.com/arl/statsviz v0.4.0
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/go-logr/logr v0.4.0
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/hydronica/toml v0.4.2
|
||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
|
||||
github.com/lib/pq v1.10.0
|
||||
github.com/mattn/go-sqlite3 v1.14.6
|
||||
github.com/nmcclain/ldap v0.0.0-20210720162743-7f8d1e44eeba
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/smartystreets/goconvey v1.6.4
|
||||
github.com/yaegashi/msgraph.go v0.1.1-0.20200221123608-2d438cf2a7cc
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
||||
gopkg.in/amz.v3 v3.0.0-20201001071545-24fc1eceb27b
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
)
|
||||
@@ -1,83 +0,0 @@
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/GeertJohan/yubigo v0.0.0-20190917122436-175bc097e60e h1:Bqtt5C+uVk+vH/t5dmB47uDCTwxw16EYHqvJnmY2aQc=
|
||||
github.com/GeertJohan/yubigo v0.0.0-20190917122436-175bc097e60e/go.mod h1:njRCDrl+1RQ/A/+KVU8Ho2EWAxUSkohOWczdW3dzDG0=
|
||||
github.com/arl/statsviz v0.4.0 h1:pv8DWfti0TC8M2Baold2xGnCnir9jhqv1BO6UNKQmqY=
|
||||
github.com/arl/statsviz v0.4.0/go.mod h1:+5inUy/dxy11x/KSmicG3ZrEEy0Yr81AFm3dn4QC04M=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
|
||||
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
|
||||
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hydronica/toml v0.4.2 h1:BY+iAvyl2u1BlfgkJQzqc1UXDGLWfpMmOWuqJrwAVDU=
|
||||
github.com/hydronica/toml v0.4.2/go.mod h1:c7QhbYq3Wp9SlOWuG7MAieKUyXP2P/hXhy/YqWfbS/4=
|
||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
|
||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E=
|
||||
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 h1:D9EvfGQvlkKaDr2CRKN++7HbSXbefUNDrPq60T+g24s=
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484/go.mod h1:O1EljZ+oHprtxDDPHiMWVo/5dBT6PlvWX5PSwj80aBA=
|
||||
github.com/nmcclain/ldap v0.0.0-20210720162743-7f8d1e44eeba h1:DO8NFYdcRv1dnyAINJIBm6Bw2XibtLvQniNFGzf2W8E=
|
||||
github.com/nmcclain/ldap v0.0.0-20210720162743-7f8d1e44eeba/go.mod h1:4S0XndRL8HNOaQBfdViJ2F/GPCgL524xlXRuXFH12/U=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs=
|
||||
github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/yaegashi/msgraph.go v0.1.1-0.20200221123608-2d438cf2a7cc h1:ejaC8rvIvCWmsaFrvmGOxhBuMxxhBB1xRshuM98XQ7M=
|
||||
github.com/yaegashi/msgraph.go v0.1.1-0.20200221123608-2d438cf2a7cc/go.mod h1:tso14hwzqX4VbnWTNsxiL0DvMb2OwbGISFA7jDibdWc=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
gopkg.in/amz.v3 v3.0.0-20201001071545-24fc1eceb27b h1:tdSyAzD5FHJmZAZ13d5WexAo7YJkiyGphhzFfInMnDw=
|
||||
gopkg.in/amz.v3 v3.0.0-20201001071545-24fc1eceb27b/go.mod h1:cE0dCGx2UfBTjLFlzEx4EXJUmoX6BXBoX9GjKOvqha4=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@@ -1,23 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 e5ffe9fd
|
||||
dn: cn=config
|
||||
objectClass: olcGlobal
|
||||
cn: config
|
||||
olcArgsFile: /var/run/slapd/slapd.args
|
||||
olcLogLevel: none
|
||||
olcPidFile: /var/run/slapd/slapd.pid
|
||||
olcToolThreads: 1
|
||||
structuralObjectClass: olcGlobal
|
||||
entryUUID: 5c21e0e8-7ddc-103b-9ebb-c740e015b81b
|
||||
creatorsName: cn=config
|
||||
createTimestamp: 20210720192809Z
|
||||
olcTLSCipherSuite: SECURE256:+SECURE128:-VERS-TLS-ALL:+VERS-TLS1.2:-RSA:-DHE
|
||||
-DSS:-CAMELLIA-128-CBC:-CAMELLIA-256-CBC
|
||||
olcTLSCACertificateFile: /container/service/slapd/assets/certs/ca.crt
|
||||
olcTLSCertificateFile: /container/service/slapd/assets/certs/ldap.crt
|
||||
olcTLSCertificateKeyFile: /container/service/slapd/assets/certs/ldap.key
|
||||
olcTLSDHParamFile: /container/service/slapd/assets/certs/dhparam.pem
|
||||
olcTLSVerifyClient: demand
|
||||
entryCSN: 20210812002157.868917Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20210812002157Z
|
||||
@@ -1,16 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 2aabc521
|
||||
dn: cn=module{0}
|
||||
objectClass: olcModuleList
|
||||
cn: module{0}
|
||||
olcModulePath: /usr/lib/ldap
|
||||
olcModuleLoad: {0}back_mdb
|
||||
olcModuleLoad: {1}memberof
|
||||
olcModuleLoad: {2}refint
|
||||
structuralObjectClass: olcModuleList
|
||||
entryUUID: 5c2b1fb4-7ddc-103b-9ec3-c740e015b81b
|
||||
creatorsName: cn=admin,cn=config
|
||||
createTimestamp: 20210720192809Z
|
||||
entryCSN: 20210720192809.856380Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20210720192809Z
|
||||
@@ -1,12 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 bc37faae
|
||||
dn: cn=schema
|
||||
objectClass: olcSchemaConfig
|
||||
cn: schema
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 5c2570a0-7ddc-103b-9ebe-c740e015b81b
|
||||
creatorsName: cn=admin,cn=config
|
||||
createTimestamp: 20210720192809Z
|
||||
entryCSN: 20210720192809.316331Z#000000#000#000000
|
||||
modifiersName: cn=admin,cn=config
|
||||
modifyTimestamp: 20210720192809Z
|
||||
@@ -1,249 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 57d21f8c
|
||||
dn: cn={0}core
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {0}core
|
||||
olcAttributeTypes: {0}( 2.5.4.2 NAME 'knowledgeInformation' DESC 'RFC2256: k
|
||||
nowledge information' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.
|
||||
121.1.15{32768} )
|
||||
olcAttributeTypes: {1}( 2.5.4.4 NAME ( 'sn' 'surname' ) DESC 'RFC2256: last
|
||||
(family) name(s) for which the entity is known by' SUP name )
|
||||
olcAttributeTypes: {2}( 2.5.4.5 NAME 'serialNumber' DESC 'RFC2256: serial nu
|
||||
mber of the entity' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMat
|
||||
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44{64} )
|
||||
olcAttributeTypes: {3}( 2.5.4.6 NAME ( 'c' 'countryName' ) DESC 'RFC4519: tw
|
||||
o-letter ISO-3166 country code' SUP name SYNTAX 1.3.6.1.4.1.1466.115.121.1.
|
||||
11 SINGLE-VALUE )
|
||||
olcAttributeTypes: {4}( 2.5.4.7 NAME ( 'l' 'localityName' ) DESC 'RFC2256: l
|
||||
ocality which this object resides in' SUP name )
|
||||
olcAttributeTypes: {5}( 2.5.4.8 NAME ( 'st' 'stateOrProvinceName' ) DESC 'RF
|
||||
C2256: state or province which this object resides in' SUP name )
|
||||
olcAttributeTypes: {6}( 2.5.4.9 NAME ( 'street' 'streetAddress' ) DESC 'RFC2
|
||||
256: street address of this object' EQUALITY caseIgnoreMatch SUBSTR caseIgn
|
||||
oreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||
olcAttributeTypes: {7}( 2.5.4.10 NAME ( 'o' 'organizationName' ) DESC 'RFC22
|
||||
56: organization this object belongs to' SUP name )
|
||||
olcAttributeTypes: {8}( 2.5.4.11 NAME ( 'ou' 'organizationalUnitName' ) DESC
|
||||
'RFC2256: organizational unit this object belongs to' SUP name )
|
||||
olcAttributeTypes: {9}( 2.5.4.12 NAME 'title' DESC 'RFC2256: title associate
|
||||
d with the entity' SUP name )
|
||||
olcAttributeTypes: {10}( 2.5.4.14 NAME 'searchGuide' DESC 'RFC2256: search g
|
||||
uide, deprecated by enhancedSearchGuide' SYNTAX 1.3.6.1.4.1.1466.115.121.1.
|
||||
25 )
|
||||
olcAttributeTypes: {11}( 2.5.4.15 NAME 'businessCategory' DESC 'RFC2256: bus
|
||||
iness category' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch S
|
||||
YNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||
olcAttributeTypes: {12}( 2.5.4.16 NAME 'postalAddress' DESC 'RFC2256: postal
|
||||
address' EQUALITY caseIgnoreListMatch SUBSTR caseIgnoreListSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
|
||||
olcAttributeTypes: {13}( 2.5.4.17 NAME 'postalCode' DESC 'RFC2256: postal co
|
||||
de' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.
|
||||
1.4.1.1466.115.121.1.15{40} )
|
||||
olcAttributeTypes: {14}( 2.5.4.18 NAME 'postOfficeBox' DESC 'RFC2256: Post O
|
||||
ffice Box' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX
|
||||
1.3.6.1.4.1.1466.115.121.1.15{40} )
|
||||
olcAttributeTypes: {15}( 2.5.4.19 NAME 'physicalDeliveryOfficeName' DESC 'RF
|
||||
C2256: Physical Delivery Office Name' EQUALITY caseIgnoreMatch SUBSTR caseI
|
||||
gnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||
olcAttributeTypes: {16}( 2.5.4.20 NAME 'telephoneNumber' DESC 'RFC2256: Tele
|
||||
phone Number' EQUALITY telephoneNumberMatch SUBSTR telephoneNumberSubstring
|
||||
sMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.50{32} )
|
||||
olcAttributeTypes: {17}( 2.5.4.21 NAME 'telexNumber' DESC 'RFC2256: Telex Nu
|
||||
mber' SYNTAX 1.3.6.1.4.1.1466.115.121.1.52 )
|
||||
olcAttributeTypes: {18}( 2.5.4.22 NAME 'teletexTerminalIdentifier' DESC 'RFC
|
||||
2256: Teletex Terminal Identifier' SYNTAX 1.3.6.1.4.1.1466.115.121.1.51 )
|
||||
olcAttributeTypes: {19}( 2.5.4.23 NAME ( 'facsimileTelephoneNumber' 'fax' )
|
||||
DESC 'RFC2256: Facsimile (Fax) Telephone Number' SYNTAX 1.3.6.1.4.1.1466.11
|
||||
5.121.1.22 )
|
||||
olcAttributeTypes: {20}( 2.5.4.24 NAME 'x121Address' DESC 'RFC2256: X.121 Ad
|
||||
dress' EQUALITY numericStringMatch SUBSTR numericStringSubstringsMatch SYNT
|
||||
AX 1.3.6.1.4.1.1466.115.121.1.36{15} )
|
||||
olcAttributeTypes: {21}( 2.5.4.25 NAME 'internationaliSDNNumber' DESC 'RFC22
|
||||
56: international ISDN number' EQUALITY numericStringMatch SUBSTR numericSt
|
||||
ringSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.36{16} )
|
||||
olcAttributeTypes: {22}( 2.5.4.26 NAME 'registeredAddress' DESC 'RFC2256: re
|
||||
gistered postal address' SUP postalAddress SYNTAX 1.3.6.1.4.1.1466.115.121.
|
||||
1.41 )
|
||||
olcAttributeTypes: {23}( 2.5.4.27 NAME 'destinationIndicator' DESC 'RFC2256:
|
||||
destination indicator' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstring
|
||||
sMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44{128} )
|
||||
olcAttributeTypes: {24}( 2.5.4.28 NAME 'preferredDeliveryMethod' DESC 'RFC22
|
||||
56: preferred delivery method' SYNTAX 1.3.6.1.4.1.1466.115.121.1.14 SINGLE-
|
||||
VALUE )
|
||||
olcAttributeTypes: {25}( 2.5.4.29 NAME 'presentationAddress' DESC 'RFC2256:
|
||||
presentation address' EQUALITY presentationAddressMatch SYNTAX 1.3.6.1.4.1.
|
||||
1466.115.121.1.43 SINGLE-VALUE )
|
||||
olcAttributeTypes: {26}( 2.5.4.30 NAME 'supportedApplicationContext' DESC 'R
|
||||
FC2256: supported application context' EQUALITY objectIdentifierMatch SYNTA
|
||||
X 1.3.6.1.4.1.1466.115.121.1.38 )
|
||||
olcAttributeTypes: {27}( 2.5.4.31 NAME 'member' DESC 'RFC2256: member of a g
|
||||
roup' SUP distinguishedName )
|
||||
olcAttributeTypes: {28}( 2.5.4.32 NAME 'owner' DESC 'RFC2256: owner (of the
|
||||
object)' SUP distinguishedName )
|
||||
olcAttributeTypes: {29}( 2.5.4.33 NAME 'roleOccupant' DESC 'RFC2256: occupan
|
||||
t of role' SUP distinguishedName )
|
||||
olcAttributeTypes: {30}( 2.5.4.36 NAME 'userCertificate' DESC 'RFC2256: X.50
|
||||
9 user certificate, use ;binary' EQUALITY certificateExactMatch SYNTAX 1.3.
|
||||
6.1.4.1.1466.115.121.1.8 )
|
||||
olcAttributeTypes: {31}( 2.5.4.37 NAME 'cACertificate' DESC 'RFC2256: X.509
|
||||
CA certificate, use ;binary' EQUALITY certificateExactMatch SYNTAX 1.3.6.1.
|
||||
4.1.1466.115.121.1.8 )
|
||||
olcAttributeTypes: {32}( 2.5.4.38 NAME 'authorityRevocationList' DESC 'RFC22
|
||||
56: X.509 authority revocation list, use ;binary' SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.9 )
|
||||
olcAttributeTypes: {33}( 2.5.4.39 NAME 'certificateRevocationList' DESC 'RFC
|
||||
2256: X.509 certificate revocation list, use ;binary' SYNTAX 1.3.6.1.4.1.14
|
||||
66.115.121.1.9 )
|
||||
olcAttributeTypes: {34}( 2.5.4.40 NAME 'crossCertificatePair' DESC 'RFC2256:
|
||||
X.509 cross certificate pair, use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121
|
||||
.1.10 )
|
||||
olcAttributeTypes: {35}( 2.5.4.42 NAME ( 'givenName' 'gn' ) DESC 'RFC2256: f
|
||||
irst name(s) for which the entity is known by' SUP name )
|
||||
olcAttributeTypes: {36}( 2.5.4.43 NAME 'initials' DESC 'RFC2256: initials of
|
||||
some or all of names, but not the surname(s).' SUP name )
|
||||
olcAttributeTypes: {37}( 2.5.4.44 NAME 'generationQualifier' DESC 'RFC2256:
|
||||
name qualifier indicating a generation' SUP name )
|
||||
olcAttributeTypes: {38}( 2.5.4.45 NAME 'x500UniqueIdentifier' DESC 'RFC2256:
|
||||
X.500 unique identifier' EQUALITY bitStringMatch SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.6 )
|
||||
olcAttributeTypes: {39}( 2.5.4.46 NAME 'dnQualifier' DESC 'RFC2256: DN quali
|
||||
fier' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR case
|
||||
IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
|
||||
olcAttributeTypes: {40}( 2.5.4.47 NAME 'enhancedSearchGuide' DESC 'RFC2256:
|
||||
enhanced search guide' SYNTAX 1.3.6.1.4.1.1466.115.121.1.21 )
|
||||
olcAttributeTypes: {41}( 2.5.4.48 NAME 'protocolInformation' DESC 'RFC2256:
|
||||
protocol information' EQUALITY protocolInformationMatch SYNTAX 1.3.6.1.4.1.
|
||||
1466.115.121.1.42 )
|
||||
olcAttributeTypes: {42}( 2.5.4.50 NAME 'uniqueMember' DESC 'RFC2256: unique
|
||||
member of a group' EQUALITY uniqueMemberMatch SYNTAX 1.3.6.1.4.1.1466.115.1
|
||||
21.1.34 )
|
||||
olcAttributeTypes: {43}( 2.5.4.51 NAME 'houseIdentifier' DESC 'RFC2256: hous
|
||||
e identifier' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYN
|
||||
TAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
|
||||
olcAttributeTypes: {44}( 2.5.4.52 NAME 'supportedAlgorithms' DESC 'RFC2256:
|
||||
supported algorithms' SYNTAX 1.3.6.1.4.1.1466.115.121.1.49 )
|
||||
olcAttributeTypes: {45}( 2.5.4.53 NAME 'deltaRevocationList' DESC 'RFC2256:
|
||||
delta revocation list; use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 )
|
||||
olcAttributeTypes: {46}( 2.5.4.54 NAME 'dmdName' DESC 'RFC2256: name of DMD'
|
||||
SUP name )
|
||||
olcAttributeTypes: {47}( 2.5.4.65 NAME 'pseudonym' DESC 'X.520(4th): pseudon
|
||||
ym for the object' SUP name )
|
||||
olcAttributeTypes: {48}( 0.9.2342.19200300.100.1.3 NAME ( 'mail' 'rfc822Mail
|
||||
box' ) DESC 'RFC1274: RFC822 Mailbox' EQUALITY caseIgnoreIA5Match SUBST
|
||||
R caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256}
|
||||
)
|
||||
olcAttributeTypes: {49}( 0.9.2342.19200300.100.1.25 NAME ( 'dc' 'domainCompo
|
||||
nent' ) DESC 'RFC1274/2247: domain component' EQUALITY caseIgnoreIA5Match S
|
||||
UBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SIN
|
||||
GLE-VALUE )
|
||||
olcAttributeTypes: {50}( 0.9.2342.19200300.100.1.37 NAME 'associatedDomain'
|
||||
DESC 'RFC1274: domain associated with object' EQUALITY caseIgnoreIA5Match S
|
||||
UBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {51}( 1.2.840.113549.1.9.1 NAME ( 'email' 'emailAddress'
|
||||
'pkcs9email' ) DESC 'RFC3280: legacy attribute for email addresses in DNs'
|
||||
EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.
|
||||
6.1.4.1.1466.115.121.1.26{128} )
|
||||
olcObjectClasses: {0}( 2.5.6.2 NAME 'country' DESC 'RFC2256: a country' SUP
|
||||
top STRUCTURAL MUST c MAY ( searchGuide $ description ) )
|
||||
olcObjectClasses: {1}( 2.5.6.3 NAME 'locality' DESC 'RFC2256: a locality' SU
|
||||
P top STRUCTURAL MAY ( street $ seeAlso $ searchGuide $ st $ l $ descriptio
|
||||
n ) )
|
||||
olcObjectClasses: {2}( 2.5.6.4 NAME 'organization' DESC 'RFC2256: an organiz
|
||||
ation' SUP top STRUCTURAL MUST o MAY ( userPassword $ searchGuide $ seeAlso
|
||||
$ businessCategory $ x121Address $ registeredAddress $ destinationIndicato
|
||||
r $ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ tel
|
||||
ephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $ street
|
||||
$ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName
|
||||
$ st $ l $ description ) )
|
||||
olcObjectClasses: {3}( 2.5.6.5 NAME 'organizationalUnit' DESC 'RFC2256: an o
|
||||
rganizational unit' SUP top STRUCTURAL MUST ou MAY ( userPassword $ searchG
|
||||
uide $ seeAlso $ businessCategory $ x121Address $ registeredAddress $ desti
|
||||
nationIndicator $ preferredDeliveryMethod $ telexNumber $ teletexTerminalId
|
||||
entifier $ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNu
|
||||
mber $ street $ postOfficeBox $ postalCode $ postalAddress $ physicalDelive
|
||||
ryOfficeName $ st $ l $ description ) )
|
||||
olcObjectClasses: {4}( 2.5.6.6 NAME 'person' DESC 'RFC2256: a person' SUP to
|
||||
p STRUCTURAL MUST ( sn $ cn ) MAY ( userPassword $ telephoneNumber $ seeAls
|
||||
o $ description ) )
|
||||
olcObjectClasses: {5}( 2.5.6.7 NAME 'organizationalPerson' DESC 'RFC2256: an
|
||||
organizational person' SUP person STRUCTURAL MAY ( title $ x121Address $ r
|
||||
egisteredAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNu
|
||||
mber $ teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumbe
|
||||
r $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $ posta
|
||||
lAddress $ physicalDeliveryOfficeName $ ou $ st $ l ) )
|
||||
olcObjectClasses: {6}( 2.5.6.8 NAME 'organizationalRole' DESC 'RFC2256: an o
|
||||
rganizational role' SUP top STRUCTURAL MUST cn MAY ( x121Address $ register
|
||||
edAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNumber $
|
||||
teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ fac
|
||||
simileTelephoneNumber $ seeAlso $ roleOccupant $ preferredDeliveryMethod $
|
||||
street $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOffic
|
||||
eName $ ou $ st $ l $ description ) )
|
||||
olcObjectClasses: {7}( 2.5.6.9 NAME 'groupOfNames' DESC 'RFC2256: a group of
|
||||
names (DNs)' SUP top STRUCTURAL MUST ( member $ cn ) MAY ( businessCategor
|
||||
y $ seeAlso $ owner $ ou $ o $ description ) )
|
||||
olcObjectClasses: {8}( 2.5.6.10 NAME 'residentialPerson' DESC 'RFC2256: an r
|
||||
esidential person' SUP person STRUCTURAL MUST l MAY ( businessCategory $ x1
|
||||
21Address $ registeredAddress $ destinationIndicator $ preferredDeliveryMet
|
||||
hod $ telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internati
|
||||
onaliSDNNumber $ facsimileTelephoneNumber $ preferredDeliveryMethod $ stree
|
||||
t $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName
|
||||
$ st $ l ) )
|
||||
olcObjectClasses: {9}( 2.5.6.11 NAME 'applicationProcess' DESC 'RFC2256: an
|
||||
application process' SUP top STRUCTURAL MUST cn MAY ( seeAlso $ ou $ l $ de
|
||||
scription ) )
|
||||
olcObjectClasses: {10}( 2.5.6.12 NAME 'applicationEntity' DESC 'RFC2256: an
|
||||
application entity' SUP top STRUCTURAL MUST ( presentationAddress $ cn ) MA
|
||||
Y ( supportedApplicationContext $ seeAlso $ ou $ o $ l $ description ) )
|
||||
olcObjectClasses: {11}( 2.5.6.13 NAME 'dSA' DESC 'RFC2256: a directory syste
|
||||
m agent (a server)' SUP applicationEntity STRUCTURAL MAY knowledgeInformati
|
||||
on )
|
||||
olcObjectClasses: {12}( 2.5.6.14 NAME 'device' DESC 'RFC2256: a device' SUP
|
||||
top STRUCTURAL MUST cn MAY ( serialNumber $ seeAlso $ owner $ ou $ o $ l $
|
||||
description ) )
|
||||
olcObjectClasses: {13}( 2.5.6.15 NAME 'strongAuthenticationUser' DESC 'RFC22
|
||||
56: a strong authentication user' SUP top AUXILIARY MUST userCertificate )
|
||||
olcObjectClasses: {14}( 2.5.6.16 NAME 'certificationAuthority' DESC 'RFC2256
|
||||
: a certificate authority' SUP top AUXILIARY MUST ( authorityRevocationList
|
||||
$ certificateRevocationList $ cACertificate ) MAY crossCertificatePair )
|
||||
olcObjectClasses: {15}( 2.5.6.17 NAME 'groupOfUniqueNames' DESC 'RFC2256: a
|
||||
group of unique names (DN and Unique Identifier)' SUP top STRUCTURAL MUST (
|
||||
uniqueMember $ cn ) MAY ( businessCategory $ seeAlso $ owner $ ou $ o $ de
|
||||
scription ) )
|
||||
olcObjectClasses: {16}( 2.5.6.18 NAME 'userSecurityInformation' DESC 'RFC225
|
||||
6: a user security information' SUP top AUXILIARY MAY ( supportedAlgorithms
|
||||
) )
|
||||
olcObjectClasses: {17}( 2.5.6.16.2 NAME 'certificationAuthority-V2' SUP cert
|
||||
ificationAuthority AUXILIARY MAY ( deltaRevocationList ) )
|
||||
olcObjectClasses: {18}( 2.5.6.19 NAME 'cRLDistributionPoint' SUP top STRUCTU
|
||||
RAL MUST ( cn ) MAY ( certificateRevocationList $ authorityRevocationList $
|
||||
deltaRevocationList ) )
|
||||
olcObjectClasses: {19}( 2.5.6.20 NAME 'dmd' SUP top STRUCTURAL MUST ( dmdNam
|
||||
e ) MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $ x121Add
|
||||
ress $ registeredAddress $ destinationIndicator $ preferredDeliveryMethod $
|
||||
telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internationali
|
||||
SDNNumber $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode
|
||||
$ postalAddress $ physicalDeliveryOfficeName $ st $ l $ description ) )
|
||||
olcObjectClasses: {20}( 2.5.6.21 NAME 'pkiUser' DESC 'RFC2587: a PKI user' S
|
||||
UP top AUXILIARY MAY userCertificate )
|
||||
olcObjectClasses: {21}( 2.5.6.22 NAME 'pkiCA' DESC 'RFC2587: PKI certificate
|
||||
authority' SUP top AUXILIARY MAY ( authorityRevocationList $ certificateRe
|
||||
vocationList $ cACertificate $ crossCertificatePair ) )
|
||||
olcObjectClasses: {22}( 2.5.6.23 NAME 'deltaCRL' DESC 'RFC2587: PKI user' SU
|
||||
P top AUXILIARY MAY deltaRevocationList )
|
||||
olcObjectClasses: {23}( 1.3.6.1.4.1.250.3.15 NAME 'labeledURIObject' DESC 'R
|
||||
FC2079: object that contains the URI attribute type' MAY ( labeledURI ) SUP
|
||||
top AUXILIARY )
|
||||
olcObjectClasses: {24}( 0.9.2342.19200300.100.4.19 NAME 'simpleSecurityObjec
|
||||
t' DESC 'RFC1274: simple security object' SUP top AUXILIARY MUST userPasswo
|
||||
rd )
|
||||
olcObjectClasses: {25}( 1.3.6.1.4.1.1466.344 NAME 'dcObject' DESC 'RFC2247:
|
||||
domain component object' SUP top AUXILIARY MUST dc )
|
||||
olcObjectClasses: {26}( 1.3.6.1.1.3.1 NAME 'uidObject' DESC 'RFC2377: uid ob
|
||||
ject' SUP top AUXILIARY MUST uid )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 5c26a290-7ddc-103b-9ebf-c740e015b81b
|
||||
creatorsName: cn=admin,cn=config
|
||||
createTimestamp: 20210720192809Z
|
||||
entryCSN: 20210720192809.324162Z#000000#000#000000
|
||||
modifiersName: cn=admin,cn=config
|
||||
modifyTimestamp: 20210720192809Z
|
||||
@@ -1,178 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 3133677b
|
||||
dn: cn={1}cosine
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {1}cosine
|
||||
olcAttributeTypes: {0}( 0.9.2342.19200300.100.1.2 NAME 'textEncodedORAddress
|
||||
' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.
|
||||
4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {1}( 0.9.2342.19200300.100.1.4 NAME 'info' DESC 'RFC1274:
|
||||
general information' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsM
|
||||
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{2048} )
|
||||
olcAttributeTypes: {2}( 0.9.2342.19200300.100.1.5 NAME ( 'drink' 'favouriteD
|
||||
rink' ) DESC 'RFC1274: favorite drink' EQUALITY caseIgnoreMatch SUBSTR case
|
||||
IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {3}( 0.9.2342.19200300.100.1.6 NAME 'roomNumber' DESC 'RF
|
||||
C1274: room number' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMat
|
||||
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {4}( 0.9.2342.19200300.100.1.7 NAME 'photo' DESC 'RFC1274
|
||||
: photo (G3 fax)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.23{25000} )
|
||||
olcAttributeTypes: {5}( 0.9.2342.19200300.100.1.8 NAME 'userClass' DESC 'RFC
|
||||
1274: category of user' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstring
|
||||
sMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {6}( 0.9.2342.19200300.100.1.9 NAME 'host' DESC 'RFC1274:
|
||||
host computer' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch S
|
||||
YNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {7}( 0.9.2342.19200300.100.1.10 NAME 'manager' DESC 'RFC1
|
||||
274: DN of manager' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466
|
||||
.115.121.1.12 )
|
||||
olcAttributeTypes: {8}( 0.9.2342.19200300.100.1.11 NAME 'documentIdentifier'
|
||||
DESC 'RFC1274: unique identifier of document' EQUALITY caseIgnoreMatch SUB
|
||||
STR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {9}( 0.9.2342.19200300.100.1.12 NAME 'documentTitle' DESC
|
||||
'RFC1274: title of document' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSub
|
||||
stringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {10}( 0.9.2342.19200300.100.1.13 NAME 'documentVersion' D
|
||||
ESC 'RFC1274: version of document' EQUALITY caseIgnoreMatch SUBSTR caseIgno
|
||||
reSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {11}( 0.9.2342.19200300.100.1.14 NAME 'documentAuthor' DE
|
||||
SC 'RFC1274: DN of author of document' EQUALITY distinguishedNameMatch SYNT
|
||||
AX 1.3.6.1.4.1.1466.115.121.1.12 )
|
||||
olcAttributeTypes: {12}( 0.9.2342.19200300.100.1.15 NAME 'documentLocation'
|
||||
DESC 'RFC1274: location of document original' EQUALITY caseIgnoreMatch SUBS
|
||||
TR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {13}( 0.9.2342.19200300.100.1.20 NAME ( 'homePhone' 'home
|
||||
TelephoneNumber' ) DESC 'RFC1274: home telephone number' EQUALITY telephone
|
||||
NumberMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.50 )
|
||||
olcAttributeTypes: {14}( 0.9.2342.19200300.100.1.21 NAME 'secretary' DESC 'R
|
||||
FC1274: DN of secretary' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1
|
||||
.1466.115.121.1.12 )
|
||||
olcAttributeTypes: {15}( 0.9.2342.19200300.100.1.22 NAME 'otherMailbox' SYNT
|
||||
AX 1.3.6.1.4.1.1466.115.121.1.39 )
|
||||
olcAttributeTypes: {16}( 0.9.2342.19200300.100.1.26 NAME 'aRecord' EQUALITY
|
||||
caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {17}( 0.9.2342.19200300.100.1.27 NAME 'mDRecord' EQUALITY
|
||||
caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {18}( 0.9.2342.19200300.100.1.28 NAME 'mXRecord' EQUALITY
|
||||
caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {19}( 0.9.2342.19200300.100.1.29 NAME 'nSRecord' EQUALITY
|
||||
caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {20}( 0.9.2342.19200300.100.1.30 NAME 'sOARecord' EQUALIT
|
||||
Y caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {21}( 0.9.2342.19200300.100.1.31 NAME 'cNAMERecord' EQUAL
|
||||
ITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {22}( 0.9.2342.19200300.100.1.38 NAME 'associatedName' DE
|
||||
SC 'RFC1274: DN of entry associated with domain' EQUALITY distinguishedName
|
||||
Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
|
||||
olcAttributeTypes: {23}( 0.9.2342.19200300.100.1.39 NAME 'homePostalAddress'
|
||||
DESC 'RFC1274: home postal address' EQUALITY caseIgnoreListMatch SUBSTR ca
|
||||
seIgnoreListSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
|
||||
olcAttributeTypes: {24}( 0.9.2342.19200300.100.1.40 NAME 'personalTitle' DES
|
||||
C 'RFC1274: personal title' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubst
|
||||
ringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {25}( 0.9.2342.19200300.100.1.41 NAME ( 'mobile' 'mobileT
|
||||
elephoneNumber' ) DESC 'RFC1274: mobile telephone number' EQUALITY telephon
|
||||
eNumberMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.
|
||||
115.121.1.50 )
|
||||
olcAttributeTypes: {26}( 0.9.2342.19200300.100.1.42 NAME ( 'pager' 'pagerTel
|
||||
ephoneNumber' ) DESC 'RFC1274: pager telephone number' EQUALITY telephoneNu
|
||||
mberMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115
|
||||
.121.1.50 )
|
||||
olcAttributeTypes: {27}( 0.9.2342.19200300.100.1.43 NAME ( 'co' 'friendlyCou
|
||||
ntryName' ) DESC 'RFC1274: friendly country name' EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {28}( 0.9.2342.19200300.100.1.44 NAME 'uniqueIdentifier'
|
||||
DESC 'RFC1274: unique identifer' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.
|
||||
1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {29}( 0.9.2342.19200300.100.1.45 NAME 'organizationalStat
|
||||
us' DESC 'RFC1274: organizational status' EQUALITY caseIgnoreMatch SUBSTR c
|
||||
aseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {30}( 0.9.2342.19200300.100.1.46 NAME 'janetMailbox' DESC
|
||||
'RFC1274: Janet mailbox' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5S
|
||||
ubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
|
||||
olcAttributeTypes: {31}( 0.9.2342.19200300.100.1.47 NAME 'mailPreferenceOpti
|
||||
on' DESC 'RFC1274: mail preference option' SYNTAX 1.3.6.1.4.1.1466.115.121.
|
||||
1.27 )
|
||||
olcAttributeTypes: {32}( 0.9.2342.19200300.100.1.48 NAME 'buildingName' DESC
|
||||
'RFC1274: name of building' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubs
|
||||
tringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {33}( 0.9.2342.19200300.100.1.49 NAME 'dSAQuality' DESC '
|
||||
RFC1274: DSA Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.19 SINGLE-VALUE )
|
||||
olcAttributeTypes: {34}( 0.9.2342.19200300.100.1.50 NAME 'singleLevelQuality
|
||||
' DESC 'RFC1274: Single Level Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.13
|
||||
SINGLE-VALUE )
|
||||
olcAttributeTypes: {35}( 0.9.2342.19200300.100.1.51 NAME 'subtreeMinimumQual
|
||||
ity' DESC 'RFC1274: Subtree Mininum Quality' SYNTAX 1.3.6.1.4.1.1466.115.12
|
||||
1.1.13 SINGLE-VALUE )
|
||||
olcAttributeTypes: {36}( 0.9.2342.19200300.100.1.52 NAME 'subtreeMaximumQual
|
||||
ity' DESC 'RFC1274: Subtree Maximun Quality' SYNTAX 1.3.6.1.4.1.1466.115.12
|
||||
1.1.13 SINGLE-VALUE )
|
||||
olcAttributeTypes: {37}( 0.9.2342.19200300.100.1.53 NAME 'personalSignature'
|
||||
DESC 'RFC1274: Personal Signature (G3 fax)' SYNTAX 1.3.6.1.4.1.1466.115.12
|
||||
1.1.23 )
|
||||
olcAttributeTypes: {38}( 0.9.2342.19200300.100.1.54 NAME 'dITRedirect' DESC
|
||||
'RFC1274: DIT Redirect' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.
|
||||
1466.115.121.1.12 )
|
||||
olcAttributeTypes: {39}( 0.9.2342.19200300.100.1.55 NAME 'audio' DESC 'RFC12
|
||||
74: audio (u-law)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.4{25000} )
|
||||
olcAttributeTypes: {40}( 0.9.2342.19200300.100.1.56 NAME 'documentPublisher'
|
||||
DESC 'RFC1274: publisher of document' EQUALITY caseIgnoreMatch SUBSTR case
|
||||
IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcObjectClasses: {0}( 0.9.2342.19200300.100.4.4 NAME ( 'pilotPerson' 'newPi
|
||||
lotPerson' ) SUP person STRUCTURAL MAY ( userid $ textEncodedORAddress $ rf
|
||||
c822Mailbox $ favouriteDrink $ roomNumber $ userClass $ homeTelephoneNumber
|
||||
$ homePostalAddress $ secretary $ personalTitle $ preferredDeliveryMethod
|
||||
$ businessCategory $ janetMailbox $ otherMailbox $ mobileTelephoneNumber $
|
||||
pagerTelephoneNumber $ organizationalStatus $ mailPreferenceOption $ person
|
||||
alSignature ) )
|
||||
olcObjectClasses: {1}( 0.9.2342.19200300.100.4.5 NAME 'account' SUP top STRU
|
||||
CTURAL MUST userid MAY ( description $ seeAlso $ localityName $ organizatio
|
||||
nName $ organizationalUnitName $ host ) )
|
||||
olcObjectClasses: {2}( 0.9.2342.19200300.100.4.6 NAME 'document' SUP top STR
|
||||
UCTURAL MUST documentIdentifier MAY ( commonName $ description $ seeAlso $
|
||||
localityName $ organizationName $ organizationalUnitName $ documentTitle $
|
||||
documentVersion $ documentAuthor $ documentLocation $ documentPublisher ) )
|
||||
olcObjectClasses: {3}( 0.9.2342.19200300.100.4.7 NAME 'room' SUP top STRUCTU
|
||||
RAL MUST commonName MAY ( roomNumber $ description $ seeAlso $ telephoneNum
|
||||
ber ) )
|
||||
olcObjectClasses: {4}( 0.9.2342.19200300.100.4.9 NAME 'documentSeries' SUP t
|
||||
op STRUCTURAL MUST commonName MAY ( description $ seeAlso $ telephonenumber
|
||||
$ localityName $ organizationName $ organizationalUnitName ) )
|
||||
olcObjectClasses: {5}( 0.9.2342.19200300.100.4.13 NAME 'domain' SUP top STRU
|
||||
CTURAL MUST domainComponent MAY ( associatedName $ organizationName $ descr
|
||||
iption $ businessCategory $ seeAlso $ searchGuide $ userPassword $ locality
|
||||
Name $ stateOrProvinceName $ streetAddress $ physicalDeliveryOfficeName $ p
|
||||
ostalAddress $ postalCode $ postOfficeBox $ streetAddress $ facsimileTeleph
|
||||
oneNumber $ internationalISDNNumber $ telephoneNumber $ teletexTerminalIden
|
||||
tifier $ telexNumber $ preferredDeliveryMethod $ destinationIndicator $ reg
|
||||
isteredAddress $ x121Address ) )
|
||||
olcObjectClasses: {6}( 0.9.2342.19200300.100.4.14 NAME 'RFC822localPart' SUP
|
||||
domain STRUCTURAL MAY ( commonName $ surname $ description $ seeAlso $ tel
|
||||
ephoneNumber $ physicalDeliveryOfficeName $ postalAddress $ postalCode $ po
|
||||
stOfficeBox $ streetAddress $ facsimileTelephoneNumber $ internationalISDNN
|
||||
umber $ telephoneNumber $ teletexTerminalIdentifier $ telexNumber $ preferr
|
||||
edDeliveryMethod $ destinationIndicator $ registeredAddress $ x121Address )
|
||||
)
|
||||
olcObjectClasses: {7}( 0.9.2342.19200300.100.4.15 NAME 'dNSDomain' SUP domai
|
||||
n STRUCTURAL MAY ( ARecord $ MDRecord $ MXRecord $ NSRecord $ SOARecord $ C
|
||||
NAMERecord ) )
|
||||
olcObjectClasses: {8}( 0.9.2342.19200300.100.4.17 NAME 'domainRelatedObject'
|
||||
DESC 'RFC1274: an object related to an domain' SUP top AUXILIARY MUST asso
|
||||
ciatedDomain )
|
||||
olcObjectClasses: {9}( 0.9.2342.19200300.100.4.18 NAME 'friendlyCountry' SUP
|
||||
country STRUCTURAL MUST friendlyCountryName )
|
||||
olcObjectClasses: {10}( 0.9.2342.19200300.100.4.20 NAME 'pilotOrganization'
|
||||
SUP ( organization $ organizationalUnit ) STRUCTURAL MAY buildingName )
|
||||
olcObjectClasses: {11}( 0.9.2342.19200300.100.4.21 NAME 'pilotDSA' SUP dsa S
|
||||
TRUCTURAL MAY dSAQuality )
|
||||
olcObjectClasses: {12}( 0.9.2342.19200300.100.4.22 NAME 'qualityLabelledData
|
||||
' SUP top AUXILIARY MUST dsaQuality MAY ( subtreeMinimumQuality $ subtreeMa
|
||||
ximumQuality ) )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 5c286ac6-7ddc-103b-9ec0-c740e015b81b
|
||||
creatorsName: cn=admin,cn=config
|
||||
createTimestamp: 20210720192809Z
|
||||
entryCSN: 20210720192809.335841Z#000000#000#000000
|
||||
modifiersName: cn=admin,cn=config
|
||||
modifyTimestamp: 20210720192809Z
|
||||
@@ -1,108 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 8c7d43da
|
||||
dn: cn={2}nis
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {2}nis
|
||||
olcAttributeTypes: {0}( 1.3.6.1.1.1.1.2 NAME 'gecos' DESC 'The GECOS field;
|
||||
the common name' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5Substrings
|
||||
Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||
olcAttributeTypes: {1}( 1.3.6.1.1.1.1.3 NAME 'homeDirectory' DESC 'The absol
|
||||
ute path to the home directory' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4
|
||||
.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||
olcAttributeTypes: {2}( 1.3.6.1.1.1.1.4 NAME 'loginShell' DESC 'The path to
|
||||
the login shell' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121
|
||||
.1.26 SINGLE-VALUE )
|
||||
olcAttributeTypes: {3}( 1.3.6.1.1.1.1.5 NAME 'shadowLastChange' EQUALITY int
|
||||
egerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {4}( 1.3.6.1.1.1.1.6 NAME 'shadowMin' EQUALITY integerMat
|
||||
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {5}( 1.3.6.1.1.1.1.7 NAME 'shadowMax' EQUALITY integerMat
|
||||
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {6}( 1.3.6.1.1.1.1.8 NAME 'shadowWarning' EQUALITY intege
|
||||
rMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {7}( 1.3.6.1.1.1.1.9 NAME 'shadowInactive' EQUALITY integ
|
||||
erMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {8}( 1.3.6.1.1.1.1.10 NAME 'shadowExpire' EQUALITY intege
|
||||
rMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {9}( 1.3.6.1.1.1.1.11 NAME 'shadowFlag' EQUALITY integerM
|
||||
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {10}( 1.3.6.1.1.1.1.12 NAME 'memberUid' EQUALITY caseExac
|
||||
tIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.12
|
||||
1.1.26 )
|
||||
olcAttributeTypes: {11}( 1.3.6.1.1.1.1.13 NAME 'memberNisNetgroup' EQUALITY
|
||||
caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.146
|
||||
6.115.121.1.26 )
|
||||
olcAttributeTypes: {12}( 1.3.6.1.1.1.1.14 NAME 'nisNetgroupTriple' DESC 'Net
|
||||
group triple' SYNTAX 1.3.6.1.1.1.0.0 )
|
||||
olcAttributeTypes: {13}( 1.3.6.1.1.1.1.15 NAME 'ipServicePort' EQUALITY inte
|
||||
gerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {14}( 1.3.6.1.1.1.1.16 NAME 'ipServiceProtocol' SUP name
|
||||
)
|
||||
olcAttributeTypes: {15}( 1.3.6.1.1.1.1.17 NAME 'ipProtocolNumber' EQUALITY i
|
||||
ntegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {16}( 1.3.6.1.1.1.1.18 NAME 'oncRpcNumber' EQUALITY integ
|
||||
erMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {17}( 1.3.6.1.1.1.1.19 NAME 'ipHostNumber' DESC 'IP addre
|
||||
ss' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
|
||||
olcAttributeTypes: {18}( 1.3.6.1.1.1.1.20 NAME 'ipNetworkNumber' DESC 'IP ne
|
||||
twork' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128
|
||||
} SINGLE-VALUE )
|
||||
olcAttributeTypes: {19}( 1.3.6.1.1.1.1.21 NAME 'ipNetmaskNumber' DESC 'IP ne
|
||||
tmask' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128
|
||||
} SINGLE-VALUE )
|
||||
olcAttributeTypes: {20}( 1.3.6.1.1.1.1.22 NAME 'macAddress' DESC 'MAC addres
|
||||
s' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
|
||||
olcAttributeTypes: {21}( 1.3.6.1.1.1.1.23 NAME 'bootParameter' DESC 'rpc.boo
|
||||
tparamd parameter' SYNTAX 1.3.6.1.1.1.0.1 )
|
||||
olcAttributeTypes: {22}( 1.3.6.1.1.1.1.24 NAME 'bootFile' DESC 'Boot image n
|
||||
ame' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {23}( 1.3.6.1.1.1.1.26 NAME 'nisMapName' SUP name )
|
||||
olcAttributeTypes: {24}( 1.3.6.1.1.1.1.27 NAME 'nisMapEntry' EQUALITY caseEx
|
||||
actIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.
|
||||
121.1.26{1024} SINGLE-VALUE )
|
||||
olcObjectClasses: {0}( 1.3.6.1.1.1.2.0 NAME 'posixAccount' DESC 'Abstraction
|
||||
of an account with POSIX attributes' SUP top AUXILIARY MUST ( cn $ uid $ u
|
||||
idNumber $ gidNumber $ homeDirectory ) MAY ( userPassword $ loginShell $ ge
|
||||
cos $ description ) )
|
||||
olcObjectClasses: {1}( 1.3.6.1.1.1.2.1 NAME 'shadowAccount' DESC 'Additional
|
||||
attributes for shadow passwords' SUP top AUXILIARY MUST uid MAY ( userPass
|
||||
word $ shadowLastChange $ shadowMin $ shadowMax $ shadowWarning $ shadowIna
|
||||
ctive $ shadowExpire $ shadowFlag $ description ) )
|
||||
olcObjectClasses: {2}( 1.3.6.1.1.1.2.2 NAME 'posixGroup' DESC 'Abstraction o
|
||||
f a group of accounts' SUP top STRUCTURAL MUST ( cn $ gidNumber ) MAY ( use
|
||||
rPassword $ memberUid $ description ) )
|
||||
olcObjectClasses: {3}( 1.3.6.1.1.1.2.3 NAME 'ipService' DESC 'Abstraction an
|
||||
Internet Protocol service' SUP top STRUCTURAL MUST ( cn $ ipServicePort $
|
||||
ipServiceProtocol ) MAY description )
|
||||
olcObjectClasses: {4}( 1.3.6.1.1.1.2.4 NAME 'ipProtocol' DESC 'Abstraction o
|
||||
f an IP protocol' SUP top STRUCTURAL MUST ( cn $ ipProtocolNumber $ descrip
|
||||
tion ) MAY description )
|
||||
olcObjectClasses: {5}( 1.3.6.1.1.1.2.5 NAME 'oncRpc' DESC 'Abstraction of an
|
||||
ONC/RPC binding' SUP top STRUCTURAL MUST ( cn $ oncRpcNumber $ description
|
||||
) MAY description )
|
||||
olcObjectClasses: {6}( 1.3.6.1.1.1.2.6 NAME 'ipHost' DESC 'Abstraction of a
|
||||
host, an IP device' SUP top AUXILIARY MUST ( cn $ ipHostNumber ) MAY ( l $
|
||||
description $ manager ) )
|
||||
olcObjectClasses: {7}( 1.3.6.1.1.1.2.7 NAME 'ipNetwork' DESC 'Abstraction of
|
||||
an IP network' SUP top STRUCTURAL MUST ( cn $ ipNetworkNumber ) MAY ( ipNe
|
||||
tmaskNumber $ l $ description $ manager ) )
|
||||
olcObjectClasses: {8}( 1.3.6.1.1.1.2.8 NAME 'nisNetgroup' DESC 'Abstraction
|
||||
of a netgroup' SUP top STRUCTURAL MUST cn MAY ( nisNetgroupTriple $ memberN
|
||||
isNetgroup $ description ) )
|
||||
olcObjectClasses: {9}( 1.3.6.1.1.1.2.9 NAME 'nisMap' DESC 'A generic abstrac
|
||||
tion of a NIS map' SUP top STRUCTURAL MUST nisMapName MAY description )
|
||||
olcObjectClasses: {10}( 1.3.6.1.1.1.2.10 NAME 'nisObject' DESC 'An entry in
|
||||
a NIS map' SUP top STRUCTURAL MUST ( cn $ nisMapEntry $ nisMapName ) MAY de
|
||||
scription )
|
||||
olcObjectClasses: {11}( 1.3.6.1.1.1.2.11 NAME 'ieee802Device' DESC 'A device
|
||||
with a MAC address' SUP top AUXILIARY MAY macAddress )
|
||||
olcObjectClasses: {12}( 1.3.6.1.1.1.2.12 NAME 'bootableDevice' DESC 'A devic
|
||||
e with boot parameters' SUP top AUXILIARY MAY ( bootFile $ bootParameter )
|
||||
)
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 5c2966ce-7ddc-103b-9ec1-c740e015b81b
|
||||
creatorsName: cn=admin,cn=config
|
||||
createTimestamp: 20210720192809Z
|
||||
entryCSN: 20210720192809.342294Z#000000#000#000000
|
||||
modifiersName: cn=admin,cn=config
|
||||
modifyTimestamp: 20210720192809Z
|
||||
@@ -1,49 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 b409a0c2
|
||||
dn: cn={3}inetorgperson
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {3}inetorgperson
|
||||
olcAttributeTypes: {0}( 2.16.840.1.113730.3.1.1 NAME 'carLicense' DESC 'RFC2
|
||||
798: vehicle license or registration plate' EQUALITY caseIgnoreMatch SUBSTR
|
||||
caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {1}( 2.16.840.1.113730.3.1.2 NAME 'departmentNumber' DESC
|
||||
'RFC2798: identifies a department within an organization' EQUALITY caseIgn
|
||||
oreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1
|
||||
.15 )
|
||||
olcAttributeTypes: {2}( 2.16.840.1.113730.3.1.241 NAME 'displayName' DESC 'R
|
||||
FC2798: preferred name to be used when displaying entries' EQUALITY caseIgn
|
||||
oreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1
|
||||
.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {3}( 2.16.840.1.113730.3.1.3 NAME 'employeeNumber' DESC '
|
||||
RFC2798: numerically identifies an employee within an organization' EQUALIT
|
||||
Y caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.
|
||||
115.121.1.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {4}( 2.16.840.1.113730.3.1.4 NAME 'employeeType' DESC 'RF
|
||||
C2798: type of employment for a person' EQUALITY caseIgnoreMatch SUBSTR cas
|
||||
eIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {5}( 0.9.2342.19200300.100.1.60 NAME 'jpegPhoto' DESC 'RF
|
||||
C2798: a JPEG image' SYNTAX 1.3.6.1.4.1.1466.115.121.1.28 )
|
||||
olcAttributeTypes: {6}( 2.16.840.1.113730.3.1.39 NAME 'preferredLanguage' DE
|
||||
SC 'RFC2798: preferred written or spoken language for a person' EQUALITY ca
|
||||
seIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.
|
||||
121.1.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {7}( 2.16.840.1.113730.3.1.40 NAME 'userSMIMECertificate'
|
||||
DESC 'RFC2798: PKCS#7 SignedData used to support S/MIME' SYNTAX 1.3.6.1.4.
|
||||
1.1466.115.121.1.5 )
|
||||
olcAttributeTypes: {8}( 2.16.840.1.113730.3.1.216 NAME 'userPKCS12' DESC 'RF
|
||||
C2798: personal identity information, a PKCS #12 PFX' SYNTAX 1.3.6.1.4.1.14
|
||||
66.115.121.1.5 )
|
||||
olcObjectClasses: {0}( 2.16.840.1.113730.3.2.2 NAME 'inetOrgPerson' DESC 'RF
|
||||
C2798: Internet Organizational Person' SUP organizationalPerson STRUCTURAL
|
||||
MAY ( audio $ businessCategory $ carLicense $ departmentNumber $ displayNam
|
||||
e $ employeeNumber $ employeeType $ givenName $ homePhone $ homePostalAddre
|
||||
ss $ initials $ jpegPhoto $ labeledURI $ mail $ manager $ mobile $ o $ page
|
||||
r $ photo $ roomNumber $ secretary $ uid $ userCertificate $ x500uniqueIden
|
||||
tifier $ preferredLanguage $ userSMIMECertificate $ userPKCS12 ) )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 5c2a5016-7ddc-103b-9ec2-c740e015b81b
|
||||
creatorsName: cn=admin,cn=config
|
||||
createTimestamp: 20210720192809Z
|
||||
entryCSN: 20210720192809.348266Z#000000#000#000000
|
||||
modifiersName: cn=admin,cn=config
|
||||
modifyTimestamp: 20210720192809Z
|
||||
@@ -1,65 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 cded21ef
|
||||
dn: cn={4}ppolicy
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {4}ppolicy
|
||||
olcAttributeTypes: {0}( 1.3.6.1.4.1.42.2.27.8.1.1 NAME 'pwdAttribute' EQUALI
|
||||
TY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
|
||||
olcAttributeTypes: {1}( 1.3.6.1.4.1.42.2.27.8.1.2 NAME 'pwdMinAge' EQUALITY
|
||||
integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.
|
||||
1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {2}( 1.3.6.1.4.1.42.2.27.8.1.3 NAME 'pwdMaxAge' EQUALITY
|
||||
integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.
|
||||
1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {3}( 1.3.6.1.4.1.42.2.27.8.1.4 NAME 'pwdInHistory' EQUALI
|
||||
TY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.1
|
||||
21.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {4}( 1.3.6.1.4.1.42.2.27.8.1.5 NAME 'pwdCheckQuality' EQU
|
||||
ALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.11
|
||||
5.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {5}( 1.3.6.1.4.1.42.2.27.8.1.6 NAME 'pwdMinLength' EQUALI
|
||||
TY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.
|
||||
121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {6}( 1.3.6.1.4.1.42.2.27.8.1.7 NAME 'pwdExpireWarning' EQ
|
||||
UALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.
|
||||
115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {7}( 1.3.6.1.4.1.42.2.27.8.1.8 NAME 'pwdGraceAuthNLimit'
|
||||
EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.146
|
||||
6.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {8}( 1.3.6.1.4.1.42.2.27.8.1.9 NAME 'pwdLockout' EQUALITY
|
||||
booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
||||
olcAttributeTypes: {9}( 1.3.6.1.4.1.42.2.27.8.1.10 NAME 'pwdLockoutDuration'
|
||||
EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.14
|
||||
66.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {10}( 1.3.6.1.4.1.42.2.27.8.1.11 NAME 'pwdMaxFailure' EQU
|
||||
ALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {11}( 1.3.6.1.4.1.42.2.27.8.1.12 NAME 'pwdFailureCountInt
|
||||
erval' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.
|
||||
4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {12}( 1.3.6.1.4.1.42.2.27.8.1.13 NAME 'pwdMustChange' EQU
|
||||
ALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
||||
olcAttributeTypes: {13}( 1.3.6.1.4.1.42.2.27.8.1.14 NAME 'pwdAllowUserChange
|
||||
' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
||||
olcAttributeTypes: {14}( 1.3.6.1.4.1.42.2.27.8.1.15 NAME 'pwdSafeModify' EQU
|
||||
ALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
||||
olcAttributeTypes: {15}( 1.3.6.1.4.1.4754.1.99.1 NAME 'pwdCheckModule' DESC
|
||||
'Loadable module that instantiates "check_password() function' EQUALITY cas
|
||||
eExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||
olcAttributeTypes: {16}( 1.3.6.1.4.1.42.2.27.8.1.30 NAME 'pwdMaxRecordedFail
|
||||
ure' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.
|
||||
1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcObjectClasses: {0}( 1.3.6.1.4.1.4754.2.99.1 NAME 'pwdPolicyChecker' SUP t
|
||||
op AUXILIARY MAY pwdCheckModule )
|
||||
olcObjectClasses: {1}( 1.3.6.1.4.1.42.2.27.8.2.1 NAME 'pwdPolicy' SUP top AU
|
||||
XILIARY MUST pwdAttribute MAY ( pwdMinAge $ pwdMaxAge $ pwdInHistory $ pwdC
|
||||
heckQuality $ pwdMinLength $ pwdExpireWarning $ pwdGraceAuthNLimit $ pwdLoc
|
||||
kout $ pwdLockoutDuration $ pwdMaxFailure $ pwdFailureCountInterval $ pwdMu
|
||||
stChange $ pwdAllowUserChange $ pwdSafeModify $ pwdMaxRecordedFailure ) )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 5c51364a-7ddc-103b-8d41-8b35b6446851
|
||||
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
createTimestamp: 20210720192809Z
|
||||
entryCSN: 20210720192809.603187Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20210720192809Z
|
||||
@@ -1,175 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 7d21399c
|
||||
dn: cn={5}kopano
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {5}kopano
|
||||
olcAttributeTypes: {0}( 1.3.6.1.4.1.47732.1.1.1.1 NAME 'kopanoQuotaOverride'
|
||||
DESC 'KOPANO: Override child quota' EQUALITY integerMatch SYNTAX 1.3.6.1.4
|
||||
.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {1}( 1.3.6.1.4.1.47732.1.1.1.2 NAME 'kopanoQuotaWarn' DES
|
||||
C 'KOPANO: Warning quota size in MB' EQUALITY integerMatch SYNTAX 1.3.6.1.4
|
||||
.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {2}( 1.3.6.1.4.1.47732.1.1.1.3 NAME 'kopanoQuotaSoft' DES
|
||||
C 'KOPANO: Soft quota size in MB' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.
|
||||
1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {3}( 1.3.6.1.4.1.47732.1.1.1.4 NAME 'kopanoQuotaHard' DES
|
||||
C 'KOPANO: Hard quota size in MB' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.
|
||||
1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {4}( 1.3.6.1.4.1.47732.1.1.1.5 NAME 'kopanoUserDefaultQuo
|
||||
taOverride' DESC 'KOPANO: Override User default quota for children' EQUALIT
|
||||
Y integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {5}( 1.3.6.1.4.1.47732.1.1.1.6 NAME 'kopanoUserDefaultQuo
|
||||
taWarn' DESC 'KOPANO: User default warning quota size in MB' EQUALITY integ
|
||||
erMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {6}( 1.3.6.1.4.1.47732.1.1.1.7 NAME 'kopanoUserDefaultQuo
|
||||
taSoft' DESC 'KOPANO: User default soft quota size in MB' EQUALITY integerM
|
||||
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {7}( 1.3.6.1.4.1.47732.1.1.1.8 NAME 'kopanoUserDefaultQuo
|
||||
taHard' DESC 'KOPANO: User default hard quota size in MB' EQUALITY integerM
|
||||
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {8}( 1.3.6.1.4.1.47732.1.1.2.1 NAME 'kopanoAdmin' DESC 'K
|
||||
OPANO: Administrator of kopano' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.14
|
||||
66.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {9}( 1.3.6.1.4.1.47732.1.1.2.2 NAME 'kopanoSharedStoreOnl
|
||||
y' DESC 'KOPANO: is store a shared store' EQUALITY integerMatch SYNTAX 1.3.
|
||||
6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {10}( 1.3.6.1.4.1.47732.1.1.2.3 NAME 'kopanoAccount' DESC
|
||||
'KOPANO: entry is a part of kopano' EQUALITY integerMatch SYNTAX 1.3.6.1.4
|
||||
.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {11}( 1.3.6.1.4.1.47732.1.1.2.4 NAME 'kopanoSendAsPrivile
|
||||
ge' DESC 'KOPANO: Users may directly send email as this user' EQUALITY case
|
||||
IgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.12
|
||||
1.1.15 )
|
||||
olcAttributeTypes: {12}( 1.3.6.1.4.1.47732.1.1.2.5 NAME 'kopanoMrAccept' DES
|
||||
C 'KOPANO: user should auto-accept meeting requests' EQUALITY integerMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {13}( 1.3.6.1.4.1.47732.1.1.2.6 NAME 'kopanoMrDeclineConf
|
||||
lict' DESC 'KOPANO: user should automatically decline conflicting meeting r
|
||||
equests' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-
|
||||
VALUE )
|
||||
olcAttributeTypes: {14}( 1.3.6.1.4.1.47732.1.1.2.7 NAME 'kopanoMrDeclineRecu
|
||||
rring' DESC 'KOPANO: user should automatically decline recurring meeting re
|
||||
quests' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-V
|
||||
ALUE )
|
||||
olcAttributeTypes: {15}( 1.3.6.1.4.1.47732.1.1.2.8 NAME 'kopanoId' DESC 'KOP
|
||||
ANO: Generic unique ID' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.40 SINGLE-VALUE )
|
||||
olcAttributeTypes: {16}( 1.3.6.1.4.1.47732.1.1.2.9 NAME 'kopanoResourceType'
|
||||
DESC 'KOPANO: for shared stores, resource is type Room or Equipment' EQUAL
|
||||
ITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.146
|
||||
6.115.121.1.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {17}( 1.3.6.1.4.1.47732.1.1.2.10 NAME 'kopanoResourceCapa
|
||||
city' DESC 'KOPANO: number of rooms or equipment available' EQUALITY intege
|
||||
rMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {18}( 1.3.6.1.4.1.47732.1.1.2.11 NAME 'kopanoHidden' DESC
|
||||
'KOPANO: This object should be hidden from address book' EQUALITY integerM
|
||||
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {19}( 1.3.6.1.4.1.47732.1.1.2.13 NAME 'kopanoEnabledFeatu
|
||||
res' DESC 'KOPANO: This user has these features explicitly enabled' EQUALIT
|
||||
Y caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
|
||||
olcAttributeTypes: {20}( 1.3.6.1.4.1.47732.1.1.2.14 NAME 'kopanoDisabledFeat
|
||||
ures' DESC 'KOPANO: This user has these features explicitly disabled' EQUAL
|
||||
ITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
|
||||
olcAttributeTypes: {21}( 1.3.6.1.4.1.47732.1.1.3.1 NAME 'kopanoAliases' DESC
|
||||
'KOPANO: All other email addresses for this user' EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {22}( 1.3.6.1.4.1.47732.1.1.4.1 NAME 'kopanoUserServer' D
|
||||
ESC 'KOPANO: Home server for the user' EQUALITY caseIgnoreMatch SUBSTR case
|
||||
IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {23}( 1.3.6.1.4.1.47732.1.1.6.1 NAME 'kopanoUserArchiveSe
|
||||
rvers' DESC 'KOPANO: List of server names that contain an archive store for
|
||||
the user' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX
|
||||
1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {24}( 1.3.6.1.4.1.47732.1.1.6.2 NAME 'kopanoUserArchiveCo
|
||||
uplings' DESC 'KOPANO: List of username:foldername pairs that specify many-
|
||||
to-one archive locations' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstri
|
||||
ngsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {25}( 1.3.6.1.4.1.47732.1.2.2.1 NAME 'kopanoSecurityGroup
|
||||
' DESC 'KOPANO: group has security possibilities' EQUALITY integerMatch SYN
|
||||
TAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {26}( 1.3.6.1.4.1.47732.1.3.2.4 NAME 'kopanoViewPrivilege
|
||||
' DESC 'KOPANO: Companies with view privileges over selected company' EQUAL
|
||||
ITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.146
|
||||
6.115.121.1.15 )
|
||||
olcAttributeTypes: {27}( 1.3.6.1.4.1.47732.1.3.2.5 NAME 'kopanoAdminPrivileg
|
||||
e' DESC 'KOPANO: Users from different companies which are administrator ove
|
||||
r selected company' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMat
|
||||
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {28}( 1.3.6.1.4.1.47732.1.3.2.6 NAME 'kopanoSystemAdmin'
|
||||
DESC 'KOPANO: The user who is the system administrator for this company' EQ
|
||||
UALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.
|
||||
1466.115.121.1.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {29}( 1.3.6.1.4.1.47732.1.3.1.5 NAME 'kopanoQuotaUserWarn
|
||||
ingRecipients' DESC 'KOPANO: Users who will receive a notification email wh
|
||||
en a user exceeds his quota' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubs
|
||||
tringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {30}( 1.3.6.1.4.1.47732.1.3.1.6 NAME 'kopanoQuotaCompanyW
|
||||
arningRecipients' DESC 'KOPANO: Users who will receive a notification email
|
||||
when a company exceeds its quota' EQUALITY caseIgnoreMatch SUBSTR caseIgno
|
||||
reSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {31}( 1.3.6.1.4.1.47732.1.3.4.1 NAME 'kopanoCompanyServer
|
||||
' DESC 'KOPANO: Home server for the public folders for a company' EQUALITY
|
||||
caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.11
|
||||
5.121.1.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {32}( 1.3.6.1.4.1.47732.1.4.4.1 NAME 'kopanoHttpPort' DES
|
||||
C 'KOPANO: Port for the http connection' EQUALITY integerMatch SYNTAX 1.3.6
|
||||
.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {33}( 1.3.6.1.4.1.47732.1.4.4.2 NAME 'kopanoSslPort' DESC
|
||||
'KOPANO: Port for the ssl connection' EQUALITY integerMatch SYNTAX 1.3.6.1
|
||||
.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {34}( 1.3.6.1.4.1.47732.1.4.4.3 NAME 'kopanoFilePath' DES
|
||||
C 'KOPANO: The Unix socket or named pipe to the server' EQUALITY caseIgnore
|
||||
Match SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
SINGLE-VALUE )
|
||||
olcAttributeTypes: {35}( 1.3.6.1.4.1.47732.1.4.4.4 NAME 'kopanoContainsPubli
|
||||
c' DESC 'KOPANO: This server contains the public store' EQUALITY integerMat
|
||||
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {36}( 1.3.6.1.4.1.47732.1.4.4.6 NAME 'kopanoProxyURL' DES
|
||||
C 'KOPANO: Full proxy URL for this server' EQUALITY caseIgnoreMatch SUBSTR
|
||||
caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
|
||||
)
|
||||
olcAttributeTypes: {37}( 1.3.6.1.4.1.47732.1.5.5.1 NAME 'kopanoFilter' DESC
|
||||
'KOPANO: LDAP Filter to apply' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSu
|
||||
bstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {38}( 1.3.6.1.4.1.47732.1.5.5.2 NAME 'kopanoBase' DESC 'K
|
||||
OPANO: LDAP Search base to apply' EQUALITY caseIgnoreMatch SUBSTR caseIgnor
|
||||
eSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
|
||||
olcObjectClasses: {0}( 1.3.6.1.4.1.47732.1.1.0.0 NAME 'kopano-user' DESC 'KO
|
||||
PANO: an user of Kopano' SUP top AUXILIARY MUST cn MAY ( kopanoQuotaOverrid
|
||||
e $ kopanoQuotaWarn $ kopanoQuotaSoft $ kopanoSendAsPrivilege $ kopanoQuota
|
||||
Hard $ kopanoAdmin $ kopanoSharedStoreOnly $ kopanoResourceType $ kopanoRes
|
||||
ourceCapacity $ kopanoAccount $ kopanoHidden $ kopanoAliases $ kopanoUserSe
|
||||
rver $ kopanoEnabledFeatures $ kopanoDisabledFeatures $ kopanoUserArchiveSe
|
||||
rvers $ kopanoUserArchiveCouplings $ uidNumber ) )
|
||||
olcObjectClasses: {1}( 1.3.6.1.4.1.47732.1.6.0.0 NAME 'kopano-contact' DESC
|
||||
'KOPANO: a contact of Kopano' SUP top AUXILIARY MUST ( cn $ uidNumber ) MAY
|
||||
( kopanoSendAsPrivilege $ kopanoHidden $ kopanoAliases $ kopanoAccount ) )
|
||||
olcObjectClasses: {2}( 1.3.6.1.4.1.47732.1.2.0.0 NAME 'kopano-group' DESC 'K
|
||||
OPANO: a group of Kopano' SUP top AUXILIARY MUST cn MAY ( kopanoAccount $ k
|
||||
opanoHidden $ mail $ kopanoAliases $ kopanoSecurityGroup $ kopanoSendAsPriv
|
||||
ilege $ gidNumber ) )
|
||||
olcObjectClasses: {3}( 1.3.6.1.4.1.47732.1.3.0.0 NAME 'kopano-company' DESC
|
||||
'KOPANO: a company of Kopano' SUP top AUXILIARY MUST ou MAY ( kopanoAccount
|
||||
$ kopanoHidden $ kopanoViewPrivilege $ kopanoAdminPrivilege $ kopanoSystem
|
||||
Admin $ kopanoQuotaOverride $ kopanoQuotaWarn $ kopanoUserDefaultQuotaOverr
|
||||
ide $ kopanoUserDefaultQuotaWarn $ kopanoUserDefaultQuotaSoft $ kopanoUserD
|
||||
efaultQuotaHard $ kopanoQuotaUserWarningRecipients $ kopanoQuotaCompanyWarn
|
||||
ingRecipients $ kopanoCompanyServer ) )
|
||||
olcObjectClasses: {4}( 1.3.6.1.4.1.47732.1.4.0.0 NAME 'kopano-server' DESC '
|
||||
KOPANO: a Kopano server' SUP top AUXILIARY MUST cn MAY ( kopanoAccount $ ko
|
||||
panoHidden $ kopanoHttpPort $ kopanoSslPort $ kopanoFilePath $ kopanoContai
|
||||
nsPublic $ kopanoProxyURL ) )
|
||||
olcObjectClasses: {5}( 1.3.6.1.4.1.47732.1.5.0.0 NAME 'kopano-addresslist' D
|
||||
ESC 'KOPANO: a Kopano Addresslist' SUP top STRUCTURAL MUST cn MAY ( kopanoA
|
||||
ccount $ kopanoHidden $ kopanoFilter $ kopanoBase ) )
|
||||
olcObjectClasses: {6}( 1.3.6.1.4.1.47732.1.7.0.0 NAME 'kopano-dynamicgroup'
|
||||
DESC 'KOPANO: a Kopano dynamic group' SUP top STRUCTURAL MUST cn MAY ( kopa
|
||||
noAccount $ kopanoHidden $ mail $ kopanoAliases $ kopanoFilter $ kopanoBase
|
||||
) )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 5c5ee420-7ddc-103b-8d42-8b35b6446851
|
||||
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
createTimestamp: 20210720192809Z
|
||||
entryCSN: 20210720192809.692846Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20210720192809Z
|
||||
@@ -1,18 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 815bdd09
|
||||
dn: cn={6}openssh-lpk
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {6}openssh-lpk
|
||||
olcAttributeTypes: {0}( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' D
|
||||
ESC 'MANDATORY: OpenSSH Public key' EQUALITY octetStringMatch SYNTAX 1.3.6.
|
||||
1.4.1.1466.115.121.1.40 )
|
||||
olcObjectClasses: {0}( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' DE
|
||||
SC 'MANDATORY: OpenSSH LPK objectclass' SUP top AUXILIARY MAY ( sshPublicKe
|
||||
y $ uid ) )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 5c6226ee-7ddc-103b-8d43-8b35b6446851
|
||||
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
createTimestamp: 20210720192809Z
|
||||
entryCSN: 20210720192809.714216Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20210720192809Z
|
||||
@@ -1,50 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 38be59a6
|
||||
dn: cn={7}postfix-book
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {7}postfix-book
|
||||
olcAttributeTypes: {0}( 1.3.6.1.4.1.29426.1.10.1 NAME 'mailHomeDirectory' DE
|
||||
SC 'The absolute path to the mail user home directory' EQUALITY caseExactIA
|
||||
5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||
olcAttributeTypes: {1}( 1.3.6.1.4.1.29426.1.10.2 NAME 'mailAlias' DESC 'RFC8
|
||||
22 Mailbox - mail alias' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5Su
|
||||
bstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
|
||||
olcAttributeTypes: {2}( 1.3.6.1.4.1.29426.1.10.3 NAME 'mailUidNumber' DESC '
|
||||
UID required to access the mailbox' EQUALITY integerMatch SYNTAX 1.3.6.1.4.
|
||||
1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {3}( 1.3.6.1.4.1.29426.1.10.4 NAME 'mailGidNumber' DESC '
|
||||
GID required to access the mailbox' EQUALITY integerMatch SYNTAX 1.3.6.1.4.
|
||||
1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {4}( 1.3.6.1.4.1.29426.1.10.5 NAME 'mailEnabled' DESC 'TR
|
||||
UE to enable, FALSE to disable account' EQUALITY booleanMatch SYNTAX 1.3.6.
|
||||
1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
||||
olcAttributeTypes: {5}( 1.3.6.1.4.1.29426.1.10.6 NAME 'mailGroupMember' DESC
|
||||
'Name of a mail distribution list' EQUALITY caseExactIA5Match SYNTAX 1.3.6
|
||||
.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {6}( 1.3.6.1.4.1.29426.1.10.7 NAME 'mailQuota' DESC 'Mail
|
||||
quota limit in kilobytes' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.14
|
||||
66.115.121.1.26 )
|
||||
olcAttributeTypes: {7}( 1.3.6.1.4.1.29426.1.10.8 NAME 'mailStorageDirectory'
|
||||
DESC 'The absolute path to the mail users mailbox' EQUALITY caseExactIA5Ma
|
||||
tch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||
olcAttributeTypes: {8}( 1.3.6.1.4.1.29426.1.10.9 NAME 'mailSieveRuleSource'
|
||||
DESC 'Sun ONE Messaging Server defined attribute' SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.26 X-ORIGIN 'Sun ONE Messaging Server' )
|
||||
olcAttributeTypes: {9}( 1.3.6.1.4.1.29426.1.10.10 NAME 'mailForwardingAddres
|
||||
s' DESC 'Address(es) to forward all incoming messages to.' EQUALITY caseIgn
|
||||
oreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{320} )
|
||||
olcObjectClasses: {0}( 1.3.6.1.4.1.29426.1.2.2.1 NAME 'PostfixBookMailAccoun
|
||||
t' DESC 'Mail account used in Postfix Book' SUP top AUXILIARY MUST mail MAY
|
||||
( mailHomeDirectory $ mailAlias $ mailGroupMember $ mailUidNumber $ mailGi
|
||||
dNumber $ mailEnabled $ mailQuota $ mailStorageDirectory $ mailSieveRuleSou
|
||||
rce ) )
|
||||
olcObjectClasses: {1}( 1.3.6.1.4.1.29426.1.2.2.2 NAME 'PostfixBookMailForwar
|
||||
d' DESC 'Mail forward used in Postfix Book' SUP top AUXILIARY MUST ( mail $
|
||||
mailAlias ) MAY mailForwardingAddress )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 5c658af0-7ddc-103b-8d44-8b35b6446851
|
||||
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
createTimestamp: 20210720192809Z
|
||||
entryCSN: 20210720192809.736438Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20210720192809Z
|
||||
@@ -1,235 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 41b81a66
|
||||
dn: cn={8}samba
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {8}samba
|
||||
olcAttributeTypes: {0}( 1.3.6.1.4.1.7165.2.1.24 NAME 'sambaLMPassword' DESC
|
||||
'LanManager Password' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.26{32} SINGLE-VALUE )
|
||||
olcAttributeTypes: {1}( 1.3.6.1.4.1.7165.2.1.25 NAME 'sambaNTPassword' DESC
|
||||
'MD4 hash of the unicode password' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6
|
||||
.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
|
||||
olcAttributeTypes: {2}( 1.3.6.1.4.1.7165.2.1.26 NAME 'sambaAcctFlags' DESC '
|
||||
Account Flags' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.
|
||||
1.26{16} SINGLE-VALUE )
|
||||
olcAttributeTypes: {3}( 1.3.6.1.4.1.7165.2.1.27 NAME 'sambaPwdLastSet' DESC
|
||||
'Timestamp of the last password update' EQUALITY integerMatch SYNTAX 1.3.6.
|
||||
1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {4}( 1.3.6.1.4.1.7165.2.1.28 NAME 'sambaPwdCanChange' DES
|
||||
C 'Timestamp of when the user is allowed to update the password' EQUALITY i
|
||||
ntegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {5}( 1.3.6.1.4.1.7165.2.1.29 NAME 'sambaPwdMustChange' DE
|
||||
SC 'Timestamp of when the password will expire' EQUALITY integerMatch SYNTA
|
||||
X 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {6}( 1.3.6.1.4.1.7165.2.1.30 NAME 'sambaLogonTime' DESC '
|
||||
Timestamp of last logon' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.
|
||||
121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {7}( 1.3.6.1.4.1.7165.2.1.31 NAME 'sambaLogoffTime' DESC
|
||||
'Timestamp of last logoff' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.11
|
||||
5.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {8}( 1.3.6.1.4.1.7165.2.1.32 NAME 'sambaKickoffTime' DESC
|
||||
'Timestamp of when the user will be logged off automatically' EQUALITY int
|
||||
egerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {9}( 1.3.6.1.4.1.7165.2.1.48 NAME 'sambaBadPasswordCount'
|
||||
DESC 'Bad password attempt count' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1
|
||||
.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {10}( 1.3.6.1.4.1.7165.2.1.49 NAME 'sambaBadPasswordTime'
|
||||
DESC 'Time of the last bad password attempt' EQUALITY integerMatch SYNTAX
|
||||
1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {11}( 1.3.6.1.4.1.7165.2.1.55 NAME 'sambaLogonHours' DESC
|
||||
'Logon Hours' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.
|
||||
1.26{42} SINGLE-VALUE )
|
||||
olcAttributeTypes: {12}( 1.3.6.1.4.1.7165.2.1.33 NAME 'sambaHomeDrive' DESC
|
||||
'Driver letter of home directory mapping' EQUALITY caseIgnoreIA5Match SYNTA
|
||||
X 1.3.6.1.4.1.1466.115.121.1.26{4} SINGLE-VALUE )
|
||||
olcAttributeTypes: {13}( 1.3.6.1.4.1.7165.2.1.34 NAME 'sambaLogonScript' DES
|
||||
C 'Logon script path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.
|
||||
121.1.15{255} SINGLE-VALUE )
|
||||
olcAttributeTypes: {14}( 1.3.6.1.4.1.7165.2.1.35 NAME 'sambaProfilePath' DES
|
||||
C 'Roaming profile path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.15{255} SINGLE-VALUE )
|
||||
olcAttributeTypes: {15}( 1.3.6.1.4.1.7165.2.1.36 NAME 'sambaUserWorkstations
|
||||
' DESC 'List of user workstations the user is allowed to logon to' EQUALITY
|
||||
caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
|
||||
olcAttributeTypes: {16}( 1.3.6.1.4.1.7165.2.1.37 NAME 'sambaHomePath' DESC '
|
||||
Home directory UNC path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.15{128} )
|
||||
olcAttributeTypes: {17}( 1.3.6.1.4.1.7165.2.1.38 NAME 'sambaDomainName' DESC
|
||||
'Windows NT domain to which the user belongs' EQUALITY caseIgnoreMatch SYN
|
||||
TAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||
olcAttributeTypes: {18}( 1.3.6.1.4.1.7165.2.1.47 NAME 'sambaMungedDial' DESC
|
||||
'Base64 encoded user parameter string' EQUALITY caseExactMatch SYNTAX 1.3.
|
||||
6.1.4.1.1466.115.121.1.15{1050} )
|
||||
olcAttributeTypes: {19}( 1.3.6.1.4.1.7165.2.1.54 NAME 'sambaPasswordHistory'
|
||||
DESC 'Concatenated MD5 hashes of the salted NT passwords used on this acco
|
||||
unt' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} )
|
||||
olcAttributeTypes: {20}( 1.3.6.1.4.1.7165.2.1.20 NAME 'sambaSID' DESC 'Secur
|
||||
ity ID' EQUALITY caseIgnoreIA5Match SUBSTR caseExactIA5SubstringsMatch SYNT
|
||||
AX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
|
||||
olcAttributeTypes: {21}( 1.3.6.1.4.1.7165.2.1.23 NAME 'sambaPrimaryGroupSID'
|
||||
DESC 'Primary Group Security ID' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.
|
||||
1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
|
||||
olcAttributeTypes: {22}( 1.3.6.1.4.1.7165.2.1.51 NAME 'sambaSIDList' DESC 'S
|
||||
ecurity ID List' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.12
|
||||
1.1.26{64} )
|
||||
olcAttributeTypes: {23}( 1.3.6.1.4.1.7165.2.1.19 NAME 'sambaGroupType' DESC
|
||||
'NT Group Type' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
|
||||
SINGLE-VALUE )
|
||||
olcAttributeTypes: {24}( 1.3.6.1.4.1.7165.2.1.21 NAME 'sambaNextUserRid' DES
|
||||
C 'Next NT rid to give our for users' EQUALITY integerMatch SYNTAX 1.3.6.1.
|
||||
4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {25}( 1.3.6.1.4.1.7165.2.1.22 NAME 'sambaNextGroupRid' DE
|
||||
SC 'Next NT rid to give out for groups' EQUALITY integerMatch SYNTAX 1.3.6.
|
||||
1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {26}( 1.3.6.1.4.1.7165.2.1.39 NAME 'sambaNextRid' DESC 'N
|
||||
ext NT rid to give out for anything' EQUALITY integerMatch SYNTAX 1.3.6.1.4
|
||||
.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {27}( 1.3.6.1.4.1.7165.2.1.40 NAME 'sambaAlgorithmicRidBa
|
||||
se' DESC 'Base at which the samba RID generation algorithm should operate'
|
||||
EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {28}( 1.3.6.1.4.1.7165.2.1.41 NAME 'sambaShareName' DESC
|
||||
'Share Name' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
SINGLE-VALUE )
|
||||
olcAttributeTypes: {29}( 1.3.6.1.4.1.7165.2.1.42 NAME 'sambaOptionName' DESC
|
||||
'Option Name' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SY
|
||||
NTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {30}( 1.3.6.1.4.1.7165.2.1.43 NAME 'sambaBoolOption' DESC
|
||||
'A boolean option' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1
|
||||
.7 SINGLE-VALUE )
|
||||
olcAttributeTypes: {31}( 1.3.6.1.4.1.7165.2.1.44 NAME 'sambaIntegerOption' D
|
||||
ESC 'An integer option' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.1
|
||||
21.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {32}( 1.3.6.1.4.1.7165.2.1.45 NAME 'sambaStringOption' DE
|
||||
SC 'A string option' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115
|
||||
.121.1.26 SINGLE-VALUE )
|
||||
olcAttributeTypes: {33}( 1.3.6.1.4.1.7165.2.1.46 NAME 'sambaStringListOption
|
||||
' DESC 'A string list option' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1
|
||||
466.115.121.1.15 )
|
||||
olcAttributeTypes: {34}( 1.3.6.1.4.1.7165.2.1.53 NAME 'sambaTrustFlags' DESC
|
||||
'Trust Password Flags' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466
|
||||
.115.121.1.26 )
|
||||
olcAttributeTypes: {35}( 1.3.6.1.4.1.7165.2.1.58 NAME 'sambaMinPwdLength' DE
|
||||
SC 'Minimal password length (default: 5)' EQUALITY integerMatch SYNTAX 1.3.
|
||||
6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {36}( 1.3.6.1.4.1.7165.2.1.59 NAME 'sambaPwdHistoryLength
|
||||
' DESC 'Length of Password History Entries (default: 0 => off)' EQUALITY in
|
||||
tegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {37}( 1.3.6.1.4.1.7165.2.1.60 NAME 'sambaLogonToChgPwd' D
|
||||
ESC 'Force Users to logon for password change (default: 0 => off, 2 => on)'
|
||||
EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {38}( 1.3.6.1.4.1.7165.2.1.61 NAME 'sambaMaxPwdAge' DESC
|
||||
'Maximum password age, in seconds (default: -1 => never expire passwords)'
|
||||
EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {39}( 1.3.6.1.4.1.7165.2.1.62 NAME 'sambaMinPwdAge' DESC
|
||||
'Minimum password age, in seconds (default: 0 => allow immediate password c
|
||||
hange)' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-V
|
||||
ALUE )
|
||||
olcAttributeTypes: {40}( 1.3.6.1.4.1.7165.2.1.63 NAME 'sambaLockoutDuration'
|
||||
DESC 'Lockout duration in minutes (default: 30, -1 => forever)' EQUALITY i
|
||||
ntegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {41}( 1.3.6.1.4.1.7165.2.1.64 NAME 'sambaLockoutObservati
|
||||
onWindow' DESC 'Reset time after lockout in minutes (default: 30)' EQUALITY
|
||||
integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {42}( 1.3.6.1.4.1.7165.2.1.65 NAME 'sambaLockoutThreshold
|
||||
' DESC 'Lockout users after bad logon attempts (default: 0 => off)' EQUALIT
|
||||
Y integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {43}( 1.3.6.1.4.1.7165.2.1.66 NAME 'sambaForceLogoff' DES
|
||||
C 'Disconnect Users outside logon hours (default: -1 => off, 0 => on)' EQUA
|
||||
LITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {44}( 1.3.6.1.4.1.7165.2.1.67 NAME 'sambaRefuseMachinePwd
|
||||
Change' DESC 'Allow Machine Password changes (default: 0 => off)' EQUALITY
|
||||
integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {45}( 1.3.6.1.4.1.7165.2.1.68 NAME 'sambaClearTextPasswor
|
||||
d' DESC 'Clear text password (used for trusted domain passwords)' EQUALITY
|
||||
octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
|
||||
olcAttributeTypes: {46}( 1.3.6.1.4.1.7165.2.1.69 NAME 'sambaPreviousClearTex
|
||||
tPassword' DESC 'Previous clear text password (used for trusted domain pass
|
||||
words)' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
|
||||
olcAttributeTypes: {47}( 1.3.6.1.4.1.7165.2.1.70 NAME 'sambaTrustType' DESC
|
||||
'Type of trust' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
|
||||
SINGLE-VALUE )
|
||||
olcAttributeTypes: {48}( 1.3.6.1.4.1.7165.2.1.71 NAME 'sambaTrustAttributes'
|
||||
DESC 'Trust attributes for a trusted domain' EQUALITY integerMatch SYNTAX
|
||||
1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {49}( 1.3.6.1.4.1.7165.2.1.72 NAME 'sambaTrustDirection'
|
||||
DESC 'Direction of a trust' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {50}( 1.3.6.1.4.1.7165.2.1.73 NAME 'sambaTrustPartner' DE
|
||||
SC 'Fully qualified name of the domain with which a trust exists' EQUALITY
|
||||
caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||
olcAttributeTypes: {51}( 1.3.6.1.4.1.7165.2.1.74 NAME 'sambaFlatName' DESC '
|
||||
NetBIOS name of a domain' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.
|
||||
115.121.1.15{128} )
|
||||
olcAttributeTypes: {52}( 1.3.6.1.4.1.7165.2.1.75 NAME 'sambaTrustAuthOutgoin
|
||||
g' DESC 'Authentication information for the outgoing portion of a trust' EQ
|
||||
UALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
|
||||
olcAttributeTypes: {53}( 1.3.6.1.4.1.7165.2.1.76 NAME 'sambaTrustAuthIncomin
|
||||
g' DESC 'Authentication information for the incoming portion of a trust' EQ
|
||||
UALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
|
||||
olcAttributeTypes: {54}( 1.3.6.1.4.1.7165.2.1.77 NAME 'sambaSecurityIdentifi
|
||||
er' DESC 'SID of a trusted domain' EQUALITY caseIgnoreIA5Match SUBSTR caseE
|
||||
xactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALU
|
||||
E )
|
||||
olcAttributeTypes: {55}( 1.3.6.1.4.1.7165.2.1.78 NAME 'sambaTrustForestTrust
|
||||
Info' DESC 'Forest trust information for a trusted domain object' EQUALITY
|
||||
caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
|
||||
olcAttributeTypes: {56}( 1.3.6.1.4.1.7165.2.1.79 NAME 'sambaTrustPosixOffset
|
||||
' DESC 'POSIX offset of a trust' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1
|
||||
466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {57}( 1.3.6.1.4.1.7165.2.1.80 NAME 'sambaSupportedEncrypt
|
||||
ionTypes' DESC 'Supported encryption types of a trust' EQUALITY integerMatc
|
||||
h SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcObjectClasses: {0}( 1.3.6.1.4.1.7165.2.2.6 NAME 'sambaSamAccount' DESC 'S
|
||||
amba 3.0 Auxilary SAM Account' SUP top AUXILIARY MUST ( uid $ sambaSID ) MA
|
||||
Y ( cn $ sambaLMPassword $ sambaNTPassword $ sambaPwdLastSet $ sambaLogonTi
|
||||
me $ sambaLogoffTime $ sambaKickoffTime $ sambaPwdCanChange $ sambaPwdMustC
|
||||
hange $ sambaAcctFlags $ displayName $ sambaHomePath $ sambaHomeDrive $ sam
|
||||
baLogonScript $ sambaProfilePath $ description $ sambaUserWorkstations $ sa
|
||||
mbaPrimaryGroupSID $ sambaDomainName $ sambaMungedDial $ sambaBadPasswordCo
|
||||
unt $ sambaBadPasswordTime $ sambaPasswordHistory $ sambaLogonHours ) )
|
||||
olcObjectClasses: {1}( 1.3.6.1.4.1.7165.2.2.4 NAME 'sambaGroupMapping' DESC
|
||||
'Samba Group Mapping' SUP top AUXILIARY MUST ( gidNumber $ sambaSID $ samba
|
||||
GroupType ) MAY ( displayName $ description $ sambaSIDList ) )
|
||||
olcObjectClasses: {2}( 1.3.6.1.4.1.7165.2.2.14 NAME 'sambaTrustPassword' DES
|
||||
C 'Samba Trust Password' SUP top STRUCTURAL MUST ( sambaDomainName $ sambaN
|
||||
TPassword $ sambaTrustFlags ) MAY ( sambaSID $ sambaPwdLastSet ) )
|
||||
olcObjectClasses: {3}( 1.3.6.1.4.1.7165.2.2.15 NAME 'sambaTrustedDomainPassw
|
||||
ord' DESC 'Samba Trusted Domain Password' SUP top STRUCTURAL MUST ( sambaDo
|
||||
mainName $ sambaSID $ sambaClearTextPassword $ sambaPwdLastSet ) MAY sambaP
|
||||
reviousClearTextPassword )
|
||||
olcObjectClasses: {4}( 1.3.6.1.4.1.7165.2.2.5 NAME 'sambaDomain' DESC 'Samba
|
||||
Domain Information' SUP top STRUCTURAL MUST ( sambaDomainName $ sambaSID )
|
||||
MAY ( sambaNextRid $ sambaNextGroupRid $ sambaNextUserRid $ sambaAlgorithm
|
||||
icRidBase $ sambaMinPwdLength $ sambaPwdHistoryLength $ sambaLogonToChgPwd
|
||||
$ sambaMaxPwdAge $ sambaMinPwdAge $ sambaLockoutDuration $ sambaLockoutObse
|
||||
rvationWindow $ sambaLockoutThreshold $ sambaForceLogoff $ sambaRefuseMachi
|
||||
nePwdChange ) )
|
||||
olcObjectClasses: {5}( 1.3.6.1.4.1.7165.2.2.7 NAME 'sambaUnixIdPool' DESC 'P
|
||||
ool for allocating UNIX uids/gids' SUP top AUXILIARY MUST ( uidNumber $ gid
|
||||
Number ) )
|
||||
olcObjectClasses: {6}( 1.3.6.1.4.1.7165.2.2.8 NAME 'sambaIdmapEntry' DESC 'M
|
||||
apping from a SID to an ID' SUP top AUXILIARY MUST sambaSID MAY ( uidNumber
|
||||
$ gidNumber ) )
|
||||
olcObjectClasses: {7}( 1.3.6.1.4.1.7165.2.2.9 NAME 'sambaSidEntry' DESC 'Str
|
||||
uctural Class for a SID' SUP top STRUCTURAL MUST sambaSID )
|
||||
olcObjectClasses: {8}( 1.3.6.1.4.1.7165.2.2.10 NAME 'sambaConfig' DESC 'Samb
|
||||
a Configuration Section' SUP top AUXILIARY MAY description )
|
||||
olcObjectClasses: {9}( 1.3.6.1.4.1.7165.2.2.11 NAME 'sambaShare' DESC 'Samba
|
||||
Share Section' SUP top STRUCTURAL MUST sambaShareName MAY description )
|
||||
olcObjectClasses: {10}( 1.3.6.1.4.1.7165.2.2.12 NAME 'sambaConfigOption' DES
|
||||
C 'Samba Configuration Option' SUP top STRUCTURAL MUST sambaOptionName MAY
|
||||
( sambaBoolOption $ sambaIntegerOption $ sambaStringOption $ sambaStringLis
|
||||
toption $ description ) )
|
||||
olcObjectClasses: {11}( 1.3.6.1.4.1.7165.2.2.16 NAME 'sambaTrustedDomain' DE
|
||||
SC 'Samba Trusted Domain Object' SUP top STRUCTURAL MUST cn MAY ( sambaTrus
|
||||
tType $ sambaTrustAttributes $ sambaTrustDirection $ sambaTrustPartner $ sa
|
||||
mbaFlatName $ sambaTrustAuthOutgoing $ sambaTrustAuthIncoming $ sambaSecuri
|
||||
tyIdentifier $ sambaTrustForestTrustInfo $ sambaTrustPosixOffset $ sambaSup
|
||||
portedEncryptionTypes ) )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 5c692e8a-7ddc-103b-8d45-8b35b6446851
|
||||
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
createTimestamp: 20210720192809Z
|
||||
entryCSN: 20210720192809.760287Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20210720192809Z
|
||||
@@ -1,18 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 6d98bb57
|
||||
dn: olcDatabase={-1}frontend
|
||||
objectClass: olcDatabaseConfig
|
||||
objectClass: olcFrontendConfig
|
||||
olcDatabase: {-1}frontend
|
||||
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=extern
|
||||
al,cn=auth manage by * break
|
||||
olcAccess: {1}to dn.exact="" by * read
|
||||
olcAccess: {2}to dn.base="cn=Subschema" by * read
|
||||
olcSizeLimit: 500
|
||||
structuralObjectClass: olcDatabaseConfig
|
||||
entryUUID: 5c22f244-7ddc-103b-9ebc-c740e015b81b
|
||||
creatorsName: cn=config
|
||||
createTimestamp: 20210720192809Z
|
||||
entryCSN: 20210720192809.299952Z#000000#000#000000
|
||||
modifiersName: cn=config
|
||||
modifyTimestamp: 20210720192809Z
|
||||
@@ -1,16 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 4503eefa
|
||||
dn: olcDatabase={0}config
|
||||
objectClass: olcDatabaseConfig
|
||||
olcDatabase: {0}config
|
||||
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=extern
|
||||
al,cn=auth manage by * break
|
||||
olcRootDN: cn=admin,cn=config
|
||||
structuralObjectClass: olcDatabaseConfig
|
||||
entryUUID: 5c248d0c-7ddc-103b-9ebd-c740e015b81b
|
||||
creatorsName: cn=config
|
||||
createTimestamp: 20210720192809Z
|
||||
olcRootPW:: e1NTSEF9eGNpMDV6aExUSXhxL3k0NHYyVHlBR2p0bCsxblV1UHc=
|
||||
entryCSN: 20210812002157.882687Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20210812002157Z
|
||||
@@ -1,31 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 506f4bf6
|
||||
dn: olcDatabase={1}mdb
|
||||
objectClass: olcDatabaseConfig
|
||||
objectClass: olcMdbConfig
|
||||
olcDatabase: {1}mdb
|
||||
olcDbDirectory: /var/lib/ldap
|
||||
olcSuffix: dc=glauth,dc=com
|
||||
olcLastMod: TRUE
|
||||
olcRootDN: cn=admin,dc=glauth,dc=com
|
||||
olcDbCheckpoint: 512 30
|
||||
olcDbMaxSize: 1073741824
|
||||
structuralObjectClass: olcMdbConfig
|
||||
entryUUID: 5c2cdd40-7ddc-103b-9ec4-c740e015b81b
|
||||
creatorsName: cn=admin,cn=config
|
||||
createTimestamp: 20210720192809Z
|
||||
olcDbIndex: uid eq
|
||||
olcDbIndex: mail eq
|
||||
olcDbIndex: memberOf eq
|
||||
olcDbIndex: entryCSN eq
|
||||
olcDbIndex: entryUUID eq
|
||||
olcDbIndex: objectClass eq
|
||||
olcAccess: {0}to attrs=userPassword,shadowLastChange by self write by dn="cn
|
||||
=serviceuser,cn=svcaccts,ou=users,dc=glauth,dc=com" write by anonymous auth
|
||||
by * none
|
||||
olcAccess: {1}to * by self write by dn="cn=serviceuser,cn=svcaccts,ou=users,
|
||||
dc=glauth,dc=com" write by users read by * none
|
||||
olcRootPW:: e1NTSEF9TzNabzVITEhzQXU1dkg5a0txNTlEVFhGclpPVDBPQUg=
|
||||
entryCSN: 20210812002157.895969Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20210812002157Z
|
||||
@@ -1,18 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 5273ae52
|
||||
dn: olcOverlay={0}memberof
|
||||
objectClass: olcOverlayConfig
|
||||
objectClass: olcMemberOf
|
||||
olcOverlay: {0}memberof
|
||||
olcMemberOfDangling: ignore
|
||||
olcMemberOfRefInt: TRUE
|
||||
olcMemberOfGroupOC: groupOfUniqueNames
|
||||
olcMemberOfMemberAD: uniqueMember
|
||||
olcMemberOfMemberOfAD: memberOf
|
||||
structuralObjectClass: olcMemberOf
|
||||
entryUUID: 5c74c556-7ddc-103b-8d46-8b35b6446851
|
||||
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
createTimestamp: 20210720192809Z
|
||||
entryCSN: 20210720192809.836237Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20210720192809Z
|
||||
@@ -1,18 +0,0 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 a7700674
|
||||
dn: olcOverlay={1}refint
|
||||
objectClass: olcOverlayConfig
|
||||
objectClass: olcRefintConfig
|
||||
olcOverlay: {1}refint
|
||||
olcRefintAttribute: owner
|
||||
olcRefintAttribute: manager
|
||||
olcRefintAttribute: uniqueMember
|
||||
olcRefintAttribute: member
|
||||
olcRefintAttribute: memberOf
|
||||
structuralObjectClass: olcRefintConfig
|
||||
entryUUID: 5c78d98e-7ddc-103b-8d47-8b35b6446851
|
||||
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
createTimestamp: 20210720192809Z
|
||||
entryCSN: 20210720192809.862968Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20210720192809Z
|
||||
@@ -1,4 +0,0 @@
|
||||
export PREVIOUS_LDAP_TLS_CA_CRT_PATH=/container/service/slapd/assets/certs/ca.crt
|
||||
export PREVIOUS_LDAP_TLS_CRT_PATH=/container/service/slapd/assets/certs/ldap.crt
|
||||
export PREVIOUS_LDAP_TLS_KEY_PATH=/container/service/slapd/assets/certs/ldap.key
|
||||
export PREVIOUS_LDAP_TLS_DH_PARAM_PATH=/container/service/slapd/assets/certs/dhparam.pem
|
||||
Binary file not shown.
Binary file not shown.
-5
File diff suppressed because one or more lines are too long
Vendored
-5
File diff suppressed because one or more lines are too long
@@ -1,47 +0,0 @@
|
||||
|
||||
.jumbotron {
|
||||
color: #c0392b;
|
||||
background-color: #ecf0f1;
|
||||
}
|
||||
|
||||
#statspanel {
|
||||
color: #34495e;
|
||||
}
|
||||
|
||||
#statspanel .metric {
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
#stats {
|
||||
color: #9b59b6;
|
||||
padding: 8px;
|
||||
}
|
||||
#servers {
|
||||
color: #9b59b6;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #2980b9;
|
||||
}
|
||||
|
||||
#tail th {
|
||||
color: #7f8c8d;
|
||||
}
|
||||
#tail td {
|
||||
padding: 4px;
|
||||
color: #34495e;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#tail td.op {
|
||||
color: #8e44ad;
|
||||
font-weight: bold;
|
||||
}
|
||||
#tail td.op.bind {
|
||||
color: #16a085;
|
||||
}
|
||||
#tail td.op.search {
|
||||
color: #1abc9c;
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package assets
|
||||
|
||||
import "embed"
|
||||
|
||||
// Content is the embedded assets.
|
||||
//go:embed *.tgz js/* css/* *.html
|
||||
var Content embed.FS
|
||||
Binary file not shown.
@@ -1,101 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>GLAuth Status</title>
|
||||
|
||||
<link rel="stylesheet" href="assets/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="assets/css/bootstrap-theme.min.css">
|
||||
<link rel="stylesheet" href="assets/css/glauth.css">
|
||||
|
||||
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body role="document">
|
||||
|
||||
<div class="container theme-showcase" role="main">
|
||||
|
||||
<!-- Main jumbotron for a primary marketing message or call to action -->
|
||||
<div class="jumbotron">
|
||||
<div id="statspanel" class="panel panel-default pull-right">
|
||||
</div>
|
||||
<h1>GLAuth</h1>
|
||||
<div id="headerpanel"></div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<h2>frontend stats</h2>
|
||||
<div id="stats" class="jumbotron">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<h2>backend (LDAP) servers</h2>
|
||||
<div id="servers" class="jumbotron">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> <!-- /container -->
|
||||
|
||||
<script id="header-messages-template" type="text/x-handlebars-template">
|
||||
{{#each header.messages}}
|
||||
<span>{{{message}}}</span><br>
|
||||
{{/each}}
|
||||
</script>
|
||||
|
||||
<script id="statspanel-template" type="text/x-handlebars-template">
|
||||
<div class="panel-body">
|
||||
<span class="metric">proxy.version:</span> {{proxy.version}}<br>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script id="stats-template" type="text/x-handlebars-template">
|
||||
<span class="metric">proxy_frontend.bind_reqs:</span> {{proxy_frontend.bind_reqs}}<br>
|
||||
<span class="metric">proxy_frontend.bind_successes:</span> {{proxy_frontend.bind_successes}}<br>
|
||||
<span class="metric">proxy_frontend.bind_errors:</span> {{proxy_frontend.bind_errors}}<br>
|
||||
<span class="metric">proxy_frontend.search_reqs:</span> {{proxy_frontend.search_reqs}}<br>
|
||||
<span class="metric">proxy_frontend.search_successes:</span> {{proxy_frontend.search_successes}}<br>
|
||||
<span class="metric">proxy_frontend.search_errors:</span> {{proxy_frontend.search_errors}}<br>
|
||||
<span class="metric">proxy_frontend.closes:</span> {{proxy_frontend.closes}}<br>
|
||||
</script>
|
||||
|
||||
<script id="servers-template" type="text/x-handlebars-template">
|
||||
{{#each proxy_backend.servers}}
|
||||
<span class="metric">{{Scheme}}://{{Hostname}}:{{Port}}</span> {{Ping}} {{Status}} <br>
|
||||
{{/each}}
|
||||
</script>
|
||||
|
||||
<script id="tail-template" type="text/x-handlebars-template">
|
||||
<tbody>
|
||||
{{#each this}}
|
||||
<tr class="">
|
||||
<td class="op {{Op}}">{{Op}}</td>
|
||||
<td class="op {{Op}}">{{BindDN}}</td>
|
||||
<td class="op {{Op}}">{{SearchReq.Filter}}</td>
|
||||
<td class="">{{ResultCode}}</td>
|
||||
<td class="">{{SearchResults}}</td>
|
||||
<td class="">{{ClientIP}}</td>
|
||||
<td class="">{{dateRelative When}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
<tbody>
|
||||
</script>
|
||||
|
||||
<script src="assets/js/jquery.min.js"></script>
|
||||
<script src="assets/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/handlebars.min.js"></script>
|
||||
<script src="assets/js/moment-with-locales.min.js"></script>
|
||||
<script src="assets/js/glauth.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
-6
File diff suppressed because one or more lines are too long
@@ -1,74 +0,0 @@
|
||||
var interval = 2000;
|
||||
|
||||
var internalsEnabled = false;
|
||||
|
||||
$(function() {
|
||||
$.get("/internals", function() {
|
||||
internalsEnabled = true;
|
||||
})
|
||||
.always(function() {
|
||||
updateMetrics();
|
||||
//updateTail();
|
||||
})
|
||||
});
|
||||
|
||||
var tailId = 0;
|
||||
|
||||
function updateMetrics() {
|
||||
var metrics = $.getJSON( "/debug/vars", function(d) {
|
||||
var template = Handlebars.compile( $("#header-messages-template").html() );
|
||||
var messages = [];
|
||||
if(internalsEnabled) {
|
||||
messages.push({"message": "Also available: <a href='/internals'>Internals: heap, cache, objects, etc.</a>"});
|
||||
}
|
||||
var html = template({
|
||||
"header": {
|
||||
"messages": messages
|
||||
}
|
||||
});
|
||||
$("#headerpanel").html(html);
|
||||
var template = Handlebars.compile( $("#statspanel-template").html() );
|
||||
var html = template(d);
|
||||
$("#statspanel").html(html);
|
||||
var template = Handlebars.compile( $("#stats-template").html() );
|
||||
var html = template(d);
|
||||
$("#stats").html(html);
|
||||
var template = Handlebars.compile( $("#servers-template").html() );
|
||||
var html = template(d);
|
||||
$("#servers").html(html);
|
||||
})
|
||||
.fail(function() {
|
||||
$("#statspanel").html("Error!");
|
||||
})
|
||||
.always(function() {
|
||||
setTimeout(updateMetrics,interval);
|
||||
});
|
||||
}
|
||||
function updateTail() {
|
||||
var tail = $.getJSON( "/tail/" + tailId , function(d) {
|
||||
d.forEach(function(e) {
|
||||
if (e.Id > tailId) {
|
||||
tailId = e.Id;
|
||||
}
|
||||
});
|
||||
var template = Handlebars.compile( $("#tail-template").html() );
|
||||
var html = template(d);
|
||||
$(html).hide().prependTo("#tail").fadeIn(2000);
|
||||
})
|
||||
.fail(function() {
|
||||
$("#tail").html("Error!");
|
||||
})
|
||||
.always(function() {
|
||||
setTimeout(updateTail,interval);
|
||||
});
|
||||
}
|
||||
|
||||
Handlebars.registerHelper('dateRelative', function(context, block) {
|
||||
if (window.moment) {
|
||||
//return moment(context).fromNow();
|
||||
return moment(context).format();
|
||||
}else{
|
||||
return context;
|
||||
};
|
||||
});
|
||||
|
||||
Vendored
-28
File diff suppressed because one or more lines are too long
Vendored
-4
File diff suppressed because one or more lines are too long
-10
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1,114 +0,0 @@
|
||||
package config
|
||||
|
||||
import "time"
|
||||
|
||||
// config file
|
||||
type Backend struct {
|
||||
BaseDN string
|
||||
Datastore string
|
||||
Insecure bool // For LDAP and owncloud backend only
|
||||
Servers []string // For LDAP and owncloud backend only
|
||||
NameFormat string
|
||||
GroupFormat string
|
||||
SSHKeyAttr string
|
||||
UseGraphAPI bool // For ownCloud backend only
|
||||
Plugin string // Path to plugin library, for plugin backend only
|
||||
PluginHandler string // Name of plugin's main handler function
|
||||
Database string // For Database backends only
|
||||
AnonymousDSE bool // For Config and Database backends only
|
||||
}
|
||||
type Helper struct {
|
||||
Enabled bool
|
||||
BaseDN string
|
||||
Datastore string
|
||||
Plugin string // Path to plugin library, for plugin backend only
|
||||
PluginHandler string // Name of plugin's main handler function
|
||||
Database string // For MySQL backend only TODO REname to match plugin
|
||||
}
|
||||
type Frontend struct {
|
||||
AllowedBaseDNs []string // For LDAP backend only
|
||||
Listen string
|
||||
Cert string
|
||||
Key string
|
||||
TLS bool
|
||||
}
|
||||
type LDAP struct {
|
||||
Enabled bool
|
||||
Listen string
|
||||
}
|
||||
type LDAPS struct {
|
||||
Enabled bool
|
||||
Listen string
|
||||
Cert string
|
||||
Key string
|
||||
}
|
||||
type API struct {
|
||||
Cert string
|
||||
Enabled bool
|
||||
Internals bool
|
||||
Key string
|
||||
Listen string
|
||||
SecretToken string
|
||||
TLS bool
|
||||
}
|
||||
type Behaviors struct {
|
||||
IgnoreCapabilities bool
|
||||
LimitFailedBinds bool
|
||||
NumberOfFailedBinds int
|
||||
PeriodOfFailedBinds time.Duration
|
||||
BlockFailedBindsFor time.Duration
|
||||
PruneSourceTableEvery time.Duration
|
||||
PruneSourcesOlderThan time.Duration
|
||||
}
|
||||
type Capability struct {
|
||||
Action string
|
||||
Object string
|
||||
}
|
||||
type User struct {
|
||||
Name string
|
||||
OtherGroups []int
|
||||
PassSHA256 string
|
||||
PassBcrypt string
|
||||
PassAppSHA256 []string
|
||||
PassAppBcrypt []string
|
||||
PrimaryGroup int
|
||||
Capabilities []Capability
|
||||
SSHKeys []string
|
||||
OTPSecret string
|
||||
Yubikey string
|
||||
Disabled bool
|
||||
UnixID int // TODO: remove after deprecating UnixID on User and Group
|
||||
UIDNumber int
|
||||
Mail string
|
||||
LoginShell string
|
||||
GivenName string
|
||||
SN string
|
||||
Homedir string
|
||||
}
|
||||
type Group struct {
|
||||
Name string
|
||||
UnixID int // TODO: remove after deprecating UnixID on User and Group
|
||||
GIDNumber int
|
||||
IncludeGroups []int
|
||||
}
|
||||
type Config struct {
|
||||
API API
|
||||
Backend Backend // Deprecated
|
||||
Backends []Backend
|
||||
Helper Helper
|
||||
Behaviors Behaviors
|
||||
Debug bool
|
||||
WatchConfig bool
|
||||
YubikeyClientID string
|
||||
YubikeySecret string
|
||||
Frontend Frontend
|
||||
LDAP LDAP
|
||||
LDAPS LDAPS
|
||||
Groups []Group
|
||||
Syslog bool
|
||||
Users []User
|
||||
ConfigFile string
|
||||
AwsAccessKeyId string
|
||||
AwsSecretAccessKey string
|
||||
AwsRegion string
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/glauth/glauth/pkg/config"
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Logger logr.Logger
|
||||
Config *config.API
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(val logr.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.API) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
|
||||
// Context provides a function to set the context option.
|
||||
func Context(val context.Context) Option {
|
||||
return func(o *Options) {
|
||||
o.Context = val
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
|
||||
"github.com/glauth/glauth/pkg/assets"
|
||||
)
|
||||
|
||||
// RunAPI provides a basic REST API
|
||||
func RunAPI(opts ...Option) {
|
||||
options := newOptions(opts...)
|
||||
log := options.Logger
|
||||
cfg := options.Config
|
||||
|
||||
register(http.DefaultServeMux, log)
|
||||
if cfg.TLS {
|
||||
log.V(3).Info("Starting HTTPS server", "address", cfg.Listen)
|
||||
err := http.ListenAndServeTLS(cfg.Listen, cfg.Cert, cfg.Key, nil)
|
||||
if err != nil {
|
||||
log.Error(err, "Error starting HTTPS server")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
log.V(3).Info("Starting HTTP server", "address", cfg.Listen)
|
||||
err := http.ListenAndServe(cfg.Listen, nil)
|
||||
if err != nil {
|
||||
log.Error(err, "Error starting HTTP server")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func register(mux *http.ServeMux, log logr.Logger) {
|
||||
fs := http.FileServer(http.FS(assets.Content))
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
log.V(6).Info("Web", "path", r.URL.Path)
|
||||
if r.URL.Path != "/" {
|
||||
log.V(6).Info("Web 404")
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
fs.ServeHTTP(w, r)
|
||||
})
|
||||
mux.Handle("/assets/", http.StripPrefix("/assets/", fs))
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
|
||||
"github.com/glauth/glauth/pkg/assets"
|
||||
)
|
||||
|
||||
// TestAPI is a small test to make sure that behavior didn't change for the
|
||||
// worse when switching over to the go1.16+ "embed" package from go-bindata.
|
||||
func TestAPI(t *testing.T) {
|
||||
tt := []struct {
|
||||
Name string
|
||||
Path string
|
||||
Check func(testing.TB, *http.Response)
|
||||
}{
|
||||
{
|
||||
Name: "Root",
|
||||
Path: "/",
|
||||
Check: func(t testing.TB, res *http.Response) {
|
||||
if res.StatusCode != http.StatusOK {
|
||||
t.Errorf("unexpected response: %v", res.Status)
|
||||
return
|
||||
}
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, res.Body); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if got, want := h.Sum(nil), mkDigest(t, "index.html"); !bytes.Equal(got, want) {
|
||||
t.Errorf("bad digest: got: %x, want %x", got, want)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "NotFound",
|
||||
Path: "/glauth.js",
|
||||
Check: func(t testing.TB, res *http.Response) {
|
||||
if res.StatusCode == http.StatusNotFound {
|
||||
return
|
||||
}
|
||||
t.Errorf("unexpected response: %v", res.Status)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Content-Type",
|
||||
Path: "/assets/glauth.js",
|
||||
Check: func(t testing.TB, res *http.Response) {
|
||||
if res.StatusCode != http.StatusOK {
|
||||
t.Errorf("unexpected response: %v", res.Status)
|
||||
}
|
||||
if got, want := res.Header.Get("Content-Type"), `application/javascript`; got != want {
|
||||
t.Errorf("bad content-type: got: %q, want %q", got, want)
|
||||
}
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, res.Body); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if got, want := h.Sum(nil), mkDigest(t, "glauth.js"); !bytes.Equal(got, want) {
|
||||
t.Errorf("bad digest: got: %x, want %x", got, want)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Content-Type",
|
||||
Path: "/assets/glauth.css",
|
||||
Check: func(t testing.TB, res *http.Response) {
|
||||
if res.StatusCode != http.StatusOK {
|
||||
t.Errorf("unexpected response: %v", res.Status)
|
||||
}
|
||||
// This isn't exactly the same, it adds a charset argument.
|
||||
if got, want := res.Header.Get("Content-Type"), `text/css; charset=utf-8`; got != want {
|
||||
t.Errorf("bad content-type: got: %q, want %q", got, want)
|
||||
}
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, res.Body); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if got, want := h.Sum(nil), mkDigest(t, "glauth.css"); !bytes.Equal(got, want) {
|
||||
t.Errorf("bad digest: got: %x, want %x", got, want)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
register(mux, logr.Discard())
|
||||
srv := httptest.NewServer(mux)
|
||||
defer srv.Close()
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+tc.Path, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res, err := srv.Client().Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
tc.Check(t, res)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func mkDigest(t testing.TB, path string) []byte {
|
||||
f, err := assets.Content.Open(path)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer f.Close()
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
return h.Sum(nil)
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
// Package gologgingr implements github.com/go-logr/logr.Logger in terms of
|
||||
// github.com/op/go-logging.
|
||||
package gologgingr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
|
||||
// The global verbosity level. See SetVerbosity().
|
||||
var globalVerbosity int = 0
|
||||
|
||||
// SetVerbosity sets the global level against which all info logs will be
|
||||
// compared. If this is greater than or equal to the "V" of the logger, the
|
||||
// message will be logged. A higher value here means more logs will be written.
|
||||
// The previous verbosity value is returned. This is not concurrent-safe -
|
||||
// callers must be sure to call it from only one goroutine.
|
||||
func SetVerbosity(v int) int {
|
||||
old := globalVerbosity
|
||||
globalVerbosity = v
|
||||
return old
|
||||
}
|
||||
|
||||
// New returns a logr.Logger which is implemented by Go's standard
|
||||
// log package, or something like it. See New for details.
|
||||
func New(opts ...Option) logr.Logger {
|
||||
options := newOptions(opts...)
|
||||
|
||||
l := logger{
|
||||
llog: options.Logger,
|
||||
level: 0,
|
||||
prefix: "",
|
||||
values: nil,
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
// LeveledLogger is the subset of the Go stdlib log.Logger API that is needed for
|
||||
// this adapter.
|
||||
type LeveledLogger interface {
|
||||
Fatal(args ...interface{})
|
||||
Error(args ...interface{})
|
||||
Warning(args ...interface{})
|
||||
Notice(args ...interface{})
|
||||
Info(args ...interface{})
|
||||
Debug(args ...interface{})
|
||||
}
|
||||
|
||||
type logger struct {
|
||||
llog LeveledLogger
|
||||
level int
|
||||
prefix string
|
||||
values []interface{}
|
||||
depth int
|
||||
}
|
||||
|
||||
func (l logger) clone() logger {
|
||||
out := l
|
||||
l.values = copySlice(l.values)
|
||||
return out
|
||||
}
|
||||
|
||||
func copySlice(in []interface{}) []interface{} {
|
||||
out := make([]interface{}, len(in))
|
||||
copy(out, in)
|
||||
return out
|
||||
}
|
||||
|
||||
// Magic string for intermediate frames that we should ignore.
|
||||
const autogeneratedFrameName = "<autogenerated>"
|
||||
|
||||
type kvPair struct {
|
||||
key string
|
||||
val interface{}
|
||||
}
|
||||
|
||||
func flatten(kvList ...interface{}) string {
|
||||
keys := make([]string, 0, len(kvList))
|
||||
vals := make(map[string]interface{}, len(kvList))
|
||||
for i := 0; i < len(kvList); i += 2 {
|
||||
k, ok := kvList[i].(string)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("key is not a string: %s", pretty(kvList[i])))
|
||||
}
|
||||
var v interface{}
|
||||
if i+1 < len(kvList) {
|
||||
v = kvList[i+1]
|
||||
}
|
||||
keys = append(keys, k)
|
||||
vals[k] = v
|
||||
}
|
||||
sort.Strings(keys)
|
||||
buf := bytes.Buffer{}
|
||||
for i, k := range keys {
|
||||
v := vals[k]
|
||||
if i > 0 {
|
||||
buf.WriteRune(' ')
|
||||
}
|
||||
buf.WriteString(pretty(k))
|
||||
buf.WriteString("=")
|
||||
buf.WriteString(pretty(v))
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func pretty(value interface{}) string {
|
||||
jb, _ := json.Marshal(value)
|
||||
return string(jb)
|
||||
}
|
||||
|
||||
func (l logger) Info(msg string, kvList ...interface{}) {
|
||||
if l.Enabled() {
|
||||
lvlStr := flatten("level", l.level)
|
||||
msgStr := flatten("msg", msg)
|
||||
fixedStr := flatten(l.values...)
|
||||
userStr := flatten(kvList...)
|
||||
l.output(fmt.Sprintln(l.prefix, lvlStr, msgStr, fixedStr, userStr))
|
||||
}
|
||||
}
|
||||
|
||||
func (l logger) Enabled() bool {
|
||||
return globalVerbosity >= l.level
|
||||
}
|
||||
|
||||
func (l logger) Error(err error, msg string, kvList ...interface{}) {
|
||||
msgStr := flatten("msg", msg)
|
||||
var loggableErr interface{}
|
||||
if err != nil {
|
||||
loggableErr = err.Error()
|
||||
}
|
||||
errStr := flatten("error", loggableErr)
|
||||
fixedStr := flatten(l.values...)
|
||||
userStr := flatten(kvList...)
|
||||
l.output(fmt.Sprintln(l.prefix, errStr, msgStr, fixedStr, userStr))
|
||||
}
|
||||
|
||||
func (l logger) output(s string) {
|
||||
switch l.level {
|
||||
case 0:
|
||||
l.llog.Error(s)
|
||||
case 1:
|
||||
l.llog.Error(s)
|
||||
case 2:
|
||||
l.llog.Warning(s)
|
||||
case 3:
|
||||
l.llog.Notice(s)
|
||||
case 4:
|
||||
l.llog.Info(s)
|
||||
case 5:
|
||||
l.llog.Info(s)
|
||||
default:
|
||||
l.llog.Debug(s)
|
||||
}
|
||||
}
|
||||
|
||||
func (l logger) V(level int) logr.InfoLogger {
|
||||
new := l.clone()
|
||||
new.level = level
|
||||
return new
|
||||
}
|
||||
|
||||
// WithName returns a new logr.Logger with the specified name appended. stdr
|
||||
// uses '/' characters to separate name elements. Callers should not pass '/'
|
||||
// in the provided name string, but this library does not actually enforce that.
|
||||
func (l logger) WithName(name string) logr.Logger {
|
||||
new := l.clone()
|
||||
if len(l.prefix) > 0 {
|
||||
new.prefix = l.prefix + "/"
|
||||
}
|
||||
new.prefix += name
|
||||
return new
|
||||
}
|
||||
|
||||
func (l logger) WithValues(kvList ...interface{}) logr.Logger {
|
||||
new := l.clone()
|
||||
new.values = append(new.values, kvList...)
|
||||
return new
|
||||
}
|
||||
|
||||
var _ logr.Logger = logger{}
|
||||
var _ logr.InfoLogger = logger{}
|
||||
@@ -1,27 +0,0 @@
|
||||
package gologgingr
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Logger LeveledLogger
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(val LeveledLogger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
@@ -1,325 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/GeertJohan/yubigo"
|
||||
"github.com/glauth/glauth/pkg/config"
|
||||
"github.com/glauth/glauth/pkg/stats"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/nmcclain/ldap"
|
||||
)
|
||||
|
||||
type configHandler struct {
|
||||
backend config.Backend
|
||||
log logr.Logger
|
||||
cfg *config.Config
|
||||
yubikeyAuth *yubigo.YubiAuth
|
||||
ldohelper LDAPOpsHelper
|
||||
attmatcher *regexp.Regexp
|
||||
}
|
||||
|
||||
// NewConfigHandler creates a new config backed handler
|
||||
func NewConfigHandler(opts ...Option) Handler {
|
||||
options := newOptions(opts...)
|
||||
|
||||
handler := configHandler{
|
||||
backend: options.Backend,
|
||||
log: options.Logger,
|
||||
cfg: options.Config, // TODO only used to access Users and Groups, move that to dedicated options
|
||||
yubikeyAuth: options.YubiAuth,
|
||||
ldohelper: options.LDAPHelper,
|
||||
attmatcher: configattributematcher,
|
||||
}
|
||||
return handler
|
||||
}
|
||||
|
||||
func (h configHandler) GetBackend() config.Backend {
|
||||
return h.backend
|
||||
}
|
||||
func (h configHandler) GetLog() logr.Logger {
|
||||
return h.log
|
||||
}
|
||||
func (h configHandler) GetCfg() *config.Config {
|
||||
return h.cfg
|
||||
}
|
||||
func (h configHandler) GetYubikeyAuth() *yubigo.YubiAuth {
|
||||
return h.yubikeyAuth
|
||||
}
|
||||
|
||||
// Bind implements a bind request against the config file
|
||||
func (h configHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resultCode ldap.LDAPResultCode, err error) {
|
||||
return h.ldohelper.Bind(h, bindDN, bindSimplePw, conn)
|
||||
}
|
||||
|
||||
// Search implements a search request against the config file
|
||||
func (h configHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (result ldap.ServerSearchResult, err error) {
|
||||
return h.ldohelper.Search(h, bindDN, searchReq, conn)
|
||||
}
|
||||
|
||||
// Add is not supported for a static config file
|
||||
func (h configHandler) Add(boundDN string, req ldap.AddRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
// Modify is not supported for a static config file
|
||||
func (h configHandler) Modify(boundDN string, req ldap.ModifyRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
// Delete is not supported for a static config file
|
||||
func (h configHandler) Delete(boundDN string, deleteDN string, conn net.Conn) (result ldap.LDAPResultCode, err error) {
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
func (h configHandler) FindUser(userName string, searchByUPN bool) (f bool, u config.User, err error) {
|
||||
user := config.User{}
|
||||
found := false
|
||||
|
||||
for _, u := range h.cfg.Users {
|
||||
if searchByUPN {
|
||||
if strings.EqualFold(u.Mail, userName) {
|
||||
found = true
|
||||
user = u
|
||||
}
|
||||
} else {
|
||||
if strings.EqualFold(u.Name, userName) {
|
||||
found = true
|
||||
user = u
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found, user, nil
|
||||
}
|
||||
|
||||
func (h configHandler) FindGroup(groupName string) (f bool, g config.Group, err error) {
|
||||
// TODO Does g get erased, and above does u get erased?
|
||||
// TODO and what about f?
|
||||
group := config.Group{}
|
||||
found := false
|
||||
for _, g := range h.cfg.Groups {
|
||||
if strings.EqualFold(g.Name, groupName) {
|
||||
found = true
|
||||
group = g
|
||||
}
|
||||
}
|
||||
return found, group, nil
|
||||
}
|
||||
|
||||
func (h configHandler) FindPosixAccounts() (entrylist []*ldap.Entry, err error) {
|
||||
entries := []*ldap.Entry{}
|
||||
|
||||
for _, u := range h.cfg.Users {
|
||||
attrs := []*ldap.EntryAttribute{}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "cn", Values: []string{u.Name}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "uid", Values: []string{u.Name}})
|
||||
|
||||
if len(u.GivenName) > 0 {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "givenName", Values: []string{u.GivenName}})
|
||||
}
|
||||
|
||||
if len(u.SN) > 0 {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "sn", Values: []string{u.SN}})
|
||||
}
|
||||
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "ou", Values: []string{h.getGroupName(u.PrimaryGroup)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "uidNumber", Values: []string{fmt.Sprintf("%d", u.UIDNumber)}})
|
||||
|
||||
if u.Disabled {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "accountStatus", Values: []string{"inactive"}})
|
||||
} else {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "accountStatus", Values: []string{"active"}})
|
||||
}
|
||||
|
||||
if len(u.Mail) > 0 {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "mail", Values: []string{u.Mail}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "userPrincipalName", Values: []string{u.Mail}})
|
||||
}
|
||||
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixAccount", "shadowAccount"}})
|
||||
|
||||
if len(u.LoginShell) > 0 {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "loginShell", Values: []string{u.LoginShell}})
|
||||
} else {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "loginShell", Values: []string{"/bin/bash"}})
|
||||
}
|
||||
|
||||
if len(u.Homedir) > 0 {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "homeDirectory", Values: []string{u.Homedir}})
|
||||
} else {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "homeDirectory", Values: []string{"/home/" + u.Name}})
|
||||
}
|
||||
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "description", Values: []string{fmt.Sprintf("%s", u.Name)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "gecos", Values: []string{fmt.Sprintf("%s", u.Name)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "gidNumber", Values: []string{fmt.Sprintf("%d", u.PrimaryGroup)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberOf", Values: h.getGroupDNs(append(u.OtherGroups, u.PrimaryGroup))})
|
||||
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowExpire", Values: []string{"-1"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowFlag", Values: []string{"134538308"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowInactive", Values: []string{"-1"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowLastChange", Values: []string{"11000"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowMax", Values: []string{"99999"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowMin", Values: []string{"-1"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "shadowWarning", Values: []string{"7"}})
|
||||
|
||||
if len(u.SSHKeys) > 0 {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: h.backend.SSHKeyAttr, Values: u.SSHKeys})
|
||||
}
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(u.PrimaryGroup), h.backend.BaseDN)
|
||||
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func (h configHandler) FindPosixGroups() (entrylist []*ldap.Entry, err error) {
|
||||
entries := []*ldap.Entry{}
|
||||
|
||||
for _, g := range h.cfg.Groups {
|
||||
attrs := []*ldap.EntryAttribute{}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "cn", Values: []string{g.Name}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "uid", Values: []string{g.Name}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "description", Values: []string{fmt.Sprintf("%s", g.Name)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "gidNumber", Values: []string{fmt.Sprintf("%d", g.GIDNumber)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixGroup"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "uniqueMember", Values: h.getGroupMembers(g.GIDNumber)})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberUid", Values: h.getGroupMemberIDs(g.GIDNumber)})
|
||||
dn := fmt.Sprintf("cn=%s,%s=groups,%s", g.Name, h.backend.GroupFormat, h.backend.BaseDN)
|
||||
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
// Close does not actually close anything, because the config data is kept in memory
|
||||
func (h configHandler) Close(boundDn string, conn net.Conn) error {
|
||||
stats.Frontend.Add("closes", 1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h configHandler) getGroupMembers(gid int) []string {
|
||||
members := make(map[string]bool)
|
||||
for _, u := range h.cfg.Users {
|
||||
if u.PrimaryGroup == gid {
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(u.PrimaryGroup), h.backend.BaseDN)
|
||||
members[dn] = true
|
||||
} else {
|
||||
for _, othergid := range u.OtherGroups {
|
||||
if othergid == gid {
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormat, u.Name, h.backend.GroupFormat, h.getGroupName(u.PrimaryGroup), h.backend.BaseDN)
|
||||
members[dn] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, g := range h.cfg.Groups {
|
||||
if gid == g.GIDNumber {
|
||||
for _, includegroupid := range g.IncludeGroups {
|
||||
if includegroupid != gid {
|
||||
includegroupmembers := h.getGroupMembers(includegroupid)
|
||||
|
||||
for _, includegroupmember := range includegroupmembers {
|
||||
members[includegroupmember] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m := []string{}
|
||||
for k := range members {
|
||||
m = append(m, k)
|
||||
}
|
||||
|
||||
sort.Strings(m)
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (h configHandler) getGroupMemberIDs(gid int) []string {
|
||||
members := make(map[string]bool)
|
||||
for _, u := range h.cfg.Users {
|
||||
if u.PrimaryGroup == gid {
|
||||
members[u.Name] = true
|
||||
} else {
|
||||
for _, othergid := range u.OtherGroups {
|
||||
if othergid == gid {
|
||||
members[u.Name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, g := range h.cfg.Groups {
|
||||
if gid == g.GIDNumber {
|
||||
for _, includegroupid := range g.IncludeGroups {
|
||||
if includegroupid == gid {
|
||||
h.log.V(2).Info("Ignoring myself as included group", "groupid", includegroupid)
|
||||
} else {
|
||||
includegroupmemberids := h.getGroupMemberIDs(includegroupid)
|
||||
|
||||
for _, includegroupmemberid := range includegroupmemberids {
|
||||
members[includegroupmemberid] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m := []string{}
|
||||
for k := range members {
|
||||
m = append(m, k)
|
||||
}
|
||||
|
||||
sort.Strings(m)
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// Converts an array of GUIDs into an array of DNs
|
||||
func (h configHandler) getGroupDNs(gids []int) []string {
|
||||
groups := make(map[string]bool)
|
||||
for _, gid := range gids {
|
||||
for _, g := range h.cfg.Groups {
|
||||
if g.GIDNumber == gid {
|
||||
dn := fmt.Sprintf("cn=%s,%s=groups,%s", g.Name, h.backend.GroupFormat, h.backend.BaseDN)
|
||||
groups[dn] = true
|
||||
}
|
||||
|
||||
for _, includegroupid := range g.IncludeGroups {
|
||||
if includegroupid == gid && g.GIDNumber != gid {
|
||||
includegroupdns := h.getGroupDNs([]int{g.GIDNumber})
|
||||
|
||||
for _, includegroupdn := range includegroupdns {
|
||||
groups[includegroupdn] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g := []string{}
|
||||
for k := range groups {
|
||||
g = append(g, k)
|
||||
}
|
||||
|
||||
sort.Strings(g)
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
func (h configHandler) getGroupName(gid int) string {
|
||||
for _, g := range h.cfg.Groups {
|
||||
if g.GIDNumber == gid {
|
||||
return g.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/glauth/glauth/pkg/config"
|
||||
"github.com/nmcclain/ldap"
|
||||
)
|
||||
|
||||
type HelperMaker interface {
|
||||
FindUser(userName string, searchByUPN bool) (bool, config.User, error)
|
||||
FindGroup(groupName string) (bool, config.Group, error)
|
||||
}
|
||||
|
||||
// Handler is the common interface for all datastores
|
||||
type Handler interface {
|
||||
// read support
|
||||
ldap.Binder
|
||||
ldap.Searcher
|
||||
ldap.Closer
|
||||
|
||||
// write support
|
||||
ldap.Adder
|
||||
ldap.Modifier // Note: modifying eg the uid or cn might change the dn because the hierarchy is determined by the backend
|
||||
ldap.Deleter
|
||||
|
||||
// helper
|
||||
HelperMaker
|
||||
}
|
||||
|
||||
// TODO When I grow up, I want to handle pointers same as I would in C
|
||||
// and not need a counter because I would not allocate statically
|
||||
// but use idiomatic slicing instead
|
||||
type HandlerWrapper struct {
|
||||
Handlers []Handler
|
||||
Count *int
|
||||
}
|
||||
@@ -1,495 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/glauth/glauth/pkg/config"
|
||||
"github.com/glauth/glauth/pkg/stats"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/nmcclain/ldap"
|
||||
"github.com/pquerna/otp/totp"
|
||||
)
|
||||
|
||||
// global matcher
|
||||
var ldapattributematcher = regexp.MustCompile(`(?i)(?P<attribute>[a-zA-Z0-9]+)\s*=\s*(?P<value>.*)`)
|
||||
|
||||
type ldapHandler struct {
|
||||
backend config.Backend
|
||||
handlers HandlerWrapper
|
||||
doPing chan bool
|
||||
log logr.Logger
|
||||
lock *sync.Mutex // for sessions and servers
|
||||
sessions map[string]ldapSession
|
||||
servers []ldapBackend
|
||||
helper Handler
|
||||
attm *regexp.Regexp
|
||||
}
|
||||
|
||||
// global lock for ldapHandler sessions & servers manipulation
|
||||
var ldaplock sync.Mutex
|
||||
|
||||
type ldapSession struct {
|
||||
id string
|
||||
c net.Conn
|
||||
ldap *ldap.Conn
|
||||
}
|
||||
type ldapBackendStatus int
|
||||
|
||||
const (
|
||||
Down ldapBackendStatus = iota
|
||||
Up
|
||||
)
|
||||
|
||||
type ldapBackend struct {
|
||||
Scheme string
|
||||
Hostname string
|
||||
Port int
|
||||
Status ldapBackendStatus
|
||||
Ping time.Duration
|
||||
}
|
||||
|
||||
func NewLdapHandler(opts ...Option) Handler {
|
||||
options := newOptions(opts...)
|
||||
|
||||
handler := ldapHandler{ // set non-zero-value defaults here
|
||||
backend: options.Backend,
|
||||
handlers: options.Handlers,
|
||||
sessions: make(map[string]ldapSession),
|
||||
doPing: make(chan bool),
|
||||
log: options.Logger,
|
||||
helper: options.Helper,
|
||||
lock: &ldaplock,
|
||||
attm: ldapattributematcher,
|
||||
}
|
||||
// parse LDAP URLs
|
||||
for _, ldapurl := range handler.backend.Servers {
|
||||
l, err := parseURL(ldapurl)
|
||||
if err != nil {
|
||||
handler.log.Error(err, "could not parse url")
|
||||
os.Exit(1)
|
||||
}
|
||||
handler.servers = append(handler.servers, l)
|
||||
}
|
||||
|
||||
// test server connectivity before listening, then keep it updated
|
||||
handler.monitorServers()
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
//
|
||||
func (h ldapHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resultCode ldap.LDAPResultCode, err error) {
|
||||
h.log.V(6).Info("Bind request", "binddn", bindDN, "src", conn.RemoteAddr())
|
||||
|
||||
// if h.helper != nil {
|
||||
if true {
|
||||
|
||||
lowerBindDN := strings.ToLower(bindDN)
|
||||
baseDN := strings.ToLower("," + h.backend.BaseDN)
|
||||
parts := strings.Split(strings.TrimSuffix(lowerBindDN, baseDN), ",")
|
||||
userName := strings.TrimPrefix(parts[0], h.backend.NameFormat+"=")
|
||||
|
||||
validotp := false
|
||||
|
||||
// Find the user
|
||||
// We are going to go through all backends and ask
|
||||
// until we find our user or die of boredom.
|
||||
user := config.User{}
|
||||
found := false
|
||||
for i, handler := range h.handlers.Handlers {
|
||||
found, user, _ = handler.FindUser(userName, false)
|
||||
if found {
|
||||
break
|
||||
}
|
||||
if i >= *h.handlers.Count {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
validotp = true
|
||||
} else {
|
||||
if len(user.OTPSecret) == 0 {
|
||||
validotp = true
|
||||
} else {
|
||||
if len(bindSimplePw) > 6 {
|
||||
otp := bindSimplePw[len(bindSimplePw)-6:]
|
||||
bindSimplePw = bindSimplePw[:len(bindSimplePw)-6]
|
||||
validotp = totp.Validate(otp, user.OTPSecret)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !validotp {
|
||||
h.log.V(6).Info(fmt.Sprintf("Bind Error: invalid OTP token as %s from %s", bindDN, conn.RemoteAddr().String()))
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
}
|
||||
|
||||
stats.Frontend.Add("bind_reqs", 1)
|
||||
s, err := h.getSession(conn)
|
||||
if err != nil {
|
||||
stats.Frontend.Add("bind_ldapSession_errors", 1)
|
||||
h.log.V(6).Info("could not get session", "binddn", bindDN, "src", conn.RemoteAddr(), "error", err)
|
||||
return ldap.LDAPResultOperationsError, err
|
||||
}
|
||||
if err := s.ldap.Bind(bindDN, bindSimplePw); err != nil {
|
||||
stats.Frontend.Add("bind_errors", 1)
|
||||
h.log.V(6).Info("invalid creds", "binddn", bindDN, "src", conn.RemoteAddr())
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
stats.Frontend.Add("bind_successes", 1)
|
||||
h.log.V(6).Info("bind success", "binddn", bindDN, "src", conn.RemoteAddr())
|
||||
return ldap.LDAPResultSuccess, nil
|
||||
}
|
||||
|
||||
//
|
||||
func (h ldapHandler) Search(boundDN string, searchReq ldap.SearchRequest, conn net.Conn) (result ldap.ServerSearchResult, err error) {
|
||||
wantAttributes := true
|
||||
wantTypesOnly := false
|
||||
|
||||
h.log.V(6).Info("Search request", "binddn", boundDN, "src", conn.RemoteAddr(), "filter", searchReq.Filter)
|
||||
|
||||
// "1.1" has special meaning: it does what an empty attribute list would do
|
||||
// if it didn't already mean "return all attributes"
|
||||
if len(searchReq.Attributes) == 1 && searchReq.Attributes[0] == "1.1" {
|
||||
wantAttributes = false
|
||||
searchReq.Attributes = searchReq.Attributes[:0]
|
||||
}
|
||||
|
||||
// TypesOnly cannot be true: if it were, glauth would not be able to
|
||||
// match the returned valuea against the query
|
||||
if searchReq.TypesOnly == true {
|
||||
wantTypesOnly = true
|
||||
searchReq.TypesOnly = false
|
||||
}
|
||||
|
||||
stats.Frontend.Add("search_reqs", 1)
|
||||
s, err := h.getSession(conn)
|
||||
if err != nil {
|
||||
stats.Frontend.Add("search_ldapSession_errors", 1)
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, nil
|
||||
}
|
||||
search := ldap.NewSearchRequest(
|
||||
searchReq.BaseDN,
|
||||
searchReq.Scope,
|
||||
searchReq.DerefAliases,
|
||||
searchReq.SizeLimit,
|
||||
searchReq.TimeLimit,
|
||||
searchReq.TypesOnly,
|
||||
searchReq.Filter,
|
||||
searchReq.Attributes,
|
||||
searchReq.Controls,
|
||||
)
|
||||
|
||||
h.log.V(6).Info("Search request to backend", "request", search)
|
||||
sr, err := s.ldap.Search(search)
|
||||
h.log.V(6).Info("Backend Search result", "result", sr)
|
||||
|
||||
if !wantAttributes {
|
||||
h.log.V(6).Info("AP: Search Info", "type", "No attributes")
|
||||
for _, entry := range sr.Entries {
|
||||
entry.Attributes = entry.Attributes[:0]
|
||||
}
|
||||
}
|
||||
|
||||
if wantTypesOnly {
|
||||
h.log.V(6).Info("AP: Search Info", "type", "Types only")
|
||||
for _, entry := range sr.Entries {
|
||||
for _, attribute := range entry.Attributes {
|
||||
attribute.Values = attribute.Values[:0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WART used to debug when testing special cases against
|
||||
// glauth acting as a backend, where it may have
|
||||
// the same workaround thus hiding the issue
|
||||
/*
|
||||
for _, entry := range sr.Entries {
|
||||
for _, attribute := range entry.Attributes {
|
||||
if attribute.Name == "objectclass" {
|
||||
attribute.Name = "bogus"
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// If our original attribute is not present, either because:
|
||||
// 1-This is a root query
|
||||
// 2-We were asked not to return attributes
|
||||
// 3-We were asked not to return values
|
||||
// then we re-insert the correct values in there.
|
||||
if searchReq.Scope == 0 && searchReq.BaseDN == "" {
|
||||
h.log.V(6).Info("AP: Search Info", "type", "Root search detected")
|
||||
}
|
||||
|
||||
filters := h.buildReqAttributesList(searchReq.Filter, []string{})
|
||||
|
||||
for _, filter := range filters {
|
||||
attbits := h.attm.FindStringSubmatch(filter)
|
||||
for _, entry := range sr.Entries {
|
||||
foundattname := false
|
||||
for _, attribute := range entry.Attributes {
|
||||
if strings.ToLower(attribute.Name) == strings.ToLower(attbits[1]) {
|
||||
foundattname = true
|
||||
if len(attbits[2]) == 0 {
|
||||
attribute.Values = []string{attbits[2]}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundattname {
|
||||
entry.Attributes = append(entry.Attributes, &ldap.EntryAttribute{Name: attbits[1], Values: []string{attbits[2]}})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ssr := ldap.ServerSearchResult{
|
||||
Entries: sr.Entries,
|
||||
Referrals: sr.Referrals,
|
||||
Controls: sr.Controls,
|
||||
}
|
||||
h.log.V(6).Info("Frontend Search result", "result", ssr)
|
||||
if err != nil {
|
||||
e := err.(*ldap.Error)
|
||||
h.log.V(6).Info("Search Err", "error", err)
|
||||
stats.Frontend.Add("search_errors", 1)
|
||||
ssr.ResultCode = ldap.LDAPResultCode(e.ResultCode)
|
||||
return ssr, err
|
||||
}
|
||||
stats.Frontend.Add("search_successes", 1)
|
||||
h.log.V(6).Info("AP: Search OK", "filter", search.Filter, "numentries", len(ssr.Entries))
|
||||
return ssr, nil
|
||||
}
|
||||
|
||||
func (h ldapHandler) buildReqAttributesList(filter string, filters []string) []string {
|
||||
maxp := len(filter)
|
||||
start := -1
|
||||
descended := false
|
||||
for p, c := range filter {
|
||||
if c == '(' {
|
||||
if p+1 < maxp {
|
||||
start = p + 1
|
||||
}
|
||||
} else if c == ')' {
|
||||
if start > -1 {
|
||||
descended = true
|
||||
filters = h.buildReqAttributesList(filter[start:p], filters)
|
||||
}
|
||||
start = -1
|
||||
}
|
||||
}
|
||||
if !descended {
|
||||
filters = append(filters, filter)
|
||||
}
|
||||
return filters
|
||||
}
|
||||
|
||||
// Add is not yet supported for the ldap backend
|
||||
func (h ldapHandler) Add(boundDN string, req ldap.AddRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
// Modify is not yet supported for the ldap backend
|
||||
func (h ldapHandler) Modify(boundDN string, req ldap.ModifyRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
// Delete is not yet supported for the ldap backend
|
||||
func (h ldapHandler) Delete(boundDN string, deleteDN string, conn net.Conn) (result ldap.LDAPResultCode, err error) {
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
func (h ldapHandler) FindUser(userName string, searchByUPN bool) (found bool, user config.User, err error) {
|
||||
return false, config.User{}, nil
|
||||
}
|
||||
|
||||
func (h ldapHandler) FindGroup(groupName string) (found bool, group config.Group, err error) {
|
||||
return false, config.Group{}, nil
|
||||
}
|
||||
|
||||
func (h ldapHandler) Close(boundDn string, conn net.Conn) error {
|
||||
conn.Close() // close connection to the server when then client is closed
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
delete(h.sessions, connID(conn))
|
||||
stats.Frontend.Add("closes", 1)
|
||||
stats.Backend.Add("closes", 1)
|
||||
return nil
|
||||
}
|
||||
|
||||
// monitorServers tests server connectivity before listening, then keeps it updated
|
||||
func (h *ldapHandler) monitorServers() {
|
||||
err := h.ping()
|
||||
if err != nil {
|
||||
h.log.Error(err, "could not ping server")
|
||||
os.Exit(1)
|
||||
// TODO return error
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-h.doPing:
|
||||
h.log.V(3).Info("doPing requested due to server failure")
|
||||
err = h.ping()
|
||||
if err != nil {
|
||||
h.log.Error(err, "could not ping server")
|
||||
os.Exit(1)
|
||||
// TODO return error
|
||||
}
|
||||
case <-time.NewTimer(60 * time.Second).C:
|
||||
h.log.V(6).Info("doPing after timeout")
|
||||
err = h.ping()
|
||||
if err != nil {
|
||||
h.log.Error(err, "could not ping server")
|
||||
os.Exit(1)
|
||||
// TODO return error
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
//
|
||||
func (h ldapHandler) getSession(conn net.Conn) (ldapSession, error) {
|
||||
id := connID(conn)
|
||||
h.lock.Lock()
|
||||
s, ok := h.sessions[id] // use server connection if it exists
|
||||
h.lock.Unlock()
|
||||
if !ok { // open a new server connection if not
|
||||
var l *ldap.Conn
|
||||
server, err := h.getBestServer() // pick the best server
|
||||
if err != nil {
|
||||
return ldapSession{}, err
|
||||
}
|
||||
dest := fmt.Sprintf("%s:%d", server.Hostname, server.Port)
|
||||
if server.Scheme == "ldaps" {
|
||||
tlsCfg := &tls.Config{}
|
||||
if h.backend.Insecure {
|
||||
tlsCfg.InsecureSkipVerify = true
|
||||
}
|
||||
l, err = ldap.DialTLS("tcp", dest, tlsCfg)
|
||||
} else if server.Scheme == "ldap" {
|
||||
l, err = ldap.Dial("tcp", dest)
|
||||
}
|
||||
if err != nil {
|
||||
select {
|
||||
case h.doPing <- true: // non-blocking send
|
||||
default:
|
||||
}
|
||||
return ldapSession{}, err
|
||||
}
|
||||
s = ldapSession{id: id, c: conn, ldap: l}
|
||||
h.lock.Lock()
|
||||
h.sessions[s.id] = s
|
||||
h.lock.Unlock()
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
//
|
||||
func (h ldapHandler) ping() error {
|
||||
healthy := false
|
||||
for k, s := range h.servers {
|
||||
var l *ldap.Conn
|
||||
var err error
|
||||
dest := fmt.Sprintf("%s:%d", s.Hostname, s.Port)
|
||||
start := time.Now()
|
||||
if h.servers[0].Scheme == "ldaps" {
|
||||
tlsCfg := &tls.Config{}
|
||||
if h.backend.Insecure {
|
||||
tlsCfg.InsecureSkipVerify = true
|
||||
}
|
||||
l, err = ldap.DialTLS("tcp", dest, tlsCfg)
|
||||
} else if h.servers[0].Scheme == "ldap" {
|
||||
l, err = ldap.Dial("tcp", dest)
|
||||
}
|
||||
elapsed := time.Since(start)
|
||||
h.lock.Lock()
|
||||
if err != nil || l == nil {
|
||||
h.log.V(1).Info("Server ping failed", "hostname", s.Hostname, "port", s.Port, "error", err)
|
||||
h.servers[k].Ping = 0
|
||||
h.servers[k].Status = Down
|
||||
} else {
|
||||
healthy = true
|
||||
h.servers[k].Ping = elapsed
|
||||
h.servers[k].Status = Up
|
||||
l.Close() // prank caller
|
||||
}
|
||||
h.lock.Unlock()
|
||||
}
|
||||
h.log.V(6).Info("Server health", "servers", h.servers)
|
||||
b, err := json.Marshal(h.servers)
|
||||
if err != nil {
|
||||
h.log.V(1).Info("Error encoding tail data", "error", err)
|
||||
}
|
||||
stats.Backend.Set("servers", stats.Stringer(string(b)))
|
||||
if healthy == false {
|
||||
return fmt.Errorf("No healthy servers")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//
|
||||
func (h ldapHandler) getBestServer() (ldapBackend, error) {
|
||||
favorite := ldapBackend{}
|
||||
forever, err := time.ParseDuration("30m")
|
||||
if err != nil {
|
||||
return ldapBackend{}, err
|
||||
}
|
||||
bestping := forever
|
||||
for _, s := range h.servers {
|
||||
if s.Status == Up && s.Ping < bestping {
|
||||
favorite = s
|
||||
bestping = s.Ping
|
||||
}
|
||||
}
|
||||
if bestping == forever {
|
||||
return ldapBackend{}, fmt.Errorf("No healthy servers found")
|
||||
}
|
||||
h.log.V(6).Info("Best server", "favorite", favorite)
|
||||
return favorite, nil
|
||||
}
|
||||
|
||||
// helper functions
|
||||
func connID(conn net.Conn) string {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(conn.LocalAddr().String() + conn.RemoteAddr().String()))
|
||||
sha := fmt.Sprintf("% x", h.Sum(nil))
|
||||
return string(sha)
|
||||
}
|
||||
func parseURL(ldapurl string) (ldapBackend, error) {
|
||||
u, err := url.Parse(ldapurl)
|
||||
if err != nil {
|
||||
return ldapBackend{}, err
|
||||
}
|
||||
var port int
|
||||
if u.Scheme == "ldaps" {
|
||||
port = 636
|
||||
} else if u.Scheme == "ldap" {
|
||||
port = 389
|
||||
} else {
|
||||
return ldapBackend{}, fmt.Errorf("Unknown LDAP scheme: %s", u.Scheme)
|
||||
}
|
||||
parts := strings.Split(u.Host, ":")
|
||||
hostname := parts[0]
|
||||
if len(parts) > 1 {
|
||||
port, err = strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return ldapBackend{}, err
|
||||
}
|
||||
}
|
||||
return ldapBackend{Scheme: u.Scheme, Hostname: hostname, Port: port}, nil
|
||||
}
|
||||
@@ -1,491 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/GeertJohan/yubigo"
|
||||
"github.com/glauth/glauth/pkg/config"
|
||||
"github.com/glauth/glauth/pkg/stats"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/nmcclain/ldap"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
var configattributematcher = regexp.MustCompile(`(?i)\((?P<attribute>[a-zA-Z0-9]+)\s*=\s*(?P<value>.*)\)`)
|
||||
var emailmatcher = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
|
||||
|
||||
type LDAPOpsHandler interface {
|
||||
GetBackend() config.Backend
|
||||
GetLog() logr.Logger
|
||||
GetCfg() *config.Config
|
||||
GetYubikeyAuth() *yubigo.YubiAuth
|
||||
|
||||
FindUser(userName string, searchByUPN bool) (f bool, u config.User, err error)
|
||||
FindGroup(groupName string) (f bool, g config.Group, err error)
|
||||
FindPosixAccounts() (entrylist []*ldap.Entry, err error)
|
||||
FindPosixGroups() (entrylist []*ldap.Entry, err error)
|
||||
}
|
||||
|
||||
type failedBind struct {
|
||||
ts time.Time
|
||||
}
|
||||
|
||||
type sourceInfo struct {
|
||||
lastSeen time.Time
|
||||
failures chan failedBind
|
||||
waitUntil time.Time
|
||||
}
|
||||
|
||||
type LDAPOpsHelper struct {
|
||||
sources map[string]*sourceInfo
|
||||
nextPruning time.Time
|
||||
}
|
||||
|
||||
func NewLDAPOpsHelper() LDAPOpsHelper {
|
||||
helper := LDAPOpsHelper{
|
||||
sources: make(map[string]*sourceInfo),
|
||||
nextPruning: time.Now(),
|
||||
}
|
||||
return helper
|
||||
}
|
||||
|
||||
func (l LDAPOpsHelper) Bind(h LDAPOpsHandler, bindDN, bindSimplePw string, conn net.Conn) (resultCode ldap.LDAPResultCode, err error) {
|
||||
if l.isInTimeout(h, conn) {
|
||||
return ldap.LDAPResultUnwillingToPerform, nil
|
||||
}
|
||||
|
||||
bindDN = strings.ToLower(bindDN)
|
||||
|
||||
h.GetLog().V(6).Info("Bind request", "binddn", bindDN, "basedn", h.GetBackend().BaseDN, "src", conn.RemoteAddr())
|
||||
|
||||
stats.Frontend.Add("bind_reqs", 1)
|
||||
|
||||
// Special Case: bind as anonymous
|
||||
if bindDN == "" && bindSimplePw == "" {
|
||||
stats.Frontend.Add("bind_successes", 1)
|
||||
h.GetLog().V(6).Info("Anonymous Bind success", "src", conn.RemoteAddr())
|
||||
return ldap.LDAPResultSuccess, nil
|
||||
}
|
||||
|
||||
user, ldapcode := l.findUser(h, bindDN, true /* checkGroup */)
|
||||
if ldapcode != ldap.LDAPResultSuccess {
|
||||
return ldapcode, nil
|
||||
}
|
||||
|
||||
validotp := false
|
||||
|
||||
if len(user.Yubikey) == 0 && len(user.OTPSecret) == 0 {
|
||||
validotp = true
|
||||
}
|
||||
|
||||
if len(user.Yubikey) > 0 && h.GetYubikeyAuth() != nil {
|
||||
if len(bindSimplePw) > 44 {
|
||||
otp := bindSimplePw[len(bindSimplePw)-44:]
|
||||
yubikeyid := otp[0:12]
|
||||
bindSimplePw = bindSimplePw[:len(bindSimplePw)-44]
|
||||
|
||||
if user.Yubikey == yubikeyid {
|
||||
_, ok, _ := h.GetYubikeyAuth().Verify(otp)
|
||||
|
||||
if ok {
|
||||
validotp = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store the full bind password provided before possibly modifying
|
||||
// in the otp check
|
||||
untouchedBindSimplePw := bindSimplePw
|
||||
|
||||
// Test OTP if is exists
|
||||
if len(user.OTPSecret) > 0 && !validotp {
|
||||
if len(bindSimplePw) > 6 {
|
||||
otp := bindSimplePw[len(bindSimplePw)-6:]
|
||||
bindSimplePw = bindSimplePw[:len(bindSimplePw)-6]
|
||||
|
||||
validotp = totp.Validate(otp, user.OTPSecret)
|
||||
}
|
||||
}
|
||||
|
||||
// finally, validate user's pw
|
||||
|
||||
// check app passwords first
|
||||
if user.PassAppBcrypt != nil {
|
||||
for index, appPw := range user.PassAppBcrypt {
|
||||
decoded, err := hex.DecodeString(appPw)
|
||||
if err != nil {
|
||||
h.GetLog().V(6).Info("invalid app credentials", "incorrect stored hash", "(omitted)")
|
||||
} else {
|
||||
if bcrypt.CompareHashAndPassword(decoded, []byte(untouchedBindSimplePw)) == nil {
|
||||
stats.Frontend.Add("bind_successes", 1)
|
||||
h.GetLog().V(6).Info("Bind success using app pw", "index", index, "binddn", bindDN, "src", conn.RemoteAddr())
|
||||
return ldap.LDAPResultSuccess, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if user.PassAppSHA256 != nil {
|
||||
hashFull := sha256.New()
|
||||
hashFull.Write([]byte(untouchedBindSimplePw))
|
||||
for index, appPw := range user.PassAppSHA256 {
|
||||
if appPw != hex.EncodeToString(hashFull.Sum(nil)) {
|
||||
h.GetLog().V(6).Info("Attempt to bind app pw failed", "index", index, "binddn", bindDN, "src", conn.RemoteAddr())
|
||||
} else {
|
||||
stats.Frontend.Add("bind_successes", 1)
|
||||
h.GetLog().V(6).Info("Bind success using app pw", "index", index, "binddn", bindDN, "src", conn.RemoteAddr())
|
||||
return ldap.LDAPResultSuccess, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then ensure the OTP is valid before checking
|
||||
if !validotp {
|
||||
h.GetLog().V(2).Info("invalid OTP token", "binddn", bindDN, "src", conn.RemoteAddr())
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
|
||||
// Now, check the pasword hash
|
||||
if user.PassBcrypt != "" {
|
||||
decoded, err := hex.DecodeString(user.PassBcrypt)
|
||||
if err != nil {
|
||||
h.GetLog().V(2).Info("invalid credentials", "incorrect stored hash", "(omitted)")
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
if bcrypt.CompareHashAndPassword(decoded, []byte(bindSimplePw)) != nil {
|
||||
h.GetLog().V(2).Info("invalid credentials", "binddn", bindDN, "src", conn.RemoteAddr())
|
||||
l.maybePutInTimeout(h, conn, true)
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
}
|
||||
if user.PassSHA256 != "" {
|
||||
hash := sha256.New()
|
||||
hash.Write([]byte(bindSimplePw))
|
||||
if user.PassSHA256 != hex.EncodeToString(hash.Sum(nil)) {
|
||||
h.GetLog().V(2).Info("invalid credentials", "binddn", bindDN, "src", conn.RemoteAddr())
|
||||
l.maybePutInTimeout(h, conn, true)
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
}
|
||||
|
||||
stats.Frontend.Add("bind_successes", 1)
|
||||
h.GetLog().V(6).Info("Bind success", "binddn", bindDN, "src", conn.RemoteAddr())
|
||||
return ldap.LDAPResultSuccess, nil
|
||||
}
|
||||
|
||||
func (l LDAPOpsHelper) Search(h LDAPOpsHandler, bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (result ldap.ServerSearchResult, err error) {
|
||||
if l.isInTimeout(h, conn) {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultUnwillingToPerform}, fmt.Errorf("Source is in a timeout")
|
||||
}
|
||||
|
||||
bindDN = strings.ToLower(bindDN)
|
||||
baseDN := strings.ToLower(h.GetBackend().BaseDN)
|
||||
delimitedBaseDN := "," + baseDN
|
||||
searchBaseDN := strings.ToLower(searchReq.BaseDN)
|
||||
|
||||
anonymous := len(bindDN) < 1
|
||||
|
||||
var boundUser *config.User
|
||||
var ldapcode ldap.LDAPResultCode
|
||||
|
||||
if !anonymous {
|
||||
boundUser, ldapcode = l.findUser(h, bindDN, false /* checkGroup */)
|
||||
if ldapcode != ldap.LDAPResultSuccess {
|
||||
return ldap.ServerSearchResult{ResultCode: ldapcode}, fmt.Errorf("Search Error: Potential bypass of BindDN %s", bindDN)
|
||||
}
|
||||
// What if this user was bound using their UPN? We still want to enforce baseDN etc so we
|
||||
// have to rewire them to their original DN which is of course a waste of cycles.
|
||||
// TODO Down the road we would want to perform lightweight memoization of DNs to UPNs
|
||||
if emailmatcher.MatchString(bindDN) {
|
||||
// cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
|
||||
bindDN = fmt.Sprintf("cn=%s,%s", boundUser.Name, baseDN)
|
||||
}
|
||||
}
|
||||
|
||||
h.GetLog().V(6).Info("Search request", "binddn", bindDN, "basedn", baseDN, "searchbasedn", searchBaseDN, "src", conn.RemoteAddr(), "scope", searchReq.Scope, "filter", searchReq.Filter)
|
||||
stats.Frontend.Add("search_reqs", 1)
|
||||
|
||||
// special case: querying the root DSE
|
||||
if searchReq.Scope == 0 && (searchBaseDN == "" || searchBaseDN == baseDN) {
|
||||
if anonymous && !h.GetBackend().AnonymousDSE {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Root DSE Search Error: Anonymous BindDN not allowed %s", bindDN)
|
||||
}
|
||||
h.GetLog().V(6).Info("Search request", "special case", "root DSE")
|
||||
entries := []*ldap.Entry{}
|
||||
attrs := []*ldap.EntryAttribute{}
|
||||
// unfortunately, objectClass is not to be included so we will respect that
|
||||
// attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"*"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "supportedSASLMechanisms", Values: []string{}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "supportedLDAPVersion", Values: []string{"3"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "supportedControl", Values: []string{}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "supportedCapabilities", Values: []string{}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "subschemaSubentry", Values: []string{"cn=schema"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "serverName", Values: []string{"unknown"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "namingContexts", Values: []string{baseDN}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "defaultNamingContext", Values: []string{baseDN}})
|
||||
attrs = l.collectRequestedAttributesBack(attrs, searchReq)
|
||||
entries = append(entries, &ldap.Entry{DN: searchBaseDN, Attributes: attrs})
|
||||
stats.Frontend.Add("search_successes", 1)
|
||||
h.GetLog().V(6).Info("AP: Root Search OK", "filter", searchReq.Filter)
|
||||
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldap.LDAPResultSuccess}, nil
|
||||
}
|
||||
|
||||
// validate the user is authenticated and has appropriate access
|
||||
if anonymous {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: Anonymous BindDN not allowed %s", bindDN)
|
||||
}
|
||||
|
||||
if searchBaseDN == "cn=schema" {
|
||||
h.GetLog().V(6).Info("Search request", "special case", "schema discovery")
|
||||
entries := []*ldap.Entry{}
|
||||
attrs := []*ldap.EntryAttribute{}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "cn", Values: []string{"schema"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "hasSubordinates", Values: []string{"false"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "modifiersName", Values: []string{"cn=Directory Manager"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "modifyTimeStamp", Values: []string{"Mar 8, 2021, 12:46:29 PM PST (20210308204629Z)"}})
|
||||
// Iterate through schema attributes provided in schema/ directory
|
||||
filenames, _ := ioutil.ReadDir("schema")
|
||||
for _, filename := range filenames {
|
||||
file, err := os.Open(filepath.Join("schema", filename.Name()))
|
||||
if err != nil {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Schema Error: attribute %s cannot be read", filename)
|
||||
}
|
||||
defer file.Close()
|
||||
values := []string{}
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
values = append(values, line)
|
||||
}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: filename.Name(), Values: values})
|
||||
}
|
||||
attrs = l.collectRequestedAttributesBack(attrs, searchReq)
|
||||
entries = append(entries, &ldap.Entry{DN: searchBaseDN, Attributes: attrs})
|
||||
stats.Frontend.Add("search_successes", 1)
|
||||
h.GetLog().V(6).Info("AP: Schema Discovery OK", "filter", searchReq.Filter)
|
||||
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldap.LDAPResultSuccess}, nil
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(bindDN, delimitedBaseDN) {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: BindDN %s not in our BaseDN %s", bindDN, h.GetBackend().BaseDN)
|
||||
}
|
||||
if !strings.HasSuffix(searchBaseDN, h.GetBackend().BaseDN) {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: search BaseDN %s is not in our BaseDN %s", searchBaseDN, h.GetBackend().BaseDN)
|
||||
}
|
||||
// Unless globally ignored, we will check that a user has capabilities allowing them to perform a search in the requested BaseDN
|
||||
if !h.GetCfg().Behaviors.IgnoreCapabilities && !l.checkCapability(*boundUser, "search", []string{"*", searchBaseDN}) {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: no capability allowing BindDN %s to perform search in %s", bindDN, searchBaseDN)
|
||||
}
|
||||
|
||||
// return all users in the config file - the LDAP library will filter results for us
|
||||
entries := []*ldap.Entry{}
|
||||
filterEntity, err := ldap.GetFilterObjectClass(searchReq.Filter)
|
||||
if err != nil {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: error parsing filter: %s", searchReq.Filter)
|
||||
}
|
||||
switch filterEntity {
|
||||
default:
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: unhandled filter type: %s [%s]", filterEntity, searchReq.Filter)
|
||||
case "posixgroup":
|
||||
posixentries, err := h.FindPosixGroups()
|
||||
if err != nil {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: Unable to retrieve data [%s]", err.Error())
|
||||
}
|
||||
entries = append(entries, posixentries...)
|
||||
case "posixaccount", "shadowaccount", "":
|
||||
posixentries, err := h.FindPosixAccounts()
|
||||
if err != nil {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: Unable to retrieve data [%s]", err.Error())
|
||||
}
|
||||
entries = append(entries, posixentries...)
|
||||
}
|
||||
stats.Frontend.Add("search_successes", 1)
|
||||
h.GetLog().V(6).Info("AP: Search OK", "filter", searchReq.Filter)
|
||||
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldap.LDAPResultSuccess}, nil
|
||||
}
|
||||
|
||||
func (l LDAPOpsHelper) findUser(h LDAPOpsHandler, bindDN string, checkGroup bool) (userWhenFound *config.User, resultCode ldap.LDAPResultCode) {
|
||||
|
||||
var user config.User
|
||||
|
||||
baseDN := strings.ToLower("," + h.GetBackend().BaseDN)
|
||||
|
||||
// Special Case: bind using UPN
|
||||
// Not using mail.ParseAddress/1 because we would allow incorrectly formatted UPNs
|
||||
if emailmatcher.MatchString(bindDN) {
|
||||
var foundUser bool // = false
|
||||
foundUser, user, _ = h.FindUser(bindDN, true)
|
||||
if !foundUser {
|
||||
h.GetLog().V(2).Info("User not found", "userprincipalname", bindDN)
|
||||
return nil, ldap.LDAPResultInvalidCredentials
|
||||
}
|
||||
} else {
|
||||
// parse the bindDN - ensure that the bindDN ends with the BaseDN
|
||||
if !strings.HasSuffix(bindDN, baseDN) {
|
||||
h.GetLog().V(2).Info("BindDN not part of our BaseDN", "binddn", bindDN, "basedn", h.GetBackend().BaseDN)
|
||||
// h.GetLog().Warning(fmt.Sprintf("Bind Error: BindDN %s not our BaseDN %s", bindDN, baseDN))
|
||||
return nil, ldap.LDAPResultInvalidCredentials
|
||||
}
|
||||
parts := strings.Split(strings.TrimSuffix(bindDN, baseDN), ",")
|
||||
groupName := ""
|
||||
userName := ""
|
||||
if len(parts) == 1 {
|
||||
userName = strings.TrimPrefix(parts[0], h.GetBackend().NameFormat+"=")
|
||||
} else if len(parts) == 2 {
|
||||
userName = strings.TrimPrefix(parts[0], h.GetBackend().NameFormat+"=")
|
||||
groupName = strings.TrimPrefix(parts[1], h.GetBackend().GroupFormat+"=")
|
||||
} else {
|
||||
h.GetLog().V(2).Info("BindDN should have only one or two parts", "binddn", bindDN, "numparts", len(parts))
|
||||
return nil, ldap.LDAPResultInvalidCredentials
|
||||
}
|
||||
|
||||
// find the user
|
||||
var foundUser bool // = false
|
||||
foundUser, user, _ = h.FindUser(userName, false)
|
||||
if !foundUser {
|
||||
h.GetLog().V(2).Info("User not found", "username", userName)
|
||||
return nil, ldap.LDAPResultInvalidCredentials
|
||||
}
|
||||
if checkGroup {
|
||||
// find the group
|
||||
var group config.Group // = nil
|
||||
var foundGroup bool // = false
|
||||
if groupName != "" {
|
||||
foundGroup, group, _ = h.FindGroup(groupName)
|
||||
if !foundGroup {
|
||||
h.GetLog().V(2).Info("Group not found", "groupname", groupName)
|
||||
return nil, ldap.LDAPResultInvalidCredentials
|
||||
}
|
||||
}
|
||||
// validate group membership
|
||||
if foundGroup {
|
||||
if user.PrimaryGroup != group.GIDNumber {
|
||||
h.GetLog().V(2).Info("primary group mismatch", "username", userName, "primarygroup", user.PrimaryGroup, "groupid", group.GIDNumber)
|
||||
return nil, ldap.LDAPResultInvalidCredentials
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return &user, ldap.LDAPResultSuccess
|
||||
}
|
||||
|
||||
func (l LDAPOpsHelper) checkCapability(user config.User, action string, objects []string) bool {
|
||||
for _, capability := range user.Capabilities {
|
||||
if capability.Action == action {
|
||||
for _, object := range objects {
|
||||
if capability.Object == object {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// If your query is for, say 'objectClass', then our LDAP
|
||||
// library will weed out this entry since it does *not* contain an objectclass attribute
|
||||
// so we are going to re-inject it to keep the LDAP library happy
|
||||
func (l LDAPOpsHelper) collectRequestedAttributesBack(attrs []*ldap.EntryAttribute, searchReq ldap.SearchRequest) []*ldap.EntryAttribute {
|
||||
attbits := configattributematcher.FindStringSubmatch(searchReq.Filter)
|
||||
if len(attbits) == 3 {
|
||||
foundattname := false
|
||||
for _, attr := range attrs {
|
||||
if strings.ToLower(attr.Name) == strings.ToLower(attbits[1]) {
|
||||
foundattname = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// the ugly hack: we are going to pretend that the requested attribute is in there
|
||||
if !foundattname {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: attbits[1], Values: []string{attbits[2]}})
|
||||
}
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
// return true if we should not process the current operation
|
||||
func (l LDAPOpsHelper) isInTimeout(handler LDAPOpsHandler, conn net.Conn) bool {
|
||||
cfg := handler.GetCfg()
|
||||
if !cfg.Behaviors.LimitFailedBinds {
|
||||
return false
|
||||
}
|
||||
|
||||
remoteAddr := l.getAddr(conn)
|
||||
now := time.Now()
|
||||
info, ok := l.sources[remoteAddr]
|
||||
if !ok {
|
||||
l.sources[remoteAddr] = &sourceInfo{
|
||||
lastSeen: now,
|
||||
failures: make(chan failedBind, cfg.Behaviors.NumberOfFailedBinds),
|
||||
waitUntil: now,
|
||||
}
|
||||
return false
|
||||
}
|
||||
// update so that this source does not get pruned
|
||||
info.lastSeen = now
|
||||
// if we are in a time out...
|
||||
if cfg.Behaviors.LimitFailedBinds && info.waitUntil.After(now) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (l LDAPOpsHelper) maybePutInTimeout(handler LDAPOpsHandler, conn net.Conn, noteFailure bool) bool {
|
||||
cfg := handler.GetCfg()
|
||||
if !cfg.Behaviors.LimitFailedBinds {
|
||||
return false
|
||||
}
|
||||
|
||||
remoteAddr := l.getAddr(conn)
|
||||
now := time.Now()
|
||||
info, _ := l.sources[remoteAddr]
|
||||
// if we have a failed bind...
|
||||
if noteFailure {
|
||||
info.failures <- failedBind{ts: time.Now()}
|
||||
// if we now have 3 failed binds in a row
|
||||
if len(info.failures) == cfg.Behaviors.NumberOfFailedBinds {
|
||||
// we cannot have more than 3 failed binds in our channel so pop the oldest one
|
||||
pruned := <-info.failures
|
||||
// if we have 3 failed bind in a row in less than 3 seconds
|
||||
if pruned.ts.Add(cfg.Behaviors.PeriodOfFailedBinds * time.Second).After(now) {
|
||||
// we will wait for 'n' seconds no matter what happens next
|
||||
info.waitUntil = time.Now().Add(cfg.Behaviors.BlockFailedBindsFor * time.Second)
|
||||
// purge our failure queue until we resume accepting operations
|
||||
for len(info.failures) > 0 {
|
||||
<-info.failures
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Prune old IPs
|
||||
// TODO We should ensure that the time between prunings is bigger than the time to determine rapid failed binds
|
||||
if l.nextPruning.Before(now) {
|
||||
for sourceIP, sourceInfo := range l.sources {
|
||||
if sourceInfo.lastSeen.Add(cfg.Behaviors.PruneSourcesOlderThan * time.Second).Before(now) {
|
||||
delete(l.sources, sourceIP)
|
||||
}
|
||||
}
|
||||
l.nextPruning = time.Now().Add(cfg.Behaviors.PruneSourceTableEvery * time.Second)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (l LDAPOpsHelper) getAddr(conn net.Conn) string {
|
||||
fullAddr := conn.RemoteAddr().String()
|
||||
sep := strings.LastIndex(fullAddr, ":")
|
||||
if sep == -1 {
|
||||
return fullAddr
|
||||
}
|
||||
return fullAddr[0:sep]
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/GeertJohan/yubigo"
|
||||
"github.com/glauth/glauth/pkg/config"
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Backend config.Backend
|
||||
Handlers HandlerWrapper
|
||||
Logger logr.Logger
|
||||
Config *config.Config
|
||||
Context *context.Context
|
||||
YubiAuth *yubigo.YubiAuth
|
||||
Helper Handler
|
||||
LDAPHelper LDAPOpsHelper
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
func NewOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// Backend is our current backend
|
||||
func Backend(val config.Backend) Option {
|
||||
return func(o *Options) {
|
||||
o.Backend = val
|
||||
}
|
||||
}
|
||||
|
||||
// Our friendly handlers for all backends
|
||||
func Handlers(val HandlerWrapper) Option {
|
||||
return func(o *Options) {
|
||||
o.Handlers = val
|
||||
}
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(val logr.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
|
||||
// Context provides a function to set the context option.
|
||||
func Context(val *context.Context) Option {
|
||||
return func(o *Options) {
|
||||
o.Context = val
|
||||
}
|
||||
}
|
||||
|
||||
// YubiAuth provides a function to set the yubiauth option.
|
||||
func YubiAuth(val *yubigo.YubiAuth) Option {
|
||||
return func(o *Options) {
|
||||
o.YubiAuth = val
|
||||
}
|
||||
}
|
||||
|
||||
// If we specified a helper, for instance for OTP injection
|
||||
func Helper(val Handler) Option {
|
||||
return func(o *Options) {
|
||||
o.Helper = val
|
||||
}
|
||||
}
|
||||
|
||||
// Global LDAP Handler
|
||||
func LDAPHelper(val LDAPOpsHelper) Option {
|
||||
return func(o *Options) {
|
||||
o.LDAPHelper = val
|
||||
}
|
||||
}
|
||||
@@ -1,375 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/glauth/glauth/pkg/config"
|
||||
"github.com/glauth/glauth/pkg/stats"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/nmcclain/ldap"
|
||||
msgraph "github.com/yaegashi/msgraph.go/v1.0"
|
||||
)
|
||||
|
||||
type ownCloudSession struct {
|
||||
log logr.Logger
|
||||
client *http.Client
|
||||
user string
|
||||
password string
|
||||
endpoint string
|
||||
useGraphAPI bool
|
||||
}
|
||||
type ownCloudHandler struct {
|
||||
backend config.Backend
|
||||
log logr.Logger
|
||||
client *http.Client
|
||||
sessions map[string]ownCloudSession
|
||||
lock *sync.Mutex
|
||||
}
|
||||
|
||||
// global lock for ownCloudHandler sessions & servers manipulation
|
||||
var ownCloudLock sync.Mutex
|
||||
|
||||
func (h ownCloudHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (ldap.LDAPResultCode, error) {
|
||||
bindDN = strings.ToLower(bindDN)
|
||||
baseDN := strings.ToLower("," + h.backend.BaseDN)
|
||||
|
||||
h.log.V(6).Info("Bind request", "binddn", bindDN, "basedn", h.backend.BaseDN, "src", conn.RemoteAddr())
|
||||
|
||||
stats.Frontend.Add("bind_reqs", 1)
|
||||
|
||||
// parse the bindDN - ensure that the bindDN ends with the BaseDN
|
||||
if !strings.HasSuffix(bindDN, baseDN) {
|
||||
h.log.V(2).Info("BindDN not part of our BaseDN", "binddn", bindDN, "basedn", h.backend.BaseDN)
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
parts := strings.Split(strings.TrimSuffix(bindDN, baseDN), ",")
|
||||
if len(parts) > 2 {
|
||||
h.log.V(2).Info("BindDN should have only one or two parts", "binddn", bindDN, "numparts", len(parts))
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
userName := strings.TrimPrefix(parts[0], "cn=")
|
||||
|
||||
// try to login
|
||||
if !h.login(userName, bindSimplePw) {
|
||||
h.log.V(2).Info("Login failed", "username", userName, "basedn", h.backend.BaseDN)
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
|
||||
// TODO reuse HTTP connection
|
||||
id := connID(conn)
|
||||
s := ownCloudSession{
|
||||
log: h.log,
|
||||
user: userName,
|
||||
password: bindSimplePw,
|
||||
endpoint: h.backend.Servers[0],
|
||||
useGraphAPI: h.backend.UseGraphAPI,
|
||||
client: h.client,
|
||||
}
|
||||
h.lock.Lock()
|
||||
h.sessions[id] = s
|
||||
h.lock.Unlock()
|
||||
|
||||
stats.Frontend.Add("bind_successes", 1)
|
||||
h.log.V(6).Info("Bind success", "binddn", bindDN, "basedn", h.backend.BaseDN, "src", conn.RemoteAddr())
|
||||
return ldap.LDAPResultSuccess, nil
|
||||
}
|
||||
|
||||
func (h ownCloudHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) {
|
||||
bindDN = strings.ToLower(bindDN)
|
||||
baseDN := strings.ToLower("," + h.backend.BaseDN)
|
||||
searchBaseDN := strings.ToLower(searchReq.BaseDN)
|
||||
h.log.V(6).Info("Search request", "binddn", bindDN, "basedn", baseDN, "src", conn.RemoteAddr(), "filter", searchReq.Filter)
|
||||
stats.Frontend.Add("search_reqs", 1)
|
||||
|
||||
// validate the user is authenticated and has appropriate access
|
||||
if len(bindDN) < 1 {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("search error: Anonymous BindDN not allowed %s", bindDN)
|
||||
}
|
||||
if !strings.HasSuffix(bindDN, baseDN) {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("search error: BindDN %s not in our BaseDN %s", bindDN, h.backend.BaseDN)
|
||||
}
|
||||
if !strings.HasSuffix(searchBaseDN, h.backend.BaseDN) {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("search error: search BaseDN %s is not in our BaseDN %s", searchBaseDN, h.backend.BaseDN)
|
||||
}
|
||||
// return all users in the config file - the LDAP library will filter results for us
|
||||
entries := []*ldap.Entry{}
|
||||
filterEntity, err := ldap.GetFilterObjectClass(searchReq.Filter)
|
||||
if err != nil {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("search error: error parsing filter: %s", searchReq.Filter)
|
||||
}
|
||||
h.lock.Lock()
|
||||
id := connID(conn)
|
||||
session := h.sessions[id]
|
||||
h.lock.Unlock()
|
||||
|
||||
switch filterEntity {
|
||||
default:
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("search error: unhandled filter type: %s [%s]", filterEntity, searchReq.Filter)
|
||||
case "posixgroup":
|
||||
groups, err := session.getGroups()
|
||||
if err != nil {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, errors.New("search error: error getting groups")
|
||||
}
|
||||
for _, g := range groups {
|
||||
attrs := []*ldap.EntryAttribute{}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "cn", Values: []string{*g.ID}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "description", Values: []string{fmt.Sprintf("%s from ownCloud", *g.ID)}})
|
||||
// attrs = append(attrs, &ldap.EntryAttribute{"gidNumber", []string{fmt.Sprintf("%d", g.GIDNumber)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixGroup"}})
|
||||
if g.Members != nil {
|
||||
members := make([]string, len(g.Members))
|
||||
for i, v := range g.Members {
|
||||
members[i] = *v.ID
|
||||
}
|
||||
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberUid", Values: members})
|
||||
}
|
||||
dn := fmt.Sprintf("cn=%s,%s=groups,%s", *g.ID, h.backend.GroupFormat, h.backend.BaseDN)
|
||||
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
|
||||
}
|
||||
case "posixaccount", "":
|
||||
userName := ""
|
||||
if searchBaseDN != strings.ToLower(h.backend.BaseDN) {
|
||||
parts := strings.Split(strings.TrimSuffix(searchBaseDN, baseDN), ",")
|
||||
if len(parts) >= 1 {
|
||||
userName = strings.TrimPrefix(parts[0], "cn=")
|
||||
}
|
||||
}
|
||||
users, err := session.getUsers(userName)
|
||||
if err != nil {
|
||||
h.log.V(6).Info("Could not get user", "username", userName, "err", err)
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, errors.New("search error: error getting users")
|
||||
}
|
||||
for _, u := range users {
|
||||
attrs := []*ldap.EntryAttribute{}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "cn", Values: []string{*u.ID}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "uid", Values: []string{*u.ID}})
|
||||
if u.DisplayName != nil {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "givenName", Values: []string{*u.DisplayName}})
|
||||
}
|
||||
if u.Mail != nil {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "mail", Values: []string{*u.Mail}})
|
||||
}
|
||||
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "objectClass", Values: []string{"posixAccount"}})
|
||||
|
||||
attrs = append(attrs, &ldap.EntryAttribute{Name: "description", Values: []string{fmt.Sprintf("%s from ownCloud", *u.ID)}})
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s,%s", h.backend.NameFormat, *u.ID, h.backend.GroupFormat, "users", h.backend.BaseDN)
|
||||
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
|
||||
}
|
||||
}
|
||||
stats.Frontend.Add("search_successes", 1)
|
||||
h.log.V(6).Info("AP: Search OK", "filter", searchReq.Filter)
|
||||
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldap.LDAPResultSuccess}, nil
|
||||
}
|
||||
|
||||
// Add is not yet supported for the owncloud backend
|
||||
func (h ownCloudHandler) Add(boundDN string, req ldap.AddRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
// Modify is not yet supported for the owncloud backend
|
||||
func (h ownCloudHandler) Modify(boundDN string, req ldap.ModifyRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
// Delete is not yet supported for the owncloud backend
|
||||
func (h ownCloudHandler) Delete(boundDN string, deleteDN string, conn net.Conn) (result ldap.LDAPResultCode, err error) {
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
// FindUser with the given username. Called by the ldap backend to authenticate the bind. Optional
|
||||
func (h ownCloudHandler) FindUser(userName string, searchByUPN bool) (found bool, user config.User, err error) {
|
||||
return false, config.User{}, nil
|
||||
}
|
||||
|
||||
func (h ownCloudHandler) FindGroup(groupName string) (found bool, group config.Group, err error) {
|
||||
return false, config.Group{}, nil
|
||||
}
|
||||
|
||||
func (h ownCloudHandler) Close(boundDN string, conn net.Conn) error {
|
||||
conn.Close() // close connection to the server when then client is closed
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
delete(h.sessions, connID(conn))
|
||||
stats.Frontend.Add("closes", 1)
|
||||
stats.Backend.Add("closes", 1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h ownCloudHandler) login(name, pw string) bool {
|
||||
var req *http.Request
|
||||
if h.backend.UseGraphAPI {
|
||||
// TODO oc10 graphapi app should implement /me
|
||||
req, _ = http.NewRequest("GET", h.backend.Servers[0]+"/users/"+name, nil)
|
||||
} else {
|
||||
// use provisioning api
|
||||
meURL := fmt.Sprintf("%s/ocs/v2.php/cloud/user?format=json", h.backend.Servers[0])
|
||||
req, _ = http.NewRequest("GET", meURL, nil)
|
||||
}
|
||||
req.SetBasicAuth(name, pw)
|
||||
resp, err := h.client.Do(req)
|
||||
if err != nil || resp.StatusCode != http.StatusOK {
|
||||
h.log.Error(err, "failed login", "status", resp.StatusCode)
|
||||
return false
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
type OCSGroupsResponse struct {
|
||||
Ocs struct {
|
||||
Meta struct {
|
||||
Message interface{} `json:"message"`
|
||||
Statuscode int `json:"statuscode"`
|
||||
Status string `json:"status"`
|
||||
} `json:"meta"`
|
||||
Data struct {
|
||||
Groups []string `json:"groups"`
|
||||
} `json:"data"`
|
||||
} `json:"ocs"`
|
||||
}
|
||||
|
||||
func (s ownCloudSession) getGroups() ([]msgraph.Group, error) {
|
||||
if s.useGraphAPI {
|
||||
ctx := context.Background()
|
||||
req := s.NewClient().Groups().Request()
|
||||
req.Expand("members")
|
||||
return req.Get(ctx)
|
||||
}
|
||||
groupsUrl := fmt.Sprintf("%s/ocs/v2.php/cloud/groups?format=json", s.endpoint)
|
||||
|
||||
req, _ := http.NewRequest("GET", groupsUrl, nil)
|
||||
req.SetBasicAuth(s.user, s.password)
|
||||
resp, err := s.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var f OCSGroupsResponse
|
||||
if err := json.Unmarshal(b, &f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]msgraph.Group, len(f.Ocs.Data.Groups))
|
||||
for i := range f.Ocs.Data.Groups {
|
||||
ret[i] = msgraph.Group{DirectoryObject: msgraph.DirectoryObject{Entity: msgraph.Entity{ID: &f.Ocs.Data.Groups[i]}}}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type OCSUsersResponse struct {
|
||||
Ocs struct {
|
||||
Data struct {
|
||||
Users []string `json:"users"`
|
||||
} `json:"data"`
|
||||
Meta struct {
|
||||
Statuscode int `json:"statuscode"`
|
||||
Message interface{} `json:"message"`
|
||||
Status string `json:"status"`
|
||||
} `json:"meta"`
|
||||
} `json:"ocs"`
|
||||
}
|
||||
|
||||
// NewClient returns GraphService request builder with default base URL
|
||||
func (s ownCloudSession) NewClient() *msgraph.GraphServiceRequestBuilder {
|
||||
httpClient := &http.Client{
|
||||
Transport: s,
|
||||
}
|
||||
g := msgraph.NewClient(httpClient)
|
||||
g.SetURL(s.endpoint)
|
||||
return g
|
||||
}
|
||||
|
||||
func (s ownCloudSession) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
req.SetBasicAuth(s.user, s.password)
|
||||
return s.client.Transport.RoundTrip(req)
|
||||
}
|
||||
|
||||
func (s ownCloudSession) getUsers(userName string) ([]msgraph.User, error) {
|
||||
if s.useGraphAPI {
|
||||
s.log.V(6).Info("using graph api")
|
||||
ctx := context.Background()
|
||||
req := s.NewClient().Users()
|
||||
if len(userName) > 0 {
|
||||
s.log.V(6).Info("fetching single user")
|
||||
u, err := req.ID(userName).Request().Get(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []msgraph.User{*u}, nil
|
||||
}
|
||||
s.log.V(6).Info("fetching all users")
|
||||
return req.Request().Get(ctx)
|
||||
}
|
||||
s.log.V(6).Info("using provisioning api")
|
||||
usersUrl := fmt.Sprintf("%s/ocs/v2.php/cloud/users?format=json", s.endpoint)
|
||||
|
||||
req, _ := http.NewRequest("GET", usersUrl, nil)
|
||||
req.SetBasicAuth(s.user, s.password)
|
||||
resp, err := s.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var f OCSUsersResponse
|
||||
if err := json.Unmarshal(b, &f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]msgraph.User, len(f.Ocs.Data.Users))
|
||||
for i := range f.Ocs.Data.Users {
|
||||
ret[i] = msgraph.User{
|
||||
DirectoryObject: msgraph.DirectoryObject{
|
||||
Entity: msgraph.Entity{ID: &f.Ocs.Data.Users[i]},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s ownCloudSession) redirectPolicyFunc(req *http.Request, via []*http.Request) error {
|
||||
s.log.V(6).Info("Setting user and password", "username", s.user)
|
||||
req.SetBasicAuth(s.user, s.password)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewOwnCloudHandler(opts ...Option) Handler {
|
||||
options := newOptions(opts...)
|
||||
|
||||
return ownCloudHandler{
|
||||
backend: options.Backend,
|
||||
log: options.Logger,
|
||||
sessions: make(map[string]ownCloudSession),
|
||||
lock: &ownCloudLock,
|
||||
client: &http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: options.Backend.Insecure,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#####################
|
||||
# Plugins Targets
|
||||
#####################
|
||||
|
||||
# Note: to make a plugin compatible with a binary built in debug mode, add `-gcflags='all=-N -l'`
|
||||
|
||||
PLUGIN_OS ?= linux
|
||||
PLUGIN_ARCH ?= amd64
|
||||
|
||||
plugins: plugin_mysql plugin_postgres plugin_sqlite
|
||||
|
||||
plugin_mysql: bin/mysql.so
|
||||
|
||||
bin/mysql.so: pkg/plugins/mysql.go pkg/plugins/basesqlhandler.go
|
||||
GOOS=$(PLUGIN_OS) GOARCH=$(PLUGIN_ARCH) go build ${TRIM_FLAGS} -ldflags "${BUILD_VARS}" -buildmode=plugin -o $@ $^
|
||||
|
||||
plugin_postgres: bin/postgres.so
|
||||
|
||||
bin/postgres.so: pkg/plugins/postgres.go pkg/plugins/basesqlhandler.go
|
||||
GOOS=$(PLUGIN_OS) GOARCH=$(PLUGIN_ARCH) go build ${TRIM_FLAGS} -ldflags "${BUILD_VARS}" -buildmode=plugin -o $@ $^
|
||||
|
||||
plugin_sqlite: bin/sqlite.so
|
||||
|
||||
bin/sqlite.so: pkg/plugins/sqlite.go pkg/plugins/basesqlhandler.go
|
||||
GOOS=$(PLUGIN_OS) GOARCH=$(PLUGIN_ARCH) go build ${TRIM_FLAGS} -ldflags "${BUILD_VARS}" -buildmode=plugin -o $@ $^
|
||||
@@ -1,140 +0,0 @@
|
||||
# GLAuth Plugins
|
||||
|
||||
This folder contains plugins; that is, backends that are not compiled in GLAuth by default.
|
||||
|
||||
To quote 'Butonic' (Jörn Friedrich Dreyer):
|
||||
|
||||
> Just keep the 'lightweight' in mind.
|
||||
|
||||
To build either back-end, type
|
||||
```
|
||||
make plugin_name
|
||||
```
|
||||
where 'name' is the plugin's name; so, for instance: `make plugin_sqlite`
|
||||
|
||||
To build back-ends for specific architectures, specify `PLUGIN_OS` and `PLUGIN_ARCH` --
|
||||
For instance, to build the sqlite plugin for the new Mac M1s:
|
||||
```
|
||||
make plugin_sqlite PLUGIN_OS=darwin PLUGIN_ARCH=arm64
|
||||
```
|
||||
|
||||
## Database Plugins
|
||||
|
||||
To use a database plugin, edit the configuration file (see pkg/plugins/sample-database.cfg) so that:
|
||||
|
||||
```
|
||||
...
|
||||
[backend]
|
||||
datastore = "plugin"
|
||||
plugin = "dynamic library you created using the previous 'make' command"
|
||||
database = "database connection string"
|
||||
...
|
||||
```
|
||||
so, let's say you built the 'sqlite' plugin, you would now specify its library: `database = sqlite.so`
|
||||
|
||||
### SQLite, MySQL, Postgres
|
||||
|
||||
Tables:
|
||||
- users, groups are self-explanatory
|
||||
- includegroups store the 'includegroups' relationships
|
||||
- othergroups, on the other hand, are a comma-separated list found in the users table (performance)
|
||||
|
||||
Here is how to insert example data using your database's REPL (more detailed information can be found in pkg/plugins/sample-database.cfg)
|
||||
|
||||
```sql
|
||||
INSERT INTO groups(name, gidnumber) VALUES('superheros', 5501);
|
||||
INSERT INTO groups(name, gidnumber) VALUES('svcaccts', 5502);
|
||||
INSERT INTO groups(name, gidnumber) VALUES('civilians', 5503);
|
||||
INSERT INTO groups(name, gidnumber) VALUES('caped', 5504);
|
||||
INSERT INTO groups(name, gidnumber) VALUES('lovesailing', 5505);
|
||||
INSERT INTO groups(name, gidnumber) VALUES('smoker', 5506);
|
||||
INSERT INTO includegroups(parentgroupid, includegroupid) VALUES(5503, 5501);
|
||||
INSERT INTO includegroups(parentgroupid, includegroupid) VALUES(5504, 5502);
|
||||
INSERT INTO includegroups(parentgroupid, includegroupid) VALUES(5504, 5501);
|
||||
INSERT INTO users(name, uidnumber, primarygroup, passsha256) VALUES('hackers', 5001, 5501, '6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a');
|
||||
INSERT INTO users(name, uidnumber, primarygroup, passsha256) VALUES('johndoe', 5002, 5502, '6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a');
|
||||
INSERT INTO users(name, mail, uidnumber, primarygroup, passsha256) VALUES('serviceuser', "serviceuser@example.com", 5003, 5502, '652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0');
|
||||
INSERT INTO users(name, uidnumber, primarygroup, passsha256, othergroups) VALUES('user4', 5004, 5504, '652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0', '5505,5506');
|
||||
INSERT INTO capabilities(userid, action, object) VALUES(5001, "search", "ou=superheros,dc=glauth,dc=com");
|
||||
INSERT INTO capabilities(userid, action, object) VALUES(5003, "search", "*");
|
||||
```
|
||||
This should be equivalent to this configuration:
|
||||
```text
|
||||
[[users]]
|
||||
name = "hackers"
|
||||
uidnumber = 5001
|
||||
primarygroup = 5501
|
||||
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "ou=superheros,dc=glauth,dc=com"
|
||||
|
||||
[[users]]
|
||||
name = "johndoe"
|
||||
uidnumber = 5002
|
||||
primarygroup = 5502
|
||||
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
|
||||
|
||||
[[users]]
|
||||
name = "serviceuser"
|
||||
mail = "serviceuser@example.com"
|
||||
uidnumber = 5003
|
||||
passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret
|
||||
primarygroup = 5502
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "*"
|
||||
|
||||
[[users]]
|
||||
name = "user4"
|
||||
uidnumber = 5003
|
||||
primarygroup = 5504
|
||||
othergroups = [5505, 5506]
|
||||
passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret
|
||||
|
||||
[[groups]]
|
||||
name = "superheros"
|
||||
gidnumber = 5501
|
||||
|
||||
[[groups]]
|
||||
name = "svcaccts"
|
||||
gidnumber = 5502
|
||||
|
||||
[[groups]]
|
||||
name = "civilians"
|
||||
gidnumber = 5503
|
||||
includegroups = [ 5501 ]
|
||||
|
||||
[[groups]]
|
||||
name = "caped"
|
||||
gidnumber = 5504
|
||||
includegroups = [ 5502, 5501 ]
|
||||
```
|
||||
and LDAP should return these `memberOf` values:
|
||||
```text
|
||||
uid: hackers
|
||||
ou: superheros
|
||||
memberOf: cn=caped,ou=groups,dc=militate,dc=com
|
||||
memberOf: cn=civilians,ou=groups,dc=militate,dc=com
|
||||
memberOf: cn=superheros,ou=groups,dc=militate,dc=com
|
||||
|
||||
uid: johndoe
|
||||
ou: svcaccts
|
||||
memberOf: cn=caped,ou=groups,dc=militate,dc=com
|
||||
memberOf: cn=svcaccts,ou=groups,dc=militate,dc=com
|
||||
|
||||
uid: serviceuser
|
||||
ou: caped
|
||||
memberOf: cn=caped,ou=groups,dc=militate,dc=com
|
||||
|
||||
uid: user4
|
||||
ou: caped
|
||||
memberOf: cn=caped,ou=groups,dc=militate,dc=com
|
||||
memberOf: cn=lovesailing,ou=groups,dc=militate,dc=com
|
||||
memberOf: cn=smoker,ou=groups,dc=militate,dc=com
|
||||
```
|
||||
If you have the ldap client package installed, this can be easily confirmed by running
|
||||
```
|
||||
ldapsearch -H ldap://localhost:3893 -D cn=hackers,ou=superheros,dc=glauth,dc=com -w dogood -x -bdc=glauth,dc=com cn=hackers
|
||||
```
|
||||
and so on.
|
||||
@@ -1,517 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/GeertJohan/yubigo"
|
||||
"github.com/glauth/glauth/pkg/config"
|
||||
"github.com/glauth/glauth/pkg/handler"
|
||||
"github.com/glauth/glauth/pkg/stats"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/nmcclain/ldap"
|
||||
)
|
||||
|
||||
var configattributematcher = regexp.MustCompile(`(?i)\((?P<attribute>[a-zA-Z0-9]+)\s*=\s*(?P<value>.*)\)`)
|
||||
|
||||
type SqlBackend interface {
|
||||
// Name used by database/sql when loading the driver
|
||||
GetDriverName() string
|
||||
// Create db/schema if necessary
|
||||
CreateSchema(db *sql.DB)
|
||||
//
|
||||
GetPrepareSymbol() string
|
||||
}
|
||||
|
||||
type database struct {
|
||||
path string
|
||||
cnx *sql.DB
|
||||
}
|
||||
|
||||
type databaseHandler struct {
|
||||
backend config.Backend
|
||||
log logr.Logger
|
||||
cfg *config.Config
|
||||
yubikeyAuth *yubigo.YubiAuth
|
||||
sqlBackend SqlBackend
|
||||
database database
|
||||
MemGroups []config.Group
|
||||
ldohelper handler.LDAPOpsHelper
|
||||
attmatcher *regexp.Regexp
|
||||
}
|
||||
|
||||
// func NewDatabaseHandler_deprecated(log *logging.Logger, cfg *config.Config, yubikeyAuth *yubigo.YubiAuth, sqlBackend SqlBackend) handler.Handler {
|
||||
func NewDatabaseHandler(sqlBackend SqlBackend, opts ...handler.Option) handler.Handler {
|
||||
options := handler.NewOptions(opts...)
|
||||
|
||||
// Note: we will never terminate this connection pool.
|
||||
db, err := sql.Open(sqlBackend.GetDriverName(), options.Backend.Database)
|
||||
if err != nil {
|
||||
options.Logger.Error(err, "Unable to open SQL database named '%s' error: %s", options.Backend.Database)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
options.Logger.Error(err, "Unable to communicate with SQL database error: %s", options.Backend.Database)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dbInfo := database{
|
||||
path: options.Backend.Database,
|
||||
cnx: db,
|
||||
}
|
||||
|
||||
handler := databaseHandler{
|
||||
backend: options.Backend,
|
||||
log: options.Logger,
|
||||
cfg: options.Config,
|
||||
yubikeyAuth: options.YubiAuth,
|
||||
sqlBackend: sqlBackend,
|
||||
database: dbInfo,
|
||||
ldohelper: options.LDAPHelper,
|
||||
attmatcher: configattributematcher}
|
||||
|
||||
sqlBackend.CreateSchema(db)
|
||||
|
||||
options.Logger.V(3).Info("Database (" + sqlBackend.GetDriverName() + "::" + options.Backend.Database + ") Plugin: Ready")
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
func (h databaseHandler) GetBackend() config.Backend {
|
||||
return h.backend
|
||||
}
|
||||
func (h databaseHandler) GetLog() logr.Logger {
|
||||
return h.log
|
||||
}
|
||||
func (h databaseHandler) GetCfg() *config.Config {
|
||||
return h.cfg
|
||||
}
|
||||
func (h databaseHandler) GetYubikeyAuth() *yubigo.YubiAuth {
|
||||
return h.yubikeyAuth
|
||||
}
|
||||
|
||||
func (h databaseHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (resultCode ldap.LDAPResultCode, err error) {
|
||||
return h.ldohelper.Bind(h, bindDN, bindSimplePw, conn)
|
||||
}
|
||||
|
||||
func (h databaseHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (result ldap.ServerSearchResult, err error) {
|
||||
return h.ldohelper.Search(h, bindDN, searchReq, conn)
|
||||
}
|
||||
|
||||
// Add is not yet supported for the sql backend
|
||||
func (h databaseHandler) Add(boundDN string, req ldap.AddRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
// Modify is not yet supported for the sql backend
|
||||
func (h databaseHandler) Modify(boundDN string, req ldap.ModifyRequest, conn net.Conn) (result ldap.LDAPResultCode, err error) {
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
// Delete is not yet supported for the sql backend
|
||||
func (h databaseHandler) Delete(boundDN string, deleteDN string, conn net.Conn) (result ldap.LDAPResultCode, err error) {
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
func (h databaseHandler) FindUser(userName string, searchByUPN bool) (f bool, u config.User, err error) {
|
||||
var criterion string
|
||||
if searchByUPN {
|
||||
criterion = "lower(u.mail)"
|
||||
} else {
|
||||
criterion = "lower(u.name)"
|
||||
}
|
||||
|
||||
user := config.User{}
|
||||
found := false
|
||||
|
||||
err = h.database.cnx.QueryRow(fmt.Sprintf(`
|
||||
SELECT u.uidnumber,u.primarygroup,u.passbcrypt,u.passsha256,u.otpsecret,u.yubikey
|
||||
FROM users u WHERE %s=%s`,
|
||||
criterion,
|
||||
h.sqlBackend.GetPrepareSymbol()), userName).Scan(
|
||||
&user.UIDNumber, &user.PrimaryGroup, &user.PassBcrypt, &user.PassSHA256, &user.OTPSecret, &user.Yubikey)
|
||||
if err == nil {
|
||||
found = true
|
||||
|
||||
if !h.cfg.Behaviors.IgnoreCapabilities {
|
||||
capability := config.Capability{}
|
||||
rows, err := h.database.cnx.Query(fmt.Sprintf(`
|
||||
SELECT c.action,c.object
|
||||
FROM capabilities c WHERE userid=%s`,
|
||||
h.sqlBackend.GetPrepareSymbol()), user.UIDNumber)
|
||||
if err == nil {
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&capability.Action, &capability.Object)
|
||||
if err == nil {
|
||||
user.Capabilities = append(user.Capabilities, capability)
|
||||
}
|
||||
}
|
||||
}
|
||||
defer rows.Close()
|
||||
}
|
||||
}
|
||||
|
||||
return found, user, err
|
||||
}
|
||||
|
||||
func (h databaseHandler) FindGroup(groupName string) (f bool, g config.Group, err error) {
|
||||
group := config.Group{}
|
||||
found := false
|
||||
|
||||
err = h.database.cnx.QueryRow(fmt.Sprintf(`
|
||||
SELECT g.gidnumber FROM groups g WHERE lower(name)=%s`, h.sqlBackend.GetPrepareSymbol()), groupName).Scan(
|
||||
&group.GIDNumber)
|
||||
if err == nil {
|
||||
found = true
|
||||
}
|
||||
|
||||
return found, group, err
|
||||
}
|
||||
|
||||
func (h databaseHandler) FindPosixAccounts() (entrylist []*ldap.Entry, err error) {
|
||||
entries := []*ldap.Entry{}
|
||||
|
||||
h.MemGroups, err = h.memoizeGroups()
|
||||
if err != nil {
|
||||
return entries, err
|
||||
}
|
||||
|
||||
rows, err := h.database.cnx.Query(`
|
||||
SELECT u.name,u.uidnumber,u.primarygroup,u.passbcrypt,u.passsha256,u.otpsecret,u.yubikey,u.othergroups,u.givenname,u.sn,u.mail,u.loginshell,u.homedirectory,u.disabled
|
||||
FROM users u`)
|
||||
if err != nil {
|
||||
return entries, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var otherGroups string
|
||||
var disabled int
|
||||
u := config.User{}
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&u.Name, &u.UIDNumber, &u.PrimaryGroup, &u.PassBcrypt, &u.PassSHA256, &u.OTPSecret, &u.Yubikey, &otherGroups, &u.GivenName, &u.SN, &u.Mail, &u.LoginShell, &u.Homedir, &disabled)
|
||||
if err != nil {
|
||||
return entries, err
|
||||
}
|
||||
u.OtherGroups = h.commaListToTable(otherGroups)
|
||||
u.Disabled = h.intToBool(disabled)
|
||||
|
||||
entries = append(entries, h.getAccount(u))
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func (h databaseHandler) FindPosixGroups() (entrylist []*ldap.Entry, err error) {
|
||||
entries := []*ldap.Entry{}
|
||||
|
||||
h.MemGroups, err = h.memoizeGroups()
|
||||
if err != nil {
|
||||
return entries, err
|
||||
}
|
||||
|
||||
for _, g := range h.MemGroups {
|
||||
entries = append(entries, h.getGroup(g))
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func (h databaseHandler) Close(boundDn string, conn net.Conn) error {
|
||||
stats.Frontend.Add("closes", 1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h databaseHandler) intToBool(value int) bool {
|
||||
if value == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (h databaseHandler) commaListToTable(commaList string) []int {
|
||||
if len(commaList) == 0 {
|
||||
return make([]int, 0)
|
||||
}
|
||||
rowsAsStrings := strings.Split(commaList, ",")
|
||||
rowsAsInts := make([]int, len(rowsAsStrings))
|
||||
for i, v := range rowsAsStrings {
|
||||
iv, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return rowsAsInts
|
||||
}
|
||||
rowsAsInts[i] = iv
|
||||
}
|
||||
return rowsAsInts
|
||||
}
|
||||
|
||||
func (h databaseHandler) memoizeGroups() ([]config.Group, error) {
|
||||
workMemGroups := make([]*config.Group, 0)
|
||||
rows, err := h.database.cnx.Query(`
|
||||
SELECT g1.name,g1.gidnumber,ig.includegroupid
|
||||
FROM groups g1
|
||||
LEFT JOIN includegroups ig ON g1.gidnumber=ig.parentgroupid
|
||||
LEFT JOIN groups g2 ON ig.includegroupid=g2.gidnumber`)
|
||||
if err != nil {
|
||||
return nil, errors.New("Unable to memoize groups list")
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var groupName string
|
||||
var groupId int
|
||||
var includeId sql.NullInt64 // Store includeid from left join
|
||||
var pg *config.Group
|
||||
recentId := -1 // id of recently updated group
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&groupName, &groupId, &includeId)
|
||||
if err != nil {
|
||||
return nil, errors.New("Unable to memoize groups list")
|
||||
}
|
||||
if recentId != groupId {
|
||||
recentId = groupId
|
||||
g := config.Group{Name: groupName, GIDNumber: groupId}
|
||||
pg = &g // To manipulate end of slice
|
||||
workMemGroups = append(workMemGroups, &g)
|
||||
}
|
||||
if includeId.Valid {
|
||||
pg.IncludeGroups = append(pg.IncludeGroups, int(includeId.Int64))
|
||||
}
|
||||
}
|
||||
memGroups := make([]config.Group, len(workMemGroups))
|
||||
for i, v := range workMemGroups {
|
||||
memGroups[i] = config.Group{Name: v.Name, GIDNumber: v.GIDNumber, IncludeGroups: v.IncludeGroups}
|
||||
}
|
||||
return memGroups, nil
|
||||
}
|
||||
|
||||
// Used when looking up Posix Groups
|
||||
func (h databaseHandler) getGroupMembers(gid int) []string {
|
||||
members := make(map[string]bool)
|
||||
|
||||
rows, err := h.database.cnx.Query(`
|
||||
SELECT u.name,u.uidnumber,u.primarygroup,u.passbcrypt,u.passsha256,u.otpsecret,u.yubikey,u.othergroups
|
||||
FROM users u WHERE lower(u.name)=?`,
|
||||
)
|
||||
if err != nil {
|
||||
// Silent fail... for now
|
||||
return []string{}
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var otherGroups string
|
||||
u := config.User{}
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&u.Name, &u.UIDNumber, &u.PrimaryGroup, &u.PassBcrypt, &u.PassSHA256, &u.OTPSecret, &u.Yubikey, &otherGroups)
|
||||
if err != nil {
|
||||
return []string{}
|
||||
}
|
||||
if u.PrimaryGroup == gid {
|
||||
dn := fmt.Sprintf("cn=%s,ou=%s,%s", u.Name, h.getGroupName(u.PrimaryGroup), h.backend.BaseDN)
|
||||
members[dn] = true
|
||||
} else {
|
||||
u.OtherGroups = h.commaListToTable(otherGroups)
|
||||
for _, othergid := range u.OtherGroups {
|
||||
if othergid == gid {
|
||||
dn := fmt.Sprintf("cn=%s,ou=%s,%s", u.Name, h.getGroupName(u.PrimaryGroup), h.backend.BaseDN)
|
||||
members[dn] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, g := range h.MemGroups {
|
||||
if gid == g.GIDNumber {
|
||||
for _, includegroupid := range g.IncludeGroups {
|
||||
if includegroupid != gid {
|
||||
includegroupmembers := h.getGroupMembers(includegroupid)
|
||||
|
||||
for _, includegroupmember := range includegroupmembers {
|
||||
members[includegroupmember] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m := []string{}
|
||||
for k := range members {
|
||||
m = append(m, k)
|
||||
}
|
||||
|
||||
sort.Strings(m)
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// Used exclusively when looking up Posix Groups
|
||||
func (h databaseHandler) getGroupMemberIDs(gid int) []string {
|
||||
members := make(map[string]bool)
|
||||
rows, err := h.database.cnx.Query(`
|
||||
SELECT u.name,u.uidnumber,u.primarygroup,u.passbcrypt,u.passsha256,u.otpsecret,u.yubikey,u.othergroups
|
||||
FROM users u`)
|
||||
if err != nil {
|
||||
// Silent fail... for now
|
||||
return []string{}
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var otherGroups string
|
||||
u := config.User{}
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&u.Name, &u.UIDNumber, &u.PrimaryGroup, &u.PassBcrypt, &u.PassSHA256, &u.OTPSecret, &u.Yubikey, &otherGroups)
|
||||
if err != nil {
|
||||
return []string{}
|
||||
}
|
||||
if u.PrimaryGroup == gid {
|
||||
members[u.Name] = true
|
||||
} else {
|
||||
u.OtherGroups = h.commaListToTable(otherGroups)
|
||||
for _, othergid := range u.OtherGroups {
|
||||
if othergid == gid {
|
||||
members[u.Name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, g := range h.MemGroups {
|
||||
if gid == g.GIDNumber {
|
||||
for _, includegroupid := range g.IncludeGroups {
|
||||
if includegroupid == gid {
|
||||
h.log.V(3).Info(fmt.Sprintf("Group: %d - Ignoring myself as included group", includegroupid))
|
||||
} else {
|
||||
includegroupmemberids := h.getGroupMemberIDs(includegroupid)
|
||||
|
||||
for _, includegroupmemberid := range includegroupmemberids {
|
||||
members[includegroupmemberid] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m := []string{}
|
||||
for k := range members {
|
||||
m = append(m, k)
|
||||
}
|
||||
|
||||
sort.Strings(m)
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// Invoked for every user being returned from our database
|
||||
func (h databaseHandler) getGroupDNs(gids []int) []string {
|
||||
groups := make(map[string]bool)
|
||||
for _, gid := range gids {
|
||||
for _, g := range h.MemGroups {
|
||||
if g.GIDNumber == gid {
|
||||
dn := fmt.Sprintf("cn=%s,ou=groups,%s", g.Name, h.backend.BaseDN)
|
||||
groups[dn] = true
|
||||
}
|
||||
|
||||
for _, includegroupid := range g.IncludeGroups {
|
||||
if includegroupid == gid && g.GIDNumber != gid {
|
||||
includegroupdns := h.getGroupDNs([]int{g.GIDNumber})
|
||||
|
||||
for _, includegroupdn := range includegroupdns {
|
||||
groups[includegroupdn] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g := []string{}
|
||||
for k := range groups {
|
||||
g = append(g, k)
|
||||
}
|
||||
|
||||
sort.Strings(g)
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
// Invoked for every user being returned from our database
|
||||
func (h databaseHandler) getGroupName(gid int) string {
|
||||
for _, g := range h.MemGroups {
|
||||
if g.GIDNumber == gid {
|
||||
return g.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Toolbox
|
||||
func (h databaseHandler) getGroup(g config.Group) *ldap.Entry {
|
||||
attrs := []*ldap.EntryAttribute{}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"cn", []string{g.Name}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"description", []string{fmt.Sprintf("%s via LDAP", g.Name)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"gidNumber", []string{fmt.Sprintf("%d", g.GIDNumber)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"objectClass", []string{"posixGroup"}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"uniqueMember", h.getGroupMembers(g.GIDNumber)})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"memberUid", h.getGroupMemberIDs(g.GIDNumber)})
|
||||
dn := fmt.Sprintf("cn=%s,ou=groups,%s", g.Name, h.backend.BaseDN)
|
||||
return &ldap.Entry{dn, attrs}
|
||||
}
|
||||
|
||||
func (h databaseHandler) getAccount(u config.User) *ldap.Entry {
|
||||
attrs := []*ldap.EntryAttribute{}
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"cn", []string{u.Name}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"uid", []string{u.Name}})
|
||||
|
||||
if len(u.GivenName) > 0 {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"givenName", []string{u.GivenName}})
|
||||
}
|
||||
|
||||
if len(u.SN) > 0 {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"sn", []string{u.SN}})
|
||||
}
|
||||
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"ou", []string{h.getGroupName(u.PrimaryGroup)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"uidNumber", []string{fmt.Sprintf("%d", u.UIDNumber)}})
|
||||
|
||||
if u.Disabled {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"accountStatus", []string{"inactive"}})
|
||||
} else {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"accountStatus", []string{"active"}})
|
||||
}
|
||||
|
||||
if len(u.Mail) > 0 {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"mail", []string{u.Mail}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"userPrincipalName", []string{u.Mail}})
|
||||
}
|
||||
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"objectClass", []string{"posixAccount"}})
|
||||
|
||||
if len(u.LoginShell) > 0 {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"loginShell", []string{u.LoginShell}})
|
||||
} else {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"loginShell", []string{"/bin/bash"}})
|
||||
}
|
||||
|
||||
if len(u.Homedir) > 0 {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"homeDirectory", []string{u.Homedir}})
|
||||
} else {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"homeDirectory", []string{"/home/" + u.Name}})
|
||||
}
|
||||
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"description", []string{fmt.Sprintf("%s via LDAP", u.Name)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"gecos", []string{fmt.Sprintf("%s via LDAP", u.Name)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"gidNumber", []string{fmt.Sprintf("%d", u.PrimaryGroup)}})
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"memberOf", h.getGroupDNs(append(u.OtherGroups, u.PrimaryGroup))})
|
||||
if len(u.SSHKeys) > 0 {
|
||||
attrs = append(attrs, &ldap.EntryAttribute{"sshPublicKey", u.SSHKeys})
|
||||
}
|
||||
dn := fmt.Sprintf("cn=%s,ou=%s,%s", u.Name, h.getGroupName(u.PrimaryGroup), h.backend.BaseDN)
|
||||
return &ldap.Entry{dn, attrs}
|
||||
}
|
||||
|
||||
func main() {}
|
||||
@@ -1,58 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
|
||||
"github.com/glauth/glauth/pkg/handler"
|
||||
)
|
||||
|
||||
type MysqlBackend struct {
|
||||
}
|
||||
|
||||
func NewMySQLHandler(opts ...handler.Option) handler.Handler {
|
||||
backend := MysqlBackend{}
|
||||
return NewDatabaseHandler(backend, opts...)
|
||||
}
|
||||
|
||||
func (b MysqlBackend) GetDriverName() string {
|
||||
return "mysql"
|
||||
}
|
||||
|
||||
func (b MysqlBackend) GetPrepareSymbol() string {
|
||||
return "?"
|
||||
}
|
||||
|
||||
// Create db/schema if necessary
|
||||
func (b MysqlBackend) CreateSchema(db *sql.DB) {
|
||||
statement, _ := db.Prepare(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
uidnumber INTEGER NOT NULL,
|
||||
primarygroup INTEGER NOT NULL,
|
||||
othergroups VARCHAR(1024) DEFAULT '',
|
||||
givenname VARCHAR(64) DEFAULT '',
|
||||
sn VARCHAR(64) DEFAULT '',
|
||||
mail VARCHAR(254) DEFAULT '',
|
||||
loginshell VARCHAR(64) DEFAULT '',
|
||||
homedirectory VARCHAR(64) DEFAULT '',
|
||||
disabled SMALLINT DEFAULT 0,
|
||||
passsha256 VARCHAR(64) DEFAULT '',
|
||||
passbcrypt VARCHAR(64) DEFAULT '',
|
||||
otpsecret VARCHAR(64) DEFAULT '',
|
||||
yubikey VARCHAR(128) DEFAULT '')
|
||||
`)
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE UNIQUE INDEX idx_user_name on users(name)")
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE TABLE IF NOT EXISTS groups (id INTEGER AUTO_INCREMENT PRIMARY KEY, name VARCHAR(64) NOT NULL, gidnumber INTEGER NOT NULL)")
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE UNIQUE INDEX idx_group_name on groups(name)")
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE TABLE IF NOT EXISTS includegroups (id INTEGER AUTO_INCREMENT PRIMARY KEY, parentgroupid INTEGER NOT NULL, includegroupid INTEGER NOT NULL)")
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE TABLE IF NOT EXISTS capabilities (id INTEGER AUTO_INCREMENT PRIMARY KEY, userid INTEGER NOT NULL, action VARCHAR(128) NOT NULL, object VARCHAR(128) NOT NULL)")
|
||||
statement.Exec()
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
|
||||
"github.com/glauth/glauth/pkg/handler"
|
||||
)
|
||||
|
||||
type PostgresBackend struct {
|
||||
}
|
||||
|
||||
func NewPostgresHandler(opts ...handler.Option) handler.Handler {
|
||||
backend := PostgresBackend{}
|
||||
return NewDatabaseHandler(backend, opts...)
|
||||
}
|
||||
|
||||
func (b PostgresBackend) GetDriverName() string {
|
||||
return "postgres"
|
||||
}
|
||||
|
||||
func (b PostgresBackend) GetPrepareSymbol() string {
|
||||
return "$1"
|
||||
}
|
||||
|
||||
// Create db/schema if necessary
|
||||
func (b PostgresBackend) CreateSchema(db *sql.DB) {
|
||||
statement, _ := db.Prepare(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
uidnumber INTEGER NOT NULL,
|
||||
primarygroup INTEGER NOT NULL,
|
||||
othergroups TEXT DEFAULT '',
|
||||
givenname TEXT DEFAULT '',
|
||||
sn TEXT DEFAULT '',
|
||||
mail TEXT DEFAULT '',
|
||||
loginshell TEXT DEFAULT '',
|
||||
homedirectory TEXT DEFAULT '',
|
||||
disabled SMALLINT DEFAULT 0,
|
||||
passsha256 TEXT DEFAULT '',
|
||||
passbcrypt TEXT DEFAULT '',
|
||||
otpsecret TEXT DEFAULT '',
|
||||
yubikey TEXT DEFAULT '')
|
||||
`)
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE UNIQUE INDEX IF NOT EXISTS idx_user_name on users(name)")
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE TABLE IF NOT EXISTS groups (id SERIAL PRIMARY KEY, name TEXT NOT NULL, gidnumber INTEGER NOT NULL)")
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE UNIQUE INDEX IF NOT EXISTS idx_group_name on groups(name)")
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE TABLE IF NOT EXISTS includegroups (id SERIAL PRIMARY KEY, parentgroupid INTEGER NOT NULL, includegroupid INTEGER NOT NULL)")
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE TABLE IF NOT EXISTS capabilities (id SERIAL PRIMARY KEY, userid INTEGER NOT NULL, action TEXT NOT NULL, object TEXT NOT NULL)")
|
||||
statement.Exec()
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
#################
|
||||
# glauth.conf
|
||||
|
||||
#################
|
||||
# General configuration.
|
||||
debug = true
|
||||
# syslog = true
|
||||
#
|
||||
# Enable hot-reload of configuration on changes
|
||||
# - does NOT work [ldap], [ldaps], [backend] or [api] sections
|
||||
# watchconfig = true
|
||||
|
||||
#################
|
||||
# yubikeyclientid = "yubi-api-clientid"
|
||||
# yubikeysecret = "yubi-api-secret"
|
||||
|
||||
#################
|
||||
# Server configuration.
|
||||
#
|
||||
# This is the old server-config format
|
||||
# - will still work, but please use new format, shown below
|
||||
#
|
||||
# [frontend]
|
||||
# listen = "localhost:8484"
|
||||
# tls = false
|
||||
# cert ="certs/server.crt"
|
||||
# key = "certs/server.key"
|
||||
|
||||
# This ([ldap] and [ldaps]) is the new server-config format
|
||||
[ldap]
|
||||
enabled = true
|
||||
listen = "0.0.0.0:3893"
|
||||
|
||||
[ldaps]
|
||||
enabled = false
|
||||
listen = "0.0.0.0:3894"
|
||||
cert = "certs/server.crt"
|
||||
key = "certs/server.key"
|
||||
|
||||
#################
|
||||
# The backend section controls the data store.
|
||||
[backend]
|
||||
datastore = "plugin"
|
||||
# If "plugin," uncomment the line below
|
||||
plugin = "bin/sqlite.so"
|
||||
pluginhandler = "NewSQLiteHandler"
|
||||
baseDN = "dc=glauth,dc=com"
|
||||
|
||||
# // MySQL //
|
||||
# Uncomment this line to configure your MySQL database connection string:
|
||||
# database = "glauth:glauth@tcp(127.0.0.1:3306)/glauth"
|
||||
# Creating database:
|
||||
# create database glauth identified;
|
||||
# grant all on glauth.* to glauth@localhost identified by 'glauth';
|
||||
# // Postgres //
|
||||
# Uncomment this line to configure your Postgres database connection string:
|
||||
# database = "host=127.0.0.1 port=5432 dbname=glauth user=glauth password=glauth sslmode=disable"
|
||||
# Creating database:
|
||||
# CREATE DATABASE <dbname>;
|
||||
# CREATE USER <username> WITH PASSWORD '<userpassword>'
|
||||
# GRANT ALL PRIVILEGES ON DATABASE <dbname> to <username>;
|
||||
# // SQLite //
|
||||
# Uncomment this line to configure your SQLite database connection string:
|
||||
database = "gl.db"
|
||||
|
||||
## Configure dn format to use structures like
|
||||
## "uid=serviceuser,cn=svcaccts,$BASEDN" instead of "cn=serviceuser,ou=svcaccts,$BASEDN"
|
||||
## to help ease migrations from other LDAP systems
|
||||
# nameformat = "uid"
|
||||
# groupformat = "cn"
|
||||
|
||||
## Configure ssh-key attribute name, default is 'sshPublicKey'
|
||||
# sshkeyattr = "ipaSshPubKey"
|
||||
|
||||
#################
|
||||
# Enable and configure the optional REST API here.
|
||||
[api]
|
||||
enabled = true
|
||||
tls = false # enable TLS for production!!
|
||||
listen = "0.0.0.0:5555"
|
||||
cert = "cert.pem"
|
||||
key = "key.pem"
|
||||
@@ -1,45 +0,0 @@
|
||||
#################
|
||||
# glauth.conf
|
||||
|
||||
#################
|
||||
# General configuration.
|
||||
#debug = true
|
||||
syslog = true
|
||||
|
||||
#################
|
||||
# Server configuration.
|
||||
[ldap]
|
||||
enabled = true
|
||||
listen = "0.0.0.0:389"
|
||||
|
||||
[ldaps]
|
||||
enabled = true
|
||||
listen = "0.0.0.0:636"
|
||||
cert = "glauth.crt"
|
||||
key = "glauth.key"
|
||||
|
||||
#################
|
||||
# The backend section controls the data store.
|
||||
[backend]
|
||||
datastore = "ldap"
|
||||
servers = [ "ldaps://server1:636", "ldaps://server2:636" ]
|
||||
|
||||
# Ignore SSL certificate errors when connecting to backend LDAP servers.
|
||||
#insecure = true
|
||||
|
||||
[helper]
|
||||
enabled = true
|
||||
datastore = "plugin"
|
||||
plugin = "bin/sqlite.so"
|
||||
pluginhandler = "NewSQLiteHandler"
|
||||
database = "glh.db"
|
||||
|
||||
#################
|
||||
# Enable and configure the optional REST API here.
|
||||
[api]
|
||||
enabled = true
|
||||
tls = true
|
||||
listen = "localhost:5555"
|
||||
cert = "glauth.crt"
|
||||
key = "glauth.key"
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
|
||||
"github.com/glauth/glauth/pkg/handler"
|
||||
)
|
||||
|
||||
type SqliteBackend struct {
|
||||
}
|
||||
|
||||
func NewSQLiteHandler(opts ...handler.Option) handler.Handler {
|
||||
backend := SqliteBackend{}
|
||||
return NewDatabaseHandler(backend, opts...)
|
||||
}
|
||||
|
||||
func (b SqliteBackend) GetDriverName() string {
|
||||
return "sqlite3"
|
||||
}
|
||||
|
||||
func (b SqliteBackend) GetPrepareSymbol() string {
|
||||
return "?"
|
||||
}
|
||||
|
||||
// Create db/schema if necessary
|
||||
func (b SqliteBackend) CreateSchema(db *sql.DB) {
|
||||
statement, _ := db.Prepare(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
uidnumber INTEGER NOT NULL,
|
||||
primarygroup INTEGER NOT NULL,
|
||||
othergroups TEXT DEFAULT '',
|
||||
givenname TEXT DEFAULT '',
|
||||
sn TEXT DEFAULT '',
|
||||
mail TEXT DEFAULT '',
|
||||
loginshell TYEXT DEFAULT '',
|
||||
homedirectory TEXT DEFAULT '',
|
||||
disabled SMALLINT DEFAULT 0,
|
||||
passsha256 TEXT DEFAULT '',
|
||||
passbcrypt TEXT DEFAULT '',
|
||||
otpsecret TEXT DEFAULT '',
|
||||
yubikey TEXT DEFAULT '')
|
||||
`)
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE UNIQUE INDEX IF NOT EXISTS idx_user_name on users(name)")
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE TABLE IF NOT EXISTS groups (id INTEGER PRIMARY KEY, name TEXT NOT NULL, gidnumber INTEGER NOT NULL)")
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE UNIQUE INDEX IF NOT EXISTS idx_group_name on groups(name)")
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE TABLE IF NOT EXISTS includegroups (id INTEGER PRIMARY KEY, parentgroupid INTEGER NOT NULL, includegroupid INTEGER NOT NULL)")
|
||||
statement.Exec()
|
||||
statement, _ = db.Prepare("CREATE TABLE IF NOT EXISTS capabilities (id INTEGER PRIMARY KEY, userid INTEGER NOT NULL, action TEXT NOT NULL, object TEXT NOT NULL)")
|
||||
statement.Exec()
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/glauth/glauth/pkg/config"
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Logger logr.Logger
|
||||
Config *config.Config
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(val logr.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
|
||||
// Context provides a function to set the context option.
|
||||
func Context(val context.Context) Option {
|
||||
return func(o *Options) {
|
||||
o.Context = val
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"plugin"
|
||||
|
||||
"github.com/GeertJohan/yubigo"
|
||||
"github.com/glauth/glauth/pkg/config"
|
||||
"github.com/glauth/glauth/pkg/handler"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/nmcclain/ldap"
|
||||
)
|
||||
|
||||
type LdapSvc struct {
|
||||
log logr.Logger
|
||||
c *config.Config
|
||||
yubiAuth *yubigo.YubiAuth
|
||||
l *ldap.Server
|
||||
}
|
||||
|
||||
func NewServer(opts ...Option) (*LdapSvc, error) {
|
||||
options := newOptions(opts...)
|
||||
|
||||
s := LdapSvc{
|
||||
log: options.Logger,
|
||||
c: options.Config,
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
if len(s.c.YubikeyClientID) > 0 && len(s.c.YubikeySecret) > 0 {
|
||||
s.yubiAuth, err = yubigo.NewYubiAuth(s.c.YubikeyClientID, s.c.YubikeySecret)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.New("Yubikey Auth failed")
|
||||
}
|
||||
}
|
||||
|
||||
var helper handler.Handler
|
||||
|
||||
loh := handler.NewLDAPOpsHelper()
|
||||
|
||||
// instantiate the helper, if any
|
||||
if s.c.Helper.Enabled {
|
||||
switch s.c.Helper.Datastore {
|
||||
case "config":
|
||||
helper = handler.NewConfigHandler(
|
||||
handler.Logger(s.log),
|
||||
handler.Config(s.c),
|
||||
handler.YubiAuth(s.yubiAuth),
|
||||
handler.LDAPHelper(loh),
|
||||
)
|
||||
case "plugin":
|
||||
plug, err := plugin.Open(s.c.Helper.Plugin)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("Unable to load specified helper plugin: %s", err))
|
||||
}
|
||||
nph, err := plug.Lookup(s.c.Helper.PluginHandler)
|
||||
if err != nil {
|
||||
return nil, errors.New("Unable to find 'NewPluginHandler' in loaded helper plugin")
|
||||
}
|
||||
initFunc, ok := nph.(func(...handler.Option) handler.Handler)
|
||||
|
||||
if !ok {
|
||||
return nil, errors.New("Loaded helper plugin lacks a proper NewPluginHandler function")
|
||||
}
|
||||
// Normally, here, we would somehow have imported our plugin into our
|
||||
// handler namespace. Oops?
|
||||
helper = initFunc(
|
||||
handler.Logger(s.log),
|
||||
handler.Config(s.c),
|
||||
handler.YubiAuth(s.yubiAuth),
|
||||
handler.LDAPHelper(loh),
|
||||
)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported helper %s - must be one of 'config', 'plugin'", s.c.Helper.Datastore)
|
||||
}
|
||||
s.log.V(3).Info("Using helper", "datastore", s.c.Helper.Datastore)
|
||||
}
|
||||
|
||||
backendCounter := -1
|
||||
allHandlers := handler.HandlerWrapper{Handlers: make([]handler.Handler, 10), Count: &backendCounter}
|
||||
|
||||
// configure the backends
|
||||
s.l = ldap.NewServer()
|
||||
s.l.EnforceLDAP = true
|
||||
for i, backend := range s.c.Backends {
|
||||
var h handler.Handler
|
||||
switch backend.Datastore {
|
||||
case "ldap":
|
||||
h = handler.NewLdapHandler(
|
||||
handler.Backend(backend),
|
||||
handler.Handlers(allHandlers),
|
||||
handler.Logger(s.log),
|
||||
handler.Helper(helper),
|
||||
)
|
||||
case "owncloud":
|
||||
h = handler.NewOwnCloudHandler(
|
||||
handler.Backend(backend),
|
||||
handler.Logger(s.log),
|
||||
)
|
||||
case "config":
|
||||
h = handler.NewConfigHandler(
|
||||
handler.Backend(backend),
|
||||
handler.Logger(s.log),
|
||||
handler.Config(s.c), // TODO only used to access Users and Groups, move that to dedicated options
|
||||
handler.YubiAuth(s.yubiAuth),
|
||||
handler.LDAPHelper(loh),
|
||||
)
|
||||
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))
|
||||
}
|
||||
nph, err := plug.Lookup(backend.PluginHandler)
|
||||
if err != nil {
|
||||
return nil, errors.New("Unable to find 'NewPluginHandler' in loaded backend plugin")
|
||||
}
|
||||
initFunc, ok := nph.(func(...handler.Option) handler.Handler)
|
||||
|
||||
if !ok {
|
||||
return nil, errors.New("Loaded backend plugin lacks a proper NewPluginHandler function")
|
||||
}
|
||||
// Normally, here, we would somehow have imported our plugin into our
|
||||
// handler namespace. Oops?
|
||||
h = initFunc(
|
||||
handler.Backend(backend),
|
||||
handler.Logger(s.log),
|
||||
handler.Config(s.c),
|
||||
handler.YubiAuth(s.yubiAuth),
|
||||
handler.LDAPHelper(loh),
|
||||
)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported backend %s - must be one of 'config', 'ldap','owncloud' or 'plugin'", backend.Datastore)
|
||||
}
|
||||
s.log.V(3).Info("Loading backend", "datastore", backend.Datastore, "position", i)
|
||||
|
||||
// Only our first backend will answer proper LDAP queries.
|
||||
// Note that this could evolve towars something nicer where we would maintain
|
||||
// multiple binders in addition to the existing multiple LDAP backends
|
||||
if i == 0 {
|
||||
s.l.BindFunc("", h)
|
||||
s.l.SearchFunc("", h)
|
||||
s.l.CloseFunc("", h)
|
||||
}
|
||||
allHandlers.Handlers[i] = h
|
||||
backendCounter++
|
||||
}
|
||||
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
// ListenAndServe listens on the TCP network address s.c.LDAP.Listen
|
||||
func (s *LdapSvc) ListenAndServe() error {
|
||||
s.log.V(3).Info("LDAP server listening", "address", s.c.LDAP.Listen)
|
||||
return s.l.ListenAndServe(s.c.LDAP.Listen)
|
||||
}
|
||||
|
||||
// ListenAndServeTLS listens on the TCP network address s.c.LDAPS.Listen
|
||||
func (s *LdapSvc) ListenAndServeTLS() error {
|
||||
s.log.V(3).Info("LDAPS server listening", "address", s.c.LDAPS.Listen)
|
||||
return s.l.ListenAndServeTLS(
|
||||
s.c.LDAPS.Listen,
|
||||
s.c.LDAPS.Cert,
|
||||
s.c.LDAPS.Key,
|
||||
)
|
||||
}
|
||||
|
||||
// Shutdown ends listeners by sending true to the ldap serves quit channel
|
||||
func (s *LdapSvc) Shutdown() {
|
||||
s.l.Quit <- true
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package stats
|
||||
|
||||
import "expvar"
|
||||
|
||||
// exposed expvar variables
|
||||
var (
|
||||
Frontend = expvar.NewMap("proxy_frontend")
|
||||
Backend = expvar.NewMap("proxy_backend")
|
||||
General = expvar.NewMap("proxy")
|
||||
)
|
||||
@@ -1,5 +0,0 @@
|
||||
package stats
|
||||
|
||||
type Stringer string
|
||||
|
||||
func (s Stringer) String() string { return "\"" + string(s) + "\"" }
|
||||
@@ -1,42 +0,0 @@
|
||||
#################
|
||||
# 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://localhost: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"
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#################
|
||||
# glauth.conf
|
||||
|
||||
#################
|
||||
# General configuration.
|
||||
#debug = true
|
||||
syslog = true
|
||||
|
||||
#################
|
||||
# Server configuration.
|
||||
[ldap]
|
||||
enabled = true
|
||||
listen = "0.0.0.0:389"
|
||||
|
||||
[ldaps]
|
||||
enabled = true
|
||||
listen = "0.0.0.0:636"
|
||||
cert = "glauth.crt"
|
||||
key = "glauth.key"
|
||||
|
||||
#################
|
||||
# The backend section controls the data store.
|
||||
[backend]
|
||||
datastore = "ldap"
|
||||
servers = [ "ldaps://server1:636", "ldaps://server2:636" ]
|
||||
|
||||
# Ignore SSL certificate errors when connecting to backend LDAP servers.
|
||||
#insecure = true
|
||||
|
||||
#################
|
||||
# Enable and configure the optional REST API here.
|
||||
[api]
|
||||
enabled = true
|
||||
tls = true
|
||||
listen = "localhost:5555"
|
||||
cert = "glauth.crt"
|
||||
key = "glauth.key"
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#################
|
||||
# General configuration.
|
||||
debug = true
|
||||
|
||||
#################
|
||||
# Server configuration.
|
||||
|
||||
# This ([ldap] and [ldaps]) is the new server-config format
|
||||
[ldap]
|
||||
enabled = true
|
||||
listen = "localhost:3893"
|
||||
|
||||
[ldaps]
|
||||
enabled = false
|
||||
|
||||
#################
|
||||
# The backend section controls the data store.
|
||||
[backend]
|
||||
datastore = "owncloud"
|
||||
baseDN = "dc=owncloud,dc=com"
|
||||
UseGraphAPI = true
|
||||
servers = ["https://cloud.example.com/index.php/apps/graphapi/v1.0/"]
|
||||
# if not using the graph API only provide the url to the owncloud instance
|
||||
#UseGraphAPI = false
|
||||
#servers = ["https://other.example.com/owncloud/"]
|
||||
@@ -1,151 +0,0 @@
|
||||
#################
|
||||
# glauth.conf
|
||||
|
||||
#################
|
||||
# General configuration.
|
||||
debug = true
|
||||
# syslog = true
|
||||
#
|
||||
# Enable hot-reload of configuration on changes
|
||||
# - does NOT work [ldap], [ldaps], [backend] or [api] sections
|
||||
# watchconfig = true
|
||||
|
||||
#################
|
||||
# yubikeyclientid = "yubi-api-clientid"
|
||||
# yubikeysecret = "yubi-api-secret"
|
||||
|
||||
#################
|
||||
# Server configuration.
|
||||
[ldap]
|
||||
enabled = true
|
||||
# run on a non privileged port
|
||||
listen = "0.0.0.0:3893"
|
||||
|
||||
[ldaps]
|
||||
# to enable ldaps genrerate a certificate, eg. with:
|
||||
# openssl req -x509 -newkey rsa:4096 -keyout glauth.key -out glauth.crt -days 365 -nodes -subj '/CN=`hostname`'
|
||||
enabled = false
|
||||
listen = "0.0.0.0:3894"
|
||||
cert = "glauth.crt"
|
||||
key = "glauth.key"
|
||||
|
||||
#################
|
||||
# The backend section controls the data store.
|
||||
[backend]
|
||||
datastore = "config"
|
||||
baseDN = "dc=glauth,dc=com"
|
||||
|
||||
## Configure dn format to use structures like
|
||||
## "uid=serviceuser,cn=svcaccts,$BASEDN" instead of "cn=serviceuser,ou=svcaccts,$BASEDN"
|
||||
## to help ease migrations from other LDAP systems
|
||||
# nameformat = "uid"
|
||||
# groupformat = "cn"
|
||||
|
||||
## Configure ssh-key attribute name, default is 'sshPublicKey'
|
||||
# sshkeyattr = "ipaSshPubKey"
|
||||
|
||||
[behaviors]
|
||||
# Ignore all capabilities restrictions, for instance allowing every user to perform a search
|
||||
IgnoreCapabilities = false
|
||||
# Enable a "fail2ban" type backoff mechanism temporarily banning repeated failed login attempts
|
||||
LimitFailedBinds = true
|
||||
# How many failed login attempts are allowed before a ban is imposed
|
||||
NumberOfFailedBinds = 3
|
||||
# How long (in seconds) is the window for failed login attempts
|
||||
PeriodOfFailedBinds = 10
|
||||
# How long (in seconds) is the ban duration
|
||||
BlockFailedBindsFor = 60
|
||||
# Clean learnt IP addresses every N seconds
|
||||
PruneSourceTableEvery = 600
|
||||
# Clean learnt IP addresses not seen in N seconds
|
||||
PruneSourcesOlderThan = 600
|
||||
|
||||
#################
|
||||
# The users section contains a hardcoded list of valid users.
|
||||
# to create a passSHA256: echo -n "mysecret" | openssl dgst -sha256
|
||||
[[users]]
|
||||
name = "hackers"
|
||||
uidnumber = 5001
|
||||
primarygroup = 5501
|
||||
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "ou=superheros,dc=glauth,dc=com"
|
||||
|
||||
# This user record shows all of the possible fields available
|
||||
[[users]]
|
||||
name = "johndoe"
|
||||
givenname="John"
|
||||
sn="Doe"
|
||||
mail = "jdoe@example.com"
|
||||
uidnumber = 5002
|
||||
primarygroup = 5501
|
||||
loginShell = "/bin/sh"
|
||||
homeDir = "/root"
|
||||
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
|
||||
sshkeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwVw3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4hC/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLVIfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XBYnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+txSuhw== rsa-key-20160209"]
|
||||
passappsha256 = [
|
||||
"c32255dbf6fd6b64883ec8801f793bccfa2a860f2b1ae1315cd95cdac1338efa", # TestAppPw1
|
||||
"c9853d5f2599e90497e9f8cc671bd2022b0fb5d1bd7cfff92f079e8f8f02b8d3", # TestAppPw2
|
||||
"4939efa7c87095dacb5e7e8b8cfb3a660fa1f5edcc9108f6d7ec20ea4d6b3a88", # TestAppPw3
|
||||
]
|
||||
|
||||
[[users]]
|
||||
name = "serviceuser"
|
||||
mail = "serviceuser@example.com"
|
||||
uidnumber = 5003
|
||||
primarygroup = 5502
|
||||
passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "*"
|
||||
|
||||
# Test user showing 2 factor auth authentication
|
||||
[[users]]
|
||||
name = "otpuser"
|
||||
uidnumber = 5004
|
||||
primarygroup = 5501
|
||||
passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret
|
||||
otpsecret = "3hnvnk4ycv44glzigd6s25j4dougs3rk"
|
||||
yubikey = "vvjrcfalhlaa"
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "ou=superheros,dc=glauth,dc=com"
|
||||
|
||||
[[users]]
|
||||
name = "uberhackers"
|
||||
uidnumber = 5005
|
||||
primarygroup = 5501
|
||||
# bcrypt format: hex($2y$2^<number of rounds>$<salt>$<hash>)
|
||||
passappbcrypt = [
|
||||
"243261243130244B62463462656F7265504F762E794F324957746D656541326B4B46596275674A79336A476845764B616D65446169784E41384F4432" # dogood
|
||||
]
|
||||
# uncomment and comment out above array to test password with otp code
|
||||
# passappbcrypt = "243261243130244B62463462656F7265504F762E794F324957746D656541326B4B46596275674A79336A476845764B616D65446169784E41384F4432" # dogood
|
||||
otpsecret = "3hnvnk4ycv44glzigd6s25j4dougs3rk"
|
||||
|
||||
|
||||
#################
|
||||
# The groups section contains a hardcoded list of valid users.
|
||||
[[groups]]
|
||||
name = "superheros"
|
||||
gidnumber = 5501
|
||||
|
||||
[[groups]]
|
||||
name = "svcaccts"
|
||||
gidnumber = 5502
|
||||
|
||||
[[groups]]
|
||||
name = "vpn"
|
||||
gidnumber = 5503
|
||||
includegroups = [ 5501 ]
|
||||
|
||||
#################
|
||||
# Enable and configure the optional REST API here.
|
||||
[api]
|
||||
enabled = true
|
||||
internals = true # debug application performance
|
||||
tls = false # enable TLS for production!!
|
||||
listen = "0.0.0.0:5555"
|
||||
cert = "cert.pem"
|
||||
key = "key.pem"
|
||||
@@ -1 +0,0 @@
|
||||
/test-results
|
||||
@@ -1,207 +0,0 @@
|
||||
dn: cn=hackers,ou=superheros,dc=glauth,dc=com
|
||||
cn: hackers
|
||||
uid: hackers
|
||||
ou: superheros
|
||||
uidNumber: 5001
|
||||
accountStatus: active
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
loginShell: /bin/bash
|
||||
homeDirectory: /home/hackers
|
||||
description: hackers
|
||||
gecos: hackers
|
||||
gidNumber: 5501
|
||||
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
|
||||
shadowExpire: -1
|
||||
shadowFlag: 134538308
|
||||
shadowInactive: -1
|
||||
shadowLastChange: 11000
|
||||
shadowMax: 99999
|
||||
shadowMin: -1
|
||||
shadowWarning: 7
|
||||
|
||||
dn: cn=johndoe,ou=superheros,dc=glauth,dc=com
|
||||
cn: johndoe
|
||||
uid: johndoe
|
||||
givenName: John
|
||||
sn: Doe
|
||||
ou: superheros
|
||||
uidNumber: 5002
|
||||
accountStatus: active
|
||||
mail: jdoe@example.com
|
||||
userPrincipalName: jdoe@example.com
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
loginShell: /bin/sh
|
||||
homeDirectory: /root
|
||||
description: johndoe
|
||||
gecos: johndoe
|
||||
gidNumber: 5501
|
||||
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
|
||||
shadowExpire: -1
|
||||
shadowFlag: 134538308
|
||||
shadowInactive: -1
|
||||
shadowLastChange: 11000
|
||||
shadowMax: 99999
|
||||
shadowMin: -1
|
||||
shadowWarning: 7
|
||||
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwV
|
||||
w3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4h
|
||||
C/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLV
|
||||
IfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XB
|
||||
YnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+
|
||||
txSuhw== rsa-key-20160209
|
||||
|
||||
dn: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
|
||||
cn: jamesdoe
|
||||
uid: jamesdoe
|
||||
givenName: James
|
||||
sn: Doe
|
||||
ou: superheros
|
||||
uidNumber: 5003
|
||||
accountStatus: active
|
||||
mail: jdoe2@example.com
|
||||
userPrincipalName: jdoe2@example.com
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
loginShell: /bin/sh
|
||||
homeDirectory: /home/jamesdoe
|
||||
description: jamesdoe
|
||||
gecos: jamesdoe
|
||||
gidNumber: 5501
|
||||
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
|
||||
shadowExpire: -1
|
||||
shadowFlag: 134538308
|
||||
shadowInactive: -1
|
||||
shadowLastChange: 11000
|
||||
shadowMax: 99999
|
||||
shadowMin: -1
|
||||
shadowWarning: 7
|
||||
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwV
|
||||
w3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4h
|
||||
C/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLV
|
||||
IfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XB
|
||||
YnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+
|
||||
txSuhw== rsa-key-20160209
|
||||
|
||||
dn: cn=alexdoe,ou=superheros,dc=glauth,dc=com
|
||||
cn: alexdoe
|
||||
uid: alexdoe
|
||||
givenName: Alex
|
||||
sn: Doe
|
||||
ou: superheros
|
||||
uidNumber: 5004
|
||||
accountStatus: active
|
||||
mail: adoe@example.com
|
||||
userPrincipalName: adoe@example.com
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
loginShell: /bin/sh
|
||||
homeDirectory: /home/alexdoe
|
||||
description: alexdoe
|
||||
gecos: alexdoe
|
||||
gidNumber: 5501
|
||||
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
|
||||
shadowExpire: -1
|
||||
shadowFlag: 134538308
|
||||
shadowInactive: -1
|
||||
shadowLastChange: 11000
|
||||
shadowMax: 99999
|
||||
shadowMin: -1
|
||||
shadowWarning: 7
|
||||
|
||||
dn: cn=jackdoe,ou=superheros,dc=glauth,dc=com
|
||||
cn: jackdoe
|
||||
uid: jackdoe
|
||||
givenName: Jack
|
||||
sn: Doe
|
||||
ou: superheros
|
||||
uidNumber: 5005
|
||||
accountStatus: active
|
||||
mail: jdoe3@example.com
|
||||
userPrincipalName: jdoe3@example.com
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
loginShell: /bin/sh
|
||||
homeDirectory: /home/jackdoe
|
||||
description: jackdoe
|
||||
gecos: jackdoe
|
||||
gidNumber: 5501
|
||||
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
|
||||
shadowExpire: -1
|
||||
shadowFlag: 134538308
|
||||
shadowInactive: -1
|
||||
shadowLastChange: 11000
|
||||
shadowMax: 99999
|
||||
shadowMin: -1
|
||||
shadowWarning: 7
|
||||
|
||||
dn: cn=sarahdoe,ou=superheros,dc=glauth,dc=com
|
||||
cn: sarahdoe
|
||||
uid: sarahdoe
|
||||
givenName: Sarah
|
||||
sn: Doe
|
||||
ou: superheros
|
||||
uidNumber: 5006
|
||||
accountStatus: active
|
||||
mail: sdoe@example.com
|
||||
userPrincipalName: sdoe@example.com
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
loginShell: /bin/sh
|
||||
homeDirectory: /home/sarahdoe
|
||||
description: sarahdoe
|
||||
gecos: sarahdoe
|
||||
gidNumber: 5501
|
||||
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
|
||||
shadowExpire: -1
|
||||
shadowFlag: 134538308
|
||||
shadowInactive: -1
|
||||
shadowLastChange: 11000
|
||||
shadowMax: 99999
|
||||
shadowMin: -1
|
||||
shadowWarning: 7
|
||||
|
||||
dn: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
|
||||
cn: serviceuser
|
||||
uid: serviceuser
|
||||
ou: svcaccts
|
||||
uidNumber: 5003
|
||||
accountStatus: active
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
loginShell: /bin/bash
|
||||
homeDirectory: /home/serviceuser
|
||||
description: serviceuser
|
||||
gecos: serviceuser
|
||||
gidNumber: 5502
|
||||
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=svcaccts,ou=groups,dc=glauth,dc=com
|
||||
shadowExpire: -1
|
||||
shadowFlag: 134538308
|
||||
shadowInactive: -1
|
||||
shadowLastChange: 11000
|
||||
shadowMax: 99999
|
||||
shadowMin: -1
|
||||
shadowWarning: 7
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
dn: cn=superheros,ou=groups,dc=glauth,dc=com
|
||||
cn: superheros
|
||||
uid: superheros
|
||||
description: superheros
|
||||
gidNumber: 5501
|
||||
objectClass: posixGroup
|
||||
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=hackers,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=jackdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=johndoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=sarahdoe,ou=superheros,dc=glauth,dc=com
|
||||
memberUid: alexdoe
|
||||
memberUid: hackers
|
||||
memberUid: jackdoe
|
||||
memberUid: jamesdoe
|
||||
memberUid: johndoe
|
||||
memberUid: sarahdoe
|
||||
|
||||
dn: cn=svcaccts,ou=groups,dc=glauth,dc=com
|
||||
cn: svcaccts
|
||||
uid: svcaccts
|
||||
description: svcaccts
|
||||
gidNumber: 5502
|
||||
objectClass: posixGroup
|
||||
uniqueMember: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
|
||||
memberUid: serviceuser
|
||||
|
||||
dn: cn=vpnaccess,ou=groups,dc=glauth,dc=com
|
||||
cn: vpnaccess
|
||||
uid: vpnaccess
|
||||
description: vpnaccess
|
||||
gidNumber: 5503
|
||||
objectClass: posixGroup
|
||||
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=hackers,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=jackdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=johndoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=sarahdoe,ou=superheros,dc=glauth,dc=com
|
||||
memberUid: alexdoe
|
||||
memberUid: hackers
|
||||
memberUid: jackdoe
|
||||
memberUid: jamesdoe
|
||||
memberUid: johndoe
|
||||
memberUid: sarahdoe
|
||||
|
||||
dn: cn=allaccs,ou=groups,dc=glauth,dc=com
|
||||
cn: allaccs
|
||||
uid: allaccs
|
||||
description: allaccs
|
||||
gidNumber: 5504
|
||||
objectClass: posixGroup
|
||||
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=hackers,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=jackdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=johndoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=sarahdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
|
||||
memberUid: alexdoe
|
||||
memberUid: hackers
|
||||
memberUid: jackdoe
|
||||
memberUid: jamesdoe
|
||||
memberUid: johndoe
|
||||
memberUid: sarahdoe
|
||||
memberUid: serviceuser
|
||||
|
||||
dn: cn=mailadmin,ou=groups,dc=glauth,dc=com
|
||||
cn: mailadmin
|
||||
uid: mailadmin
|
||||
description: mailadmin
|
||||
gidNumber: 5505
|
||||
objectClass: posixGroup
|
||||
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=jackdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=sarahdoe,ou=superheros,dc=glauth,dc=com
|
||||
memberUid: alexdoe
|
||||
memberUid: jackdoe
|
||||
memberUid: jamesdoe
|
||||
memberUid: sarahdoe
|
||||
|
||||
dn: cn=webmail,ou=groups,dc=glauth,dc=com
|
||||
cn: webmail
|
||||
uid: webmail
|
||||
description: webmail
|
||||
gidNumber: 5506
|
||||
objectClass: posixGroup
|
||||
|
||||
dn: cn=fulltime,ou=groups,dc=glauth,dc=com
|
||||
cn: fulltime
|
||||
uid: fulltime
|
||||
description: fulltime
|
||||
gidNumber: 5507
|
||||
objectClass: posixGroup
|
||||
uniqueMember: cn=alexdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=jackdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
|
||||
uniqueMember: cn=sarahdoe,ou=superheros,dc=glauth,dc=com
|
||||
memberUid: alexdoe
|
||||
memberUid: jackdoe
|
||||
memberUid: jamesdoe
|
||||
memberUid: sarahdoe
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
dn: cn=hackers,ou=superheros,dc=glauth,dc=com
|
||||
cn: hackers
|
||||
uid: hackers
|
||||
ou: superheros
|
||||
uidNumber: 5001
|
||||
accountStatus: active
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
loginShell: /bin/bash
|
||||
homeDirectory: /home/hackers
|
||||
description: hackers
|
||||
gecos: hackers
|
||||
gidNumber: 5501
|
||||
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
|
||||
shadowExpire: -1
|
||||
shadowFlag: 134538308
|
||||
shadowInactive: -1
|
||||
shadowLastChange: 11000
|
||||
shadowMax: 99999
|
||||
shadowMin: -1
|
||||
shadowWarning: 7
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
dn: cn=johndoe,ou=superheros,dc=glauth,dc=com
|
||||
cn: johndoe
|
||||
uid: johndoe
|
||||
givenName: John
|
||||
sn: Doe
|
||||
ou: superheros
|
||||
uidNumber: 5002
|
||||
accountStatus: active
|
||||
mail: jdoe@example.com
|
||||
userPrincipalName: jdoe@example.com
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
loginShell: /bin/sh
|
||||
homeDirectory: /root
|
||||
description: johndoe
|
||||
gecos: johndoe
|
||||
gidNumber: 5501
|
||||
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
|
||||
shadowExpire: -1
|
||||
shadowFlag: 134538308
|
||||
shadowInactive: -1
|
||||
shadowLastChange: 11000
|
||||
shadowMax: 99999
|
||||
shadowMin: -1
|
||||
shadowWarning: 7
|
||||
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwV
|
||||
w3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4h
|
||||
C/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLV
|
||||
IfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XB
|
||||
YnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+
|
||||
txSuhw== rsa-key-20160209
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
dn: cn=serviceuser,ou=svcaccts,dc=glauth,dc=com
|
||||
cn: serviceuser
|
||||
uid: serviceuser
|
||||
ou: svcaccts
|
||||
uidNumber: 5003
|
||||
accountStatus: active
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
loginShell: /bin/bash
|
||||
homeDirectory: /home/serviceuser
|
||||
description: serviceuser
|
||||
gecos: serviceuser
|
||||
gidNumber: 5502
|
||||
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=svcaccts,ou=groups,dc=glauth,dc=com
|
||||
shadowExpire: -1
|
||||
shadowFlag: 134538308
|
||||
shadowInactive: -1
|
||||
shadowLastChange: 11000
|
||||
shadowMax: 99999
|
||||
shadowMin: -1
|
||||
shadowWarning: 7
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
dn: cn=jamesdoe,ou=superheros,dc=glauth,dc=com
|
||||
cn: jamesdoe
|
||||
uid: jamesdoe
|
||||
givenName: James
|
||||
sn: Doe
|
||||
ou: superheros
|
||||
uidNumber: 5003
|
||||
accountStatus: active
|
||||
mail: jdoe2@example.com
|
||||
userPrincipalName: jdoe2@example.com
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
loginShell: /bin/sh
|
||||
homeDirectory: /home/jamesdoe
|
||||
description: jamesdoe
|
||||
gecos: jamesdoe
|
||||
gidNumber: 5501
|
||||
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
|
||||
shadowExpire: -1
|
||||
shadowFlag: 134538308
|
||||
shadowInactive: -1
|
||||
shadowLastChange: 11000
|
||||
shadowMax: 99999
|
||||
shadowMin: -1
|
||||
shadowWarning: 7
|
||||
sshPublicKey: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwV
|
||||
w3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4h
|
||||
C/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLV
|
||||
IfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XB
|
||||
YnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+
|
||||
txSuhw== rsa-key-20160209
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
dn: cn=alexdoe,ou=superheros,dc=glauth,dc=com
|
||||
cn: alexdoe
|
||||
uid: alexdoe
|
||||
givenName: Alex
|
||||
sn: Doe
|
||||
ou: superheros
|
||||
uidNumber: 5004
|
||||
accountStatus: active
|
||||
mail: adoe@example.com
|
||||
userPrincipalName: adoe@example.com
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
loginShell: /bin/sh
|
||||
homeDirectory: /home/alexdoe
|
||||
description: alexdoe
|
||||
gecos: alexdoe
|
||||
gidNumber: 5501
|
||||
memberOf: cn=allaccs,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=fulltime,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=mailadmin,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=superheros,ou=groups,dc=glauth,dc=com
|
||||
memberOf: cn=vpnaccess,ou=groups,dc=glauth,dc=com
|
||||
shadowExpire: -1
|
||||
shadowFlag: 134538308
|
||||
shadowInactive: -1
|
||||
shadowLastChange: 11000
|
||||
shadowMax: 99999
|
||||
shadowMin: -1
|
||||
shadowWarning: 7
|
||||
|
||||
@@ -1,257 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
export CLEANUP="$1"
|
||||
|
||||
## Main Methods
|
||||
|
||||
|
||||
# Dep check
|
||||
|
||||
command -v oathtool ;
|
||||
if [[ "$?" = "1" ]] ; then
|
||||
echo "Please install oathtool or add it to \$PATH before continuing."
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
# Get the git working directory base if CI build dir isn't set
|
||||
if [[ "$CI_BUILD_DIR" == "" ]] ; then
|
||||
export CI_BUILD_DIR="$(git rev-parse --show-toplevel)/v2"
|
||||
fi
|
||||
|
||||
# This script requires that "$CI_BUILD_DIR" is set to the repo root
|
||||
|
||||
# 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;
|
||||
fi
|
||||
|
||||
# Display version string
|
||||
echo "";
|
||||
echo ""
|
||||
echo "Version string of tested binary:"
|
||||
"$CI_BUILD_DIR/bin/glauth64" --version
|
||||
echo ""
|
||||
|
||||
# Start in background, capture PID
|
||||
"$CI_BUILD_DIR/bin/glauth64" -c "$CI_BUILD_DIR/scripts/ci/test-config.cfg" &> /dev/null &
|
||||
|
||||
# Use this instead to see glauth logs while running
|
||||
# "$CI_BUILD_DIR/bin/glauth64" -c "$CI_BUILD_DIR/scripts/ci/test-config.cfg" &
|
||||
glauthPid="$!"
|
||||
|
||||
echo "Running glauth at PID=$glauthPid"
|
||||
|
||||
# Sleep 2 sec, to ensure it comes online successfully and stays up
|
||||
sleep 2;
|
||||
|
||||
# Check if process is still running before continuing
|
||||
ps aux | grep -v "grep" | grep "$glauthPid" &> /dev/null || FAIL="1"
|
||||
|
||||
if [[ "$FAIL" = "1" ]] ; then
|
||||
echo "Integration test FAILED - process did not remain running > 2 sec"
|
||||
exit 255;
|
||||
fi
|
||||
|
||||
|
||||
FAIL="0"
|
||||
|
||||
# Used for OTP testing
|
||||
# Arguments:
|
||||
# $1 - Full Bind DN
|
||||
# $2 - Full Bind PW
|
||||
# $3 - query to run
|
||||
# $4 - test name
|
||||
function bindTest() {
|
||||
ldapsearch -LLL \
|
||||
-H ldap://localhost:3893 \
|
||||
-D "$1" \
|
||||
-w "$2" \
|
||||
-x -bdc=glauth,dc=com \
|
||||
"$3" > /dev/null
|
||||
|
||||
exitCode="$?"
|
||||
|
||||
if [[ "$exitCode" = "0" ]] ; then
|
||||
echo " - PASS : Bind test '$4'";
|
||||
else
|
||||
echo " - FAIL : Bind test '$4'";
|
||||
FAIL="1"
|
||||
fi
|
||||
|
||||
|
||||
}
|
||||
|
||||
# Arguments:
|
||||
# $1 - query
|
||||
# $2 - name of snapshot
|
||||
function snapshotTest() {
|
||||
|
||||
goodResults="$CI_BUILD_DIR/scripts/ci/good-results"
|
||||
testResults="$CI_BUILD_DIR/scripts/ci/test-results"
|
||||
|
||||
mkdir "$testResults" &> /dev/null
|
||||
|
||||
# Run tests here
|
||||
ldapCmd="ldapsearch -LLL \
|
||||
-H ldap://localhost:3893 \
|
||||
-D cn=serviceuser,ou=svcaccts,dc=glauth,dc=com \
|
||||
-w mysecret \
|
||||
-x -bdc=glauth,dc=com \
|
||||
$1";
|
||||
|
||||
# Run the ldap command, pipe to file
|
||||
# "$ldapCmd" &> "$testResults/$2"
|
||||
|
||||
# Useful for debugging - output the command run
|
||||
# echo "$ldapCmd";
|
||||
|
||||
ldapsearch -LLL \
|
||||
-H ldap://localhost:3893 \
|
||||
-D cn=serviceuser,ou=svcaccts,dc=glauth,dc=com \
|
||||
-w mysecret \
|
||||
-x -bdc=glauth,dc=com \
|
||||
$1 > "$testResults/$2"
|
||||
|
||||
|
||||
# Handle the first-run case, saving the test results
|
||||
if [ ! -f "$goodResults/$2" ]; then
|
||||
echo " - FIRST RUN - SAVING SNAPSHOT, RUN AGAIN TO PASS : '$2'";
|
||||
|
||||
# Copy the results to the goodResults dir, for future runs.
|
||||
cp "$testResults/$2" "$goodResults/$2";
|
||||
|
||||
# NOTE: fail=1 must still be set, otherwise CI runs would succeed when they shouldn't
|
||||
FAIL="1"
|
||||
else
|
||||
|
||||
THISFAIL="0"
|
||||
diff -u "$goodResults/$2" "$testResults/$2" || THISFAIL="1"
|
||||
|
||||
|
||||
if [[ "$CLEANUP" = "cleanup" ]] ; then
|
||||
rm -rf "$testResults"
|
||||
fi
|
||||
|
||||
if [[ "$THISFAIL" = "0" ]] ; then
|
||||
echo " - PASS : snapshot '$2'";
|
||||
else
|
||||
echo " - FAIL : snapshot '$2'";
|
||||
FAIL="1"
|
||||
fi
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
|
||||
echo "";
|
||||
echo "";
|
||||
echo "RUNNING TESTS:"
|
||||
echo "";
|
||||
|
||||
#################
|
||||
## TEST RUNS
|
||||
#################
|
||||
|
||||
## Query output tests
|
||||
|
||||
# Regular single-user fetches
|
||||
snapshotTest "cn=hackers" userFetchTest0
|
||||
snapshotTest "cn=johndoe" userFetchTest1
|
||||
snapshotTest "cn=serviceuser" userFetchTest2
|
||||
snapshotTest "cn=jamesdoe" userFetchTest3
|
||||
snapshotTest "cn=alexdoe" userFetchTest4
|
||||
|
||||
# Test result of fetching nonexistent users
|
||||
snapshotTest "cn=fakeuser" userFetchNonexistentUser0
|
||||
snapshotTest "cn=janedoe" userFetchNonexistentUser1
|
||||
|
||||
# List all posixgroups
|
||||
snapshotTest "objectClass=posixgroup" posixGroupList0
|
||||
snapshotTest "objectClass=posixaccount" posixAccountList0
|
||||
|
||||
|
||||
## 2FA Bind Test
|
||||
# Fetch the OTP for this moment
|
||||
otpCode="$(oathtool --totp -b -d 6 '3hnvnk4ycv44glzigd6s25j4dougs3rk')"
|
||||
|
||||
pass="ThisAloneWontWork!"
|
||||
|
||||
# Try to bind with it
|
||||
bindTest "cn=alexdoe,ou=superheros,dc=glauth,dc=com" \
|
||||
"$pass$otpCode" \
|
||||
"cn=alexdoe" \
|
||||
"OtpAlexDoe"
|
||||
|
||||
## App Password Bind Test
|
||||
|
||||
# Test the main pw
|
||||
bindTest "cn=jackdoe,ou=superheros,dc=glauth,dc=com" \
|
||||
"dogood1" \
|
||||
"cn=jackdoe" \
|
||||
"AppPwNoOtp0"
|
||||
|
||||
# App passwords on user
|
||||
bindTest "cn=jackdoe,ou=superheros,dc=glauth,dc=com" \
|
||||
"TestAppPw1" \
|
||||
"cn=jackdoe" \
|
||||
"AppPwNoOtp1"
|
||||
|
||||
bindTest "cn=jackdoe,ou=superheros,dc=glauth,dc=com" \
|
||||
"TestAppPw2" \
|
||||
"cn=jackdoe" \
|
||||
"AppPwNoOtp2"
|
||||
|
||||
bindTest "cn=jackdoe,ou=superheros,dc=glauth,dc=com" \
|
||||
"TestAppPw3" \
|
||||
"cn=jackdoe" \
|
||||
"AppPwNoOtp3"
|
||||
|
||||
|
||||
####
|
||||
# Test for a user who also uses OTP
|
||||
####
|
||||
otpCode="$(oathtool --totp -b -d 6 '3hnvnk4ycv44glzigd6s25j4dougs3rk')"
|
||||
|
||||
pass="dogood1"
|
||||
|
||||
# Test the main pw
|
||||
bindTest "cn=sarahdoe,ou=superheros,dc=glauth,dc=com" \
|
||||
"$pass$otpCode" \
|
||||
"cn=sarahdoe" \
|
||||
"AppPwOtp0"
|
||||
|
||||
# App passwords on user
|
||||
bindTest "cn=sarahdoe,ou=superheros,dc=glauth,dc=com" \
|
||||
"TestAppPw1" \
|
||||
"cn=sarahdoe" \
|
||||
"AppPwOtp1"
|
||||
|
||||
bindTest "cn=sarahdoe,ou=superheros,dc=glauth,dc=com" \
|
||||
"TestAppPw2" \
|
||||
"cn=sarahdoe" \
|
||||
"AppPwOtp2"
|
||||
|
||||
bindTest "cn=sarahdoe,ou=superheros,dc=glauth,dc=com" \
|
||||
"TestAppPw3" \
|
||||
"cn=sarahdoe" \
|
||||
"AppPwOtp3"
|
||||
|
||||
#############
|
||||
## Cleanup
|
||||
#############
|
||||
|
||||
echo "";
|
||||
echo "";
|
||||
|
||||
# Kill saved PID when done
|
||||
# However - throw the fail flag if the process isn't there (ie, exited prematurely)
|
||||
echo "Killing glauth"
|
||||
kill "$glauthPid" || FAIL="1"
|
||||
|
||||
if [[ "$FAIL" = "0" ]] ; then
|
||||
echo "Integration test success"
|
||||
exit 0;
|
||||
else
|
||||
echo "Integration test FAILED"
|
||||
exit 255;
|
||||
fi
|
||||
@@ -1,178 +0,0 @@
|
||||
#################
|
||||
# glauth.conf
|
||||
|
||||
#################
|
||||
# General configuration.
|
||||
debug = true
|
||||
#syslog = true
|
||||
|
||||
#################
|
||||
# The frontend section controls how clients connect to the proxy.
|
||||
[frontend]
|
||||
tls = false # enable TLS for production!!
|
||||
|
||||
# Using a nonstandard port to ensure there are no
|
||||
# conflicts if running a test alongside a prod instance
|
||||
listen = "localhost:3893"
|
||||
cert = "cert.pem"
|
||||
key = "key.pem"
|
||||
|
||||
#################
|
||||
# The backend section controls the data store.
|
||||
[backend]
|
||||
datastore = "config"
|
||||
baseDN = "dc=glauth,dc=com"
|
||||
|
||||
#################
|
||||
# The users section contains a hardcoded list of valid users.
|
||||
# to create a passSHA256: echo -n "mysecret" | openssl dgst -sha256
|
||||
[[users]]
|
||||
name = "hackers"
|
||||
uidnumber = 5001
|
||||
primarygroup = 5501
|
||||
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "ou=superheros,dc=glauth,dc=com"
|
||||
|
||||
# This user record shows all of the possible fields available
|
||||
[[users]]
|
||||
name = "johndoe"
|
||||
givenname="John"
|
||||
sn="Doe"
|
||||
mail = "jdoe@example.com"
|
||||
uidnumber = 5002
|
||||
primarygroup = 5501
|
||||
loginShell = "/bin/sh"
|
||||
homeDir = "/root"
|
||||
passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood
|
||||
sshkeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwVw3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4hC/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLVIfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XBYnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+txSuhw== rsa-key-20160209"]
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "ou=superheros,dc=glauth,dc=com"
|
||||
|
||||
# This user record tests lots of groups, including group inheritence
|
||||
[[users]]
|
||||
name = "jamesdoe"
|
||||
givenname="James"
|
||||
sn="Doe"
|
||||
mail = "jdoe2@example.com"
|
||||
uidnumber = 5003
|
||||
primarygroup = 5501
|
||||
loginShell = "/bin/sh"
|
||||
otherGroups = [5505,5507]
|
||||
homeDir = "/home/jamesdoe"
|
||||
passsha256 = "ff522a502031e5f37054892d642c2c0f36af6c4e8f62225470d4a1a99f3c9c2a" # dogood1
|
||||
sshkeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwVw3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4hC/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLVIfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XBYnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+txSuhw== rsa-key-20160209"]
|
||||
|
||||
# 2FA test user
|
||||
[[users]]
|
||||
name = "alexdoe"
|
||||
givenname="Alex"
|
||||
sn="Doe"
|
||||
mail = "adoe@example.com"
|
||||
uidnumber = 5004
|
||||
primarygroup = 5501
|
||||
loginShell = "/bin/sh"
|
||||
otherGroups = [5505,5507]
|
||||
otpsecret = "3hnvnk4ycv44glzigd6s25j4dougs3rk"
|
||||
passsha256 = "b1cae08a1fd8b0bd0855e48f74ba2f8548904dd5de72311e6234617a3f661e16" # ThisAloneWontWork!
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "dc=glauth,dc=com"
|
||||
|
||||
|
||||
|
||||
# App PW test user without OTP
|
||||
[[users]]
|
||||
name = "jackdoe"
|
||||
givenname="Jack"
|
||||
sn="Doe"
|
||||
mail = "jdoe3@example.com"
|
||||
uidnumber = 5005
|
||||
primarygroup = 5501
|
||||
loginShell = "/bin/sh"
|
||||
otherGroups = [5505,5507]
|
||||
passsha256 = "ff522a502031e5f37054892d642c2c0f36af6c4e8f62225470d4a1a99f3c9c2a" #dogood1
|
||||
passappsha256 = [
|
||||
"c32255dbf6fd6b64883ec8801f793bccfa2a860f2b1ae1315cd95cdac1338efa", # TestAppPw1
|
||||
"c9853d5f2599e90497e9f8cc671bd2022b0fb5d1bd7cfff92f079e8f8f02b8d3", # TestAppPw2
|
||||
"4939efa7c87095dacb5e7e8b8cfb3a660fa1f5edcc9108f6d7ec20ea4d6b3a88", # TestAppPw3
|
||||
]
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "dc=glauth,dc=com"
|
||||
|
||||
# App PW test user with OTP
|
||||
[[users]]
|
||||
name = "sarahdoe"
|
||||
givenname="Sarah"
|
||||
sn="Doe"
|
||||
mail = "sdoe@example.com"
|
||||
uidnumber = 5006
|
||||
primarygroup = 5501
|
||||
loginShell = "/bin/sh"
|
||||
otherGroups = [5505,5507]
|
||||
otpsecret = "3hnvnk4ycv44glzigd6s25j4dougs3rk"
|
||||
passsha256 = "ff522a502031e5f37054892d642c2c0f36af6c4e8f62225470d4a1a99f3c9c2a" #dogood1
|
||||
passappsha256 = [
|
||||
"c32255dbf6fd6b64883ec8801f793bccfa2a860f2b1ae1315cd95cdac1338efa", # TestAppPw1
|
||||
"c9853d5f2599e90497e9f8cc671bd2022b0fb5d1bd7cfff92f079e8f8f02b8d3", # TestAppPw2
|
||||
"4939efa7c87095dacb5e7e8b8cfb3a660fa1f5edcc9108f6d7ec20ea4d6b3a88", # TestAppPw3
|
||||
]
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "dc=glauth,dc=com"
|
||||
|
||||
|
||||
[[users]]
|
||||
name = "serviceuser"
|
||||
uidnumber = 5003
|
||||
primarygroup = 5502
|
||||
passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret
|
||||
[[users.capabilities]]
|
||||
action = "search"
|
||||
object = "*"
|
||||
|
||||
#################
|
||||
# The groups section contains a hardcoded list of valid users.
|
||||
[[groups]]
|
||||
name = "superheros"
|
||||
gidnumber = 5501
|
||||
|
||||
[[groups]]
|
||||
name = "svcaccts"
|
||||
gidnumber = 5502
|
||||
|
||||
|
||||
[[groups]]
|
||||
name = "vpnaccess"
|
||||
gidnumber = 5503
|
||||
includegroups = [ 5501 ]
|
||||
|
||||
[[groups]]
|
||||
name = "allaccs"
|
||||
gidnumber = 5504
|
||||
includegroups = [ 5501, 5502 ]
|
||||
|
||||
[[groups]]
|
||||
name = "mailadmin"
|
||||
gidnumber = 5505
|
||||
|
||||
[[groups]]
|
||||
name = "webmail"
|
||||
gidnumber = 5506
|
||||
|
||||
|
||||
[[groups]]
|
||||
name = "fulltime"
|
||||
gidnumber = 5507
|
||||
|
||||
#################
|
||||
# Enable and configure the optional REST API here.
|
||||
[api]
|
||||
enabled = false
|
||||
tls = false # enable TLS for production!!
|
||||
listen = "localhost:5555"
|
||||
cert = "cert.pem"
|
||||
key = "key.pem"
|
||||
@@ -1,767 +0,0 @@
|
||||
{
|
||||
"defaultAction": "SCMP_ACT_TRACE",
|
||||
"defaultErrnoRet": 1,
|
||||
"archMap": [
|
||||
{
|
||||
"architecture": "SCMP_ARCH_X86_64",
|
||||
"subArchitectures": [
|
||||
"SCMP_ARCH_X86",
|
||||
"SCMP_ARCH_X32"
|
||||
]
|
||||
},
|
||||
{
|
||||
"architecture": "SCMP_ARCH_AARCH64",
|
||||
"subArchitectures": [
|
||||
"SCMP_ARCH_ARM"
|
||||
]
|
||||
},
|
||||
{
|
||||
"architecture": "SCMP_ARCH_MIPS64",
|
||||
"subArchitectures": [
|
||||
"SCMP_ARCH_MIPS",
|
||||
"SCMP_ARCH_MIPS64N32"
|
||||
]
|
||||
},
|
||||
{
|
||||
"architecture": "SCMP_ARCH_MIPS64N32",
|
||||
"subArchitectures": [
|
||||
"SCMP_ARCH_MIPS",
|
||||
"SCMP_ARCH_MIPS64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"architecture": "SCMP_ARCH_MIPSEL64",
|
||||
"subArchitectures": [
|
||||
"SCMP_ARCH_MIPSEL",
|
||||
"SCMP_ARCH_MIPSEL64N32"
|
||||
]
|
||||
},
|
||||
{
|
||||
"architecture": "SCMP_ARCH_MIPSEL64N32",
|
||||
"subArchitectures": [
|
||||
"SCMP_ARCH_MIPSEL",
|
||||
"SCMP_ARCH_MIPSEL64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"architecture": "SCMP_ARCH_S390X",
|
||||
"subArchitectures": [
|
||||
"SCMP_ARCH_S390"
|
||||
]
|
||||
}
|
||||
],
|
||||
"syscalls": [
|
||||
{
|
||||
"names": [
|
||||
"accept",
|
||||
"accept4",
|
||||
"access",
|
||||
"adjtimex",
|
||||
"alarm",
|
||||
"bind",
|
||||
"brk",
|
||||
"capget",
|
||||
"capset",
|
||||
"chdir",
|
||||
"chmod",
|
||||
"chown",
|
||||
"chown32",
|
||||
"clock_adjtime",
|
||||
"clock_adjtime64",
|
||||
"clock_getres",
|
||||
"clock_getres_time64",
|
||||
"clock_gettime",
|
||||
"clock_gettime64",
|
||||
"clock_nanosleep",
|
||||
"clock_nanosleep_time64",
|
||||
"close",
|
||||
"close_range",
|
||||
"connect",
|
||||
"copy_file_range",
|
||||
"creat",
|
||||
"dup",
|
||||
"dup2",
|
||||
"dup3",
|
||||
"epoll_create",
|
||||
"epoll_create1",
|
||||
"epoll_ctl",
|
||||
"epoll_ctl_old",
|
||||
"epoll_pwait",
|
||||
"epoll_pwait2",
|
||||
"epoll_wait",
|
||||
"epoll_wait_old",
|
||||
"eventfd",
|
||||
"eventfd2",
|
||||
"execve",
|
||||
"execveat",
|
||||
"exit",
|
||||
"exit_group",
|
||||
"faccessat",
|
||||
"faccessat2",
|
||||
"fadvise64",
|
||||
"fadvise64_64",
|
||||
"fallocate",
|
||||
"fanotify_mark",
|
||||
"fchdir",
|
||||
"fchmod",
|
||||
"fchmodat",
|
||||
"fchown",
|
||||
"fchown32",
|
||||
"fchownat",
|
||||
"fcntl",
|
||||
"fcntl64",
|
||||
"fdatasync",
|
||||
"fgetxattr",
|
||||
"flistxattr",
|
||||
"flock",
|
||||
"fork",
|
||||
"fremovexattr",
|
||||
"fsetxattr",
|
||||
"fstat",
|
||||
"fstat64",
|
||||
"fstatat64",
|
||||
"fstatfs",
|
||||
"fstatfs64",
|
||||
"fsync",
|
||||
"ftruncate",
|
||||
"ftruncate64",
|
||||
"futex",
|
||||
"futex_time64",
|
||||
"futimesat",
|
||||
"getcpu",
|
||||
"getcwd",
|
||||
"getdents",
|
||||
"getdents64",
|
||||
"getegid",
|
||||
"getegid32",
|
||||
"geteuid",
|
||||
"geteuid32",
|
||||
"getgid",
|
||||
"getgid32",
|
||||
"getgroups",
|
||||
"getgroups32",
|
||||
"getitimer",
|
||||
"getpeername",
|
||||
"getpgid",
|
||||
"getpgrp",
|
||||
"getpid",
|
||||
"getppid",
|
||||
"getpriority",
|
||||
"getrandom",
|
||||
"getresgid",
|
||||
"getresgid32",
|
||||
"getresuid",
|
||||
"getresuid32",
|
||||
"getrlimit",
|
||||
"get_robust_list",
|
||||
"getrusage",
|
||||
"getsid",
|
||||
"getsockname",
|
||||
"getsockopt",
|
||||
"get_thread_area",
|
||||
"gettid",
|
||||
"gettimeofday",
|
||||
"getuid",
|
||||
"getuid32",
|
||||
"getxattr",
|
||||
"inotify_add_watch",
|
||||
"inotify_init",
|
||||
"inotify_init1",
|
||||
"inotify_rm_watch",
|
||||
"io_cancel",
|
||||
"ioctl",
|
||||
"io_destroy",
|
||||
"io_getevents",
|
||||
"io_pgetevents",
|
||||
"io_pgetevents_time64",
|
||||
"ioprio_get",
|
||||
"ioprio_set",
|
||||
"io_setup",
|
||||
"io_submit",
|
||||
"io_uring_enter",
|
||||
"io_uring_register",
|
||||
"io_uring_setup",
|
||||
"ipc",
|
||||
"kill",
|
||||
"lchown",
|
||||
"lchown32",
|
||||
"lgetxattr",
|
||||
"link",
|
||||
"linkat",
|
||||
"listen",
|
||||
"listxattr",
|
||||
"llistxattr",
|
||||
"_llseek",
|
||||
"lremovexattr",
|
||||
"lseek",
|
||||
"lsetxattr",
|
||||
"lstat",
|
||||
"lstat64",
|
||||
"madvise",
|
||||
"membarrier",
|
||||
"memfd_create",
|
||||
"mincore",
|
||||
"mkdir",
|
||||
"mkdirat",
|
||||
"mknod",
|
||||
"mknodat",
|
||||
"mlock",
|
||||
"mlock2",
|
||||
"mlockall",
|
||||
"mmap",
|
||||
"mmap2",
|
||||
"mprotect",
|
||||
"mq_getsetattr",
|
||||
"mq_notify",
|
||||
"mq_open",
|
||||
"mq_timedreceive",
|
||||
"mq_timedreceive_time64",
|
||||
"mq_timedsend",
|
||||
"mq_timedsend_time64",
|
||||
"mq_unlink",
|
||||
"mremap",
|
||||
"msgctl",
|
||||
"msgget",
|
||||
"msgrcv",
|
||||
"msgsnd",
|
||||
"msync",
|
||||
"munlock",
|
||||
"munlockall",
|
||||
"munmap",
|
||||
"nanosleep",
|
||||
"newfstatat",
|
||||
"_newselect",
|
||||
"open",
|
||||
"openat",
|
||||
"openat2",
|
||||
"pause",
|
||||
"pidfd_open",
|
||||
"pidfd_send_signal",
|
||||
"pipe",
|
||||
"pipe2",
|
||||
"poll",
|
||||
"ppoll",
|
||||
"ppoll_time64",
|
||||
"prctl",
|
||||
"pread64",
|
||||
"preadv",
|
||||
"preadv2",
|
||||
"prlimit64",
|
||||
"pselect6",
|
||||
"pselect6_time64",
|
||||
"pwrite64",
|
||||
"pwritev",
|
||||
"pwritev2",
|
||||
"read",
|
||||
"readahead",
|
||||
"readlink",
|
||||
"readlinkat",
|
||||
"readv",
|
||||
"recv",
|
||||
"recvfrom",
|
||||
"recvmmsg",
|
||||
"recvmmsg_time64",
|
||||
"recvmsg",
|
||||
"remap_file_pages",
|
||||
"removexattr",
|
||||
"rename",
|
||||
"renameat",
|
||||
"renameat2",
|
||||
"restart_syscall",
|
||||
"rmdir",
|
||||
"rseq",
|
||||
"rt_sigaction",
|
||||
"rt_sigpending",
|
||||
"rt_sigprocmask",
|
||||
"rt_sigqueueinfo",
|
||||
"rt_sigreturn",
|
||||
"rt_sigsuspend",
|
||||
"rt_sigtimedwait",
|
||||
"rt_sigtimedwait_time64",
|
||||
"rt_tgsigqueueinfo",
|
||||
"sched_getaffinity",
|
||||
"sched_getattr",
|
||||
"sched_getparam",
|
||||
"sched_get_priority_max",
|
||||
"sched_get_priority_min",
|
||||
"sched_getscheduler",
|
||||
"sched_rr_get_interval",
|
||||
"sched_rr_get_interval_time64",
|
||||
"sched_setaffinity",
|
||||
"sched_setattr",
|
||||
"sched_setparam",
|
||||
"sched_setscheduler",
|
||||
"sched_yield",
|
||||
"seccomp",
|
||||
"select",
|
||||
"semctl",
|
||||
"semget",
|
||||
"semop",
|
||||
"semtimedop",
|
||||
"semtimedop_time64",
|
||||
"send",
|
||||
"sendfile",
|
||||
"sendfile64",
|
||||
"sendmmsg",
|
||||
"sendmsg",
|
||||
"sendto",
|
||||
"setfsgid",
|
||||
"setfsgid32",
|
||||
"setfsuid",
|
||||
"setfsuid32",
|
||||
"setgid",
|
||||
"setgid32",
|
||||
"setgroups",
|
||||
"setgroups32",
|
||||
"setitimer",
|
||||
"setpgid",
|
||||
"setpriority",
|
||||
"setregid",
|
||||
"setregid32",
|
||||
"setresgid",
|
||||
"setresgid32",
|
||||
"setresuid",
|
||||
"setresuid32",
|
||||
"setreuid",
|
||||
"setreuid32",
|
||||
"setrlimit",
|
||||
"set_robust_list",
|
||||
"setsid",
|
||||
"setsockopt",
|
||||
"set_thread_area",
|
||||
"set_tid_address",
|
||||
"setuid",
|
||||
"setuid32",
|
||||
"setxattr",
|
||||
"shmat",
|
||||
"shmctl",
|
||||
"shmdt",
|
||||
"shmget",
|
||||
"shutdown",
|
||||
"sigaltstack",
|
||||
"signalfd",
|
||||
"signalfd4",
|
||||
"sigprocmask",
|
||||
"sigreturn",
|
||||
"socket",
|
||||
"socketcall",
|
||||
"socketpair",
|
||||
"splice",
|
||||
"stat",
|
||||
"stat64",
|
||||
"statfs",
|
||||
"statfs64",
|
||||
"statx",
|
||||
"symlink",
|
||||
"symlinkat",
|
||||
"sync",
|
||||
"sync_file_range",
|
||||
"syncfs",
|
||||
"sysinfo",
|
||||
"tee",
|
||||
"tgkill",
|
||||
"time",
|
||||
"timer_create",
|
||||
"timer_delete",
|
||||
"timer_getoverrun",
|
||||
"timer_gettime",
|
||||
"timer_gettime64",
|
||||
"timer_settime",
|
||||
"timer_settime64",
|
||||
"timerfd_create",
|
||||
"timerfd_gettime",
|
||||
"timerfd_gettime64",
|
||||
"timerfd_settime",
|
||||
"timerfd_settime64",
|
||||
"times",
|
||||
"tkill",
|
||||
"truncate",
|
||||
"truncate64",
|
||||
"ugetrlimit",
|
||||
"umask",
|
||||
"uname",
|
||||
"unlink",
|
||||
"unlinkat",
|
||||
"utime",
|
||||
"utimensat",
|
||||
"utimensat_time64",
|
||||
"utimes",
|
||||
"vfork",
|
||||
"vmsplice",
|
||||
"wait4",
|
||||
"waitid",
|
||||
"waitpid",
|
||||
"write",
|
||||
"writev"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW"
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"process_vm_readv",
|
||||
"process_vm_writev",
|
||||
"ptrace"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"minKernel": "4.8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"personality"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"args": [
|
||||
{
|
||||
"index": 0,
|
||||
"value": 0,
|
||||
"op": "SCMP_CMP_EQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"personality"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"args": [
|
||||
{
|
||||
"index": 0,
|
||||
"value": 8,
|
||||
"op": "SCMP_CMP_EQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"personality"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"args": [
|
||||
{
|
||||
"index": 0,
|
||||
"value": 131072,
|
||||
"op": "SCMP_CMP_EQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"personality"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"args": [
|
||||
{
|
||||
"index": 0,
|
||||
"value": 131080,
|
||||
"op": "SCMP_CMP_EQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"personality"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"args": [
|
||||
{
|
||||
"index": 0,
|
||||
"value": 4294967295,
|
||||
"op": "SCMP_CMP_EQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"sync_file_range2"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"arches": [
|
||||
"ppc64le"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"arm_fadvise64_64",
|
||||
"arm_sync_file_range",
|
||||
"sync_file_range2",
|
||||
"breakpoint",
|
||||
"cacheflush",
|
||||
"set_tls"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"arches": [
|
||||
"arm",
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"arch_prctl"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"arches": [
|
||||
"amd64",
|
||||
"x32"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"modify_ldt"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"arches": [
|
||||
"amd64",
|
||||
"x32",
|
||||
"x86"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"s390_pci_mmio_read",
|
||||
"s390_pci_mmio_write",
|
||||
"s390_runtime_instr"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"arches": [
|
||||
"s390",
|
||||
"s390x"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"open_by_handle_at"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"caps": [
|
||||
"CAP_DAC_READ_SEARCH"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"bpf",
|
||||
"clone",
|
||||
"clone3",
|
||||
"fanotify_init",
|
||||
"fsconfig",
|
||||
"fsmount",
|
||||
"fsopen",
|
||||
"fspick",
|
||||
"lookup_dcookie",
|
||||
"mount",
|
||||
"move_mount",
|
||||
"name_to_handle_at",
|
||||
"open_tree",
|
||||
"perf_event_open",
|
||||
"quotactl",
|
||||
"setdomainname",
|
||||
"sethostname",
|
||||
"setns",
|
||||
"syslog",
|
||||
"umount",
|
||||
"umount2",
|
||||
"unshare"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"caps": [
|
||||
"CAP_SYS_ADMIN"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"clone"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"args": [
|
||||
{
|
||||
"index": 0,
|
||||
"value": 2114060288,
|
||||
"op": "SCMP_CMP_MASKED_EQ"
|
||||
}
|
||||
],
|
||||
"excludes": {
|
||||
"caps": [
|
||||
"CAP_SYS_ADMIN"
|
||||
],
|
||||
"arches": [
|
||||
"s390",
|
||||
"s390x"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"clone"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"args": [
|
||||
{
|
||||
"index": 1,
|
||||
"value": 2114060288,
|
||||
"op": "SCMP_CMP_MASKED_EQ"
|
||||
}
|
||||
],
|
||||
"comment": "s390 parameter ordering for clone is different",
|
||||
"includes": {
|
||||
"arches": [
|
||||
"s390",
|
||||
"s390x"
|
||||
]
|
||||
},
|
||||
"excludes": {
|
||||
"caps": [
|
||||
"CAP_SYS_ADMIN"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"clone3"
|
||||
],
|
||||
"action": "SCMP_ACT_ERRNO",
|
||||
"errnoRet": 38,
|
||||
"excludes": {
|
||||
"caps": [
|
||||
"CAP_SYS_ADMIN"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"reboot"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"caps": [
|
||||
"CAP_SYS_BOOT"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"chroot"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"caps": [
|
||||
"CAP_SYS_CHROOT"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"delete_module",
|
||||
"init_module",
|
||||
"finit_module"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"caps": [
|
||||
"CAP_SYS_MODULE"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"acct"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"caps": [
|
||||
"CAP_SYS_PACCT"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"kcmp",
|
||||
"pidfd_getfd",
|
||||
"process_madvise",
|
||||
"process_vm_readv",
|
||||
"process_vm_writev",
|
||||
"ptrace"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"caps": [
|
||||
"CAP_SYS_PTRACE"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"iopl",
|
||||
"ioperm"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"caps": [
|
||||
"CAP_SYS_RAWIO"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"settimeofday",
|
||||
"stime",
|
||||
"clock_settime"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"caps": [
|
||||
"CAP_SYS_TIME"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"vhangup"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"caps": [
|
||||
"CAP_SYS_TTY_CONFIG"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"get_mempolicy",
|
||||
"mbind",
|
||||
"set_mempolicy"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"caps": [
|
||||
"CAP_SYS_NICE"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"syslog"
|
||||
],
|
||||
"action": "SCMP_ACT_ALLOW",
|
||||
"includes": {
|
||||
"caps": [
|
||||
"CAP_SYSLOG"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
inform() {
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "$@"
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
}
|
||||
|
||||
image_name() {
|
||||
[[ "$1" == "plugins" ]] && { echo "glauth-plugins"; } || { echo "glauth"; }
|
||||
}
|
||||
|
||||
ldap_port() {
|
||||
[[ "$1" == "plugins" ]] && { echo "3893"; } || { echo "3893"; }
|
||||
}
|
||||
|
||||
prepare_image() {
|
||||
profile="$1"
|
||||
img="localhost/$(image_name $profile)"
|
||||
|
||||
inform "Building profile: $profile container: $img"
|
||||
podman build --security-opt seccomp=scripts/docker/fastat-workaround.json -f Dockerfile-$profile -t $img
|
||||
|
||||
inform "Starting registry"
|
||||
podman run -d --name registry -p 5000:5000 -v ./local/registry:/var/lib/registry --restart=unless-stopped registry:2
|
||||
|
||||
inform "Pushing image to container"
|
||||
podman tag $img localhost:5000/$(image_name $profile)
|
||||
podman push --tls-verify=false localhost:5000/$(image_name $profile)
|
||||
|
||||
if command -v ldapsearch &> /dev/null; then
|
||||
inform "Running image"
|
||||
podman run -d --name checkglauth -p $(ldap_port $profile):$(ldap_port $profile) localhost:5000/$(image_name $profile)
|
||||
sleep 3
|
||||
inform "Testing image"
|
||||
checkfailed=0
|
||||
if (ldapsearch -LLL -H ldap://localhost:$(ldap_port $profile) -D cn=serviceuser,ou=svcaccts,dc=glauth,dc=com -w mysecret -x -bdc=glauth,dc=com cn=hackers | grep posixAccoun) &> /dev/null; then
|
||||
echo "Checked glauth is responding properly to ldapsearch query."
|
||||
else
|
||||
checkfailed=1
|
||||
echo "glauth check did not pass. Aborting."
|
||||
fi
|
||||
inform "Stopping image"
|
||||
podman stop checkglauth && podman rm checkglauth
|
||||
if [[ checkfailed -eq 1 ]]; then
|
||||
# Note that we are not removing the registry so that
|
||||
# we can manually check why we failed.
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Skipping ldapsearch sanity check. Command not present."
|
||||
fi
|
||||
|
||||
inform "Stopping registry"
|
||||
podman stop registry && podman rm registry
|
||||
}
|
||||
|
||||
prepare_images() {
|
||||
prepare_image standalone
|
||||
prepare_image plugins
|
||||
}
|
||||
|
||||
push_profile_to_docker() {
|
||||
profile="$1"
|
||||
tag="$2"
|
||||
REGISTRY_AUTH_FILE=~/.podmanauth
|
||||
echo "Pushing image to docker hub"
|
||||
podman login
|
||||
podman push localhost:5000/$(image_name $profile) docker.io/glauth/$(image_name $profile):$tag
|
||||
}
|
||||
|
||||
push_to_docker() {
|
||||
tag="$1"
|
||||
push_profile_to_docker standalone $tag
|
||||
push_profile_to_docker plugins $tag
|
||||
}
|
||||
|
||||
TAG="$1"
|
||||
|
||||
if [[ "$1" != "--force" ]]; then
|
||||
tested=0
|
||||
found=0
|
||||
|
||||
if command -v lsof &> /dev/null; then
|
||||
tested=1
|
||||
if lsof -i :5000 &> /dev/null; then
|
||||
found=1
|
||||
fi
|
||||
elif command -v fuser &> /dev/null; then
|
||||
tested=1
|
||||
if fuser 5000/tcp &> /dev/null; then
|
||||
found=1
|
||||
fi
|
||||
elif command -v netstat &> /dev/null; then
|
||||
tested=1
|
||||
if (netstat -plnt | grep 5000) &> /dev/null; then
|
||||
found=1
|
||||
fi
|
||||
fi
|
||||
if [[ tested -eq 0 ]]; then
|
||||
echo "Unable to figure out whether a registry is already running."
|
||||
echo "Please install lsof, fuser or netstat, or run with '--force'"
|
||||
exit 1
|
||||
fi
|
||||
if [[ found -eq 1 ]]; then
|
||||
echo "Port 5000 is busy. I will not be able to run the local registry. Aborting."
|
||||
echo "You can avoid this check by running with '--force'"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
TAG="$2"
|
||||
fi
|
||||
|
||||
if [[ "$TAG" == "" ]]; then
|
||||
echo "Please provide a tag for this image."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
prepare_images
|
||||
|
||||
while true; do
|
||||
read -p "Everything seems ok. Push to Docker registry? (y/n) " yn
|
||||
case $yn in
|
||||
[Yy]* ) push_to_docker $TAG; break;;
|
||||
[Nn]* ) exit;;
|
||||
esac
|
||||
done
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
img="localhost/glauth-dev"
|
||||
|
||||
sudo docker rmi -f "$img"
|
||||
|
||||
sudo docker build -f Dockerfile -t "$img" .
|
||||
|
||||
sudo docker run -P "$img"
|
||||
@@ -0,0 +1,70 @@
|
||||
# Changelog
|
||||
|
||||
## [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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove spinlock in monitoring code ([#405](https://github.com/glauth/glauth/issues/405)) ([a2c151b](https://github.com/glauth/glauth/commit/a2c151b0d025332462369a846a51ef4deff5332b))
|
||||
|
||||
## [2.3.1](https://github.com/glauth/glauth/compare/v2.2.1...v2.3.1) (2024-02-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* allow tracing configuration via main config ([f692394](https://github.com/glauth/glauth/commit/f692394942aa0f93a1aa1572331fd1c3e3553156))
|
||||
* 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 otelsql ([2ca5312](https://github.com/glauth/glauth/commit/2ca53126965aa7d42b23aee15f750df12822d454))
|
||||
* introduce otlp tracer ([0cf0403](https://github.com/glauth/glauth/commit/0cf04037a2a7b38c8ed7af2451b115f48c5427b5))
|
||||
* wire up basic tracer ([1c2b23c](https://github.com/glauth/glauth/commit/1c2b23c00ff85b83a6d2e4bf4a9a68081aaf2777))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 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))
|
||||
* removed config setup from main, reshoring log configuration ([5fe8aca](https://github.com/glauth/glauth/commit/5fe8aca852bdfe7e375ea99b87074bae35fc3407))
|
||||
* 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
|
||||
|
||||
* release 2.3.1 ([0bf3d4a](https://github.com/glauth/glauth/commit/0bf3d4a82a8451e7bbda74e3730345aab5a855a7))
|
||||
|
||||
## [2.3.0](https://github.com/glauth/glauth/compare/v2.2.0...v2.3.0) (2023-10-02)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **plugins:** Rename the groups table to ldapgroups ([#326](https://github.com/glauth/glauth/issues/326))
|
||||
|
||||
### Features
|
||||
|
||||
* Update migration code to support table names ([#339](https://github.com/glauth/glauth/issues/339)) ([349431c](https://github.com/glauth/glauth/commit/349431c6caa0388d17ab987621eb9be5f019155e))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **plugins:** Rename the groups table to ldapgroups ([#326](https://github.com/glauth/glauth/issues/326)) ([675b236](https://github.com/glauth/glauth/commit/675b236328a21a65daa7876a1a3c6900b85b1964))
|
||||
|
||||
|
||||
### Miscellaneous Chores
|
||||
|
||||
* release 2.3.0 ([0c11325](https://github.com/glauth/glauth/commit/0c11325a2482d5067c805a4c7ed948a5e337b8f8))
|
||||
@@ -1,64 +0,0 @@
|
||||
#################
|
||||
# Build Step
|
||||
#################
|
||||
|
||||
FROM golang:buster as build
|
||||
LABEL maintainers="Ben Yanke <ben@benyanke.com>, Jörn Friedrich Dreyer <jfd@butonic.de>, Chris F Ravenscroft <chris@voilaweb.com>"
|
||||
|
||||
# Setup work env
|
||||
RUN mkdir /app /tmp/gocode
|
||||
ADD . /app/
|
||||
WORKDIR /app
|
||||
|
||||
# Required envs for GO
|
||||
ENV GOPATH=/tmp/gocode
|
||||
ENV GOOS=linux
|
||||
ENV GOARCH=amd64
|
||||
ENV GO_ENABLED=1
|
||||
|
||||
# Only needed for alpine builds // also: busybox
|
||||
RUN apt -qq update && apt -qq install -y git make build-essential busybox-static dumb-init
|
||||
|
||||
# Install deps
|
||||
RUN go get -d -v ./...
|
||||
|
||||
# Build and copy final result
|
||||
RUN uname -a
|
||||
RUN uname -m
|
||||
RUN bash -c '[[ $(uname -m) == x86_64 ]] && { make linuxamd64 && cp ./bin/linuxamd64/glauth /app/glauth; export PLUGIN_OS=linux PLUGIN_ARCH=amd64; make bin/linuxamd64/sqlite.so bin/linuxamd64/postgres.so bin/linuxamd64/mysql.so && cp ./bin/linuxamd64/*.so /app/; } || { true; }'
|
||||
RUN bash -c '[[ $(uname -m) == aarch64 ]] && { make linuxarm64 && cp ./bin/linuxarm64/glauth /app/glauth; export PLUGIN_OS=linux PLUGIN_ARCH=arm64; make bin/linuxarm64/sqlite.so bin/linuxarm64/postgres.so bin/linuxarm64/mysql.so && cp ./bin/linuxarm64/*.so /app/; } || { true; }'
|
||||
|
||||
# Check glauth works
|
||||
RUN /app/glauth --version
|
||||
|
||||
#################
|
||||
# Run Step
|
||||
#################
|
||||
|
||||
FROM gcr.io/distroless/base-debian10 as run
|
||||
LABEL maintainers="Ben Yanke <ben@benyanke.com>, Jörn Friedrich Dreyer <jfd@butonic.de>, Chris F Ravenscroft <chris@voilaweb.com>"
|
||||
|
||||
# Copy binary from build container
|
||||
COPY --from=build /app/glauth /app/glauth
|
||||
COPY --from=build /app/*.so /app/
|
||||
|
||||
# Copy docker specific scripts from build container
|
||||
COPY --from=build /app/scripts/docker/start-plugins.sh /app/docker/
|
||||
COPY --from=build /app/scripts/docker/default-config-plugins.cfg /app/docker/
|
||||
COPY --from=build /app/scripts/docker/gl.db /app/docker/
|
||||
|
||||
# Just what we need
|
||||
COPY --from=build /usr/bin/dumb-init /usr/bin/dumb-init
|
||||
COPY --from=build /bin/busybox /bin/sh
|
||||
COPY --from=build /bin/busybox /bin/ln
|
||||
COPY --from=build /bin/busybox /bin/rm
|
||||
# Debug env: COPY --from=build /bin/busybox /bin/busybox
|
||||
RUN ln /bin/sh /usr/bin/cp && ln /bin/sh /usr/bin/mkdir && rm /bin/ln /bin/rm
|
||||
|
||||
# Install init
|
||||
|
||||
# Expose web and LDAP ports
|
||||
EXPOSE 389 636 5555
|
||||
|
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
||||
CMD ["/bin/sh", "/app/docker/start-plugins.sh"]
|
||||
@@ -1,61 +0,0 @@
|
||||
#################
|
||||
# Build Step
|
||||
#################
|
||||
|
||||
FROM golang:alpine as build
|
||||
LABEL maintainers="Ben Yanke <ben@benyanke.com>, Jörn Friedrich Dreyer <jfd@butonic.de>, Chris F Ravenscroft <chris@voilaweb.com>"
|
||||
|
||||
# Setup work env
|
||||
RUN mkdir /app /tmp/gocode
|
||||
ADD . /app/
|
||||
WORKDIR /app
|
||||
|
||||
# Required envs for GO
|
||||
ENV GOPATH=/tmp/gocode
|
||||
ENV GOOS=linux
|
||||
ENV GOARCH=amd64
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
# Only needed for alpine builds // also: busybox
|
||||
RUN apk add --no-cache git make busybox-static dumb-init
|
||||
|
||||
# Install deps
|
||||
RUN go get -d -v ./...
|
||||
|
||||
# Build and copy final result
|
||||
RUN uname -a
|
||||
RUN if [ $(uname -m) == x86_64 ]; then make linuxamd64 && cp ./bin/linuxamd64/glauth /app/glauth; fi
|
||||
RUN if [ $(uname -m) == aarch64 ]; then make linuxarm64 && cp ./bin/linuxarm64/glauth /app/glauth; fi
|
||||
RUN if [ $(uname -m) == armv7l ]; then make linuxarm && cp ./bin/linuxarm/glauth /app/glauth; fi
|
||||
|
||||
# Check glauth works
|
||||
RUN /app/glauth --version
|
||||
|
||||
#################
|
||||
# Run Step
|
||||
#################
|
||||
|
||||
FROM gcr.io/distroless/base-debian10 as run
|
||||
LABEL maintainers="Ben Yanke <ben@benyanke.com>, Jörn Friedrich Dreyer <jfd@butonic.de>, Chris F Ravenscroft <chris@voilaweb.com>"
|
||||
|
||||
# Copy binary from build container
|
||||
COPY --from=build /app/glauth /app/glauth
|
||||
|
||||
# Copy docker specific scripts from build container
|
||||
COPY --from=build /app/scripts/docker/start-standalone.sh /app/docker/
|
||||
COPY --from=build /app/scripts/docker/default-config-standalone.cfg /app/docker/
|
||||
|
||||
# Just what we need
|
||||
COPY --from=build /usr/bin/dumb-init /usr/bin/dumb-init
|
||||
COPY --from=build /bin/busybox.static /bin/sh
|
||||
COPY --from=build /bin/busybox.static /bin/ln
|
||||
COPY --from=build /bin/busybox.static /bin/rm
|
||||
RUN ln /bin/sh /usr/bin/cp && ln /bin/sh /usr/bin/mkdir && rm /bin/ln /bin/rm
|
||||
|
||||
# Install init
|
||||
|
||||
# Expose web and LDAP ports
|
||||
EXPOSE 389 636 5555
|
||||
|
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
||||
CMD ["/bin/sh", "/app/docker/start-standalone.sh"]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user