mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-05 19:59:37 -06:00
add glauth fallback backend
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
8
changelog/unreleased/glauth-fallback-option.md
Normal file
8
changelog/unreleased/glauth-fallback-option.md
Normal file
@@ -0,0 +1,8 @@
|
||||
Enhancement: Add glauth fallback backend
|
||||
|
||||
We introduced the `fallback-datastore` config option and the corresponding options to allow configuring a simple chain of two handlers.
|
||||
|
||||
Simple, because it is intended for bind and single result search queries. Merging large sets of results is currently out of scope. For now, the implementation will only search the fallback backend if the default backend returns an error or the number of results is 0. This is sufficient to allow an IdP to authenticate users from ocis as well as owncloud 10 as described in the [bridge scenario](https://owncloud.github.io/ocis/bridge/).
|
||||
|
||||
https://github.com/owncloud/ocis/pull/649
|
||||
https://github.com/owncloud/ocis-glauth/issues/18
|
||||
@@ -7,8 +7,6 @@ geekdocEditPath: edit/master/docs/extensions/glauth
|
||||
geekdocFilePath: _index.md
|
||||
---
|
||||
|
||||
This service provides a simple glauth world API which can be used by clients or other extensions.
|
||||
This service provides a [glauth](https://github.com/glauth/glauth) based LDAP proxy for ocis which can be used by clients or other extensions. It allows applications relying on LDAP to access the accounts stored in the ocis accounts service. It can be used to make firewalls or identity providers aware of all users, including guest accounts.
|
||||
|
||||
- reiner proxy
|
||||
ldap für eos und firewall
|
||||
- backend ist der accounts service
|
||||
We are using it to make eos aware of all accounts so the native ACLs can be used to persist share information in the storage.
|
||||
|
||||
@@ -9,264 +9,6 @@ geekdocFilePath: getting-started.md
|
||||
|
||||
{{< toc >}}
|
||||
|
||||
## Installation
|
||||
## Configuration Tips
|
||||
|
||||
So far we are offering two different variants for the installation. You can choose between [Docker](https://www.docker.com/) or pre-built binaries which are stored on our download mirrors and GitHub releases. Maybe we will also provide system packages for the major distributions later if we see the need for it.
|
||||
|
||||
### Docker
|
||||
|
||||
TBD
|
||||
|
||||
### Binaries
|
||||
|
||||
TBD
|
||||
|
||||
## Configuration
|
||||
|
||||
We provide overall three different variants of configuration. The variant based on environment variables and commandline flags are split up into global values and command-specific values.
|
||||
|
||||
### Envrionment variables
|
||||
|
||||
If you prefer to configure the service with environment variables you can see the available variables below.
|
||||
|
||||
#### Global
|
||||
|
||||
GLAUTH_CONFIG_FILE
|
||||
: Path to config file, empty default value
|
||||
|
||||
GLAUTH_LOG_LEVEL
|
||||
: Set logging level, defaults to `info`
|
||||
|
||||
GLAUTH_LOG_COLOR
|
||||
: Enable colored logging, defaults to `true`
|
||||
|
||||
GLAUTH_LOG_PRETTY
|
||||
: Enable pretty logging, defaults to `true`
|
||||
|
||||
#### Server
|
||||
|
||||
GLAUTH_TRACING_ENABLED
|
||||
: Enable sending traces, defaults to `false`
|
||||
|
||||
GLAUTH_TRACING_TYPE
|
||||
: Tracing backend type, defaults to `jaeger`
|
||||
|
||||
GLAUTH_TRACING_ENDPOINT
|
||||
: Endpoint for the agent, empty default value
|
||||
|
||||
GLAUTH_TRACING_COLLECTOR
|
||||
: Endpoint for the collector, empty default value
|
||||
|
||||
GLAUTH_TRACING_SERVICE
|
||||
: Service name for tracing, defaults to `glauth`
|
||||
|
||||
GLAUTH_DEBUG_ADDR
|
||||
: Address to bind debug server, defaults to `0.0.0.0:9124`
|
||||
|
||||
GLAUTH_DEBUG_TOKEN
|
||||
: Token to grant metrics access, empty default value
|
||||
|
||||
GLAUTH_DEBUG_PPROF
|
||||
: Enable pprof debugging, defaults to `false`
|
||||
|
||||
GLAUTH_DEBUG_ZPAGES
|
||||
: Enable zpages debugging, defaults to `false`
|
||||
|
||||
GLAUTH_HTTP_ADDR
|
||||
: Address to bind http server, defaults to `0.0.0.0:9120`
|
||||
|
||||
GLAUTH_HTTP_NAMESPACE
|
||||
: The http namespace
|
||||
|
||||
GLAUTH_HTTP_ROOT
|
||||
: Root path of http server, defaults to `/`
|
||||
|
||||
#### Health
|
||||
|
||||
GLAUTH_DEBUG_ADDR
|
||||
: Address to debug endpoint, defaults to `0.0.0.0:9124`
|
||||
|
||||
### Commandline flags
|
||||
|
||||
If you prefer to configure the service with commandline flags you can see the available variables below.
|
||||
|
||||
#### Global
|
||||
|
||||
--config-file
|
||||
: Path to config file, empty default value
|
||||
|
||||
--log-level
|
||||
: Set logging level, defaults to `info`
|
||||
|
||||
--log-color
|
||||
: Enable colored logging, defaults to `true`
|
||||
|
||||
--log-pretty
|
||||
: Enable pretty logging, defaults to `true`
|
||||
|
||||
#### Server
|
||||
|
||||
--tracing-enabled
|
||||
: Enable sending traces, defaults to `false`
|
||||
|
||||
--tracing-type
|
||||
: Tracing backend type, defaults to `jaeger`
|
||||
|
||||
--tracing-endpoint
|
||||
: Endpoint for the agent, empty default value
|
||||
|
||||
--tracing-collector
|
||||
: Endpoint for the collector, empty default value
|
||||
|
||||
--tracing-service
|
||||
: Service name for tracing, defaults to `glauth`
|
||||
|
||||
--debug-addr
|
||||
: Address to bind debug server, defaults to `0.0.0.0:9124`
|
||||
|
||||
--debug-token
|
||||
: Token to grant metrics access, empty default value
|
||||
|
||||
--debug-pprof
|
||||
: Enable pprof debugging, defaults to `false`
|
||||
|
||||
--debug-zpages
|
||||
: Enable zpages debugging, defaults to `false`
|
||||
|
||||
--http-addr
|
||||
: Address to bind http server, defaults to `0.0.0.0:9120`
|
||||
|
||||
--http-namespace
|
||||
: Namespace for internal services communication, defaults to `com.owncloud.web`
|
||||
|
||||
--http-root
|
||||
: Root path of http server, defaults to `/`
|
||||
|
||||
#### Health
|
||||
|
||||
--debug-addr
|
||||
: Address to debug endpoint, defaults to `0.0.0.0:9124`
|
||||
|
||||
### Configuration file
|
||||
|
||||
So far we support the file formats `JSON` and `YAML`, if you want to get a full example configuration just take a look at [our repository](https://github.com/owncloud/ocis/glauth/tree/master/config), there you can always see the latest configuration format. These example configurations include all available options and the default values. The configuration file will be automatically loaded if it's placed at `/etc/ocis/glauth.yml`, `${HOME}/.ocis/glauth.yml` or `$(pwd)/config/glauth.yml`.
|
||||
|
||||
## Usage
|
||||
|
||||
The program provides a few sub-commands on execution. The available configuration methods have already been mentioned above. Generally you can always see a formated help output if you execute the binary via `ocis-glauth --help`.
|
||||
|
||||
### Server
|
||||
|
||||
The server command is used to start the http and debug server on two addresses within a single process. The http server is serving the general webservice while the debug server is used for health check, readiness check and to server the metrics mentioned below. For further help please execute:
|
||||
|
||||
{{< highlight txt >}}
|
||||
ocis-glauth server --help
|
||||
{{< / highlight >}}
|
||||
|
||||
### Health
|
||||
|
||||
The health command is used to execute a health check, if the exit code equals zero the service should be up and running, if the exist code is greater than zero the service is not in a healthy state. Generally this command is used within our Docker containers, it could also be used within Kubernetes.
|
||||
|
||||
{{< highlight txt >}}
|
||||
ocis-glauth health --help
|
||||
{{< / highlight >}}
|
||||
|
||||
## Metrics
|
||||
|
||||
This service provides some [Prometheus](https://prometheus.io/) metrics through the debug endpoint, you can optionally secure the metrics endpoint by some random token, which got to be configured through one of the flag `--debug-token` or the environment variable `GLAUTH_DEBUG_TOKEN` mentioned above. By default the metrics endpoint is bound to `http://0.0.0.0:9124/metrics`.
|
||||
|
||||
go_gc_duration_seconds
|
||||
: A summary of the GC invocation durations
|
||||
|
||||
go_gc_duration_seconds_sum
|
||||
: A summary of the GC invocation durations
|
||||
|
||||
go_gc_duration_seconds_count
|
||||
: A summary of the GC invocation durations
|
||||
|
||||
go_goroutines
|
||||
: Number of goroutines that currently exist
|
||||
|
||||
go_info
|
||||
: Information about the Go environment
|
||||
|
||||
go_memstats_alloc_bytes
|
||||
: Number of bytes allocated and still in use
|
||||
|
||||
go_memstats_alloc_bytes_total
|
||||
: Total number of bytes allocated, even if freed
|
||||
|
||||
go_memstats_buck_hash_sys_bytes
|
||||
: Number of bytes used by the profiling bucket hash table
|
||||
|
||||
go_memstats_frees_total
|
||||
: Total number of frees
|
||||
|
||||
go_memstats_gc_cpu_fraction
|
||||
: The fraction of this program's available CPU time used by the GC since the program started
|
||||
|
||||
go_memstats_gc_sys_bytes
|
||||
: Number of bytes used for garbage collection system metadata
|
||||
|
||||
go_memstats_heap_alloc_bytes
|
||||
: Number of heap bytes allocated and still in use
|
||||
|
||||
go_memstats_heap_idle_bytes
|
||||
: Number of heap bytes waiting to be used
|
||||
|
||||
go_memstats_heap_inuse_bytes
|
||||
: Number of heap bytes that are in use
|
||||
|
||||
go_memstats_heap_objects
|
||||
: Number of allocated objects
|
||||
|
||||
go_memstats_heap_released_bytes
|
||||
: Number of heap bytes released to OS
|
||||
|
||||
go_memstats_heap_sys_bytes
|
||||
: Number of heap bytes obtained from system
|
||||
|
||||
go_memstats_last_gc_time_seconds
|
||||
: Number of seconds since 1970 of last garbage collection
|
||||
|
||||
go_memstats_lookups_total
|
||||
: Total number of pointer lookups
|
||||
|
||||
go_memstats_mallocs_total
|
||||
: Total number of mallocs
|
||||
|
||||
go_memstats_mcache_inuse_bytes
|
||||
: Number of bytes in use by mcache structures
|
||||
|
||||
go_memstats_mcache_sys_bytes
|
||||
: Number of bytes used for mcache structures obtained from system
|
||||
|
||||
go_memstats_mspan_inuse_bytes
|
||||
: Number of bytes in use by mspan structures
|
||||
|
||||
go_memstats_mspan_sys_bytes
|
||||
: Number of bytes used for mspan structures obtained from system
|
||||
|
||||
go_memstats_next_gc_bytes
|
||||
: Number of heap bytes when next garbage collection will take place
|
||||
|
||||
go_memstats_other_sys_bytes
|
||||
: Number of bytes used for other system allocations
|
||||
|
||||
go_memstats_stack_inuse_bytes
|
||||
: Number of bytes in use by the stack allocator
|
||||
|
||||
go_memstats_stack_sys_bytes
|
||||
: Number of bytes obtained from system for stack allocator
|
||||
|
||||
go_memstats_sys_bytes
|
||||
: Number of bytes obtained from system
|
||||
|
||||
go_threads
|
||||
: Number of OS threads created
|
||||
|
||||
promhttp_metric_handler_requests_in_flight
|
||||
: Current number of scrapes being served
|
||||
|
||||
promhttp_metric_handler_requests_total
|
||||
: Total number of scrapes by HTTP status code
|
||||
The default setup does not use a fallback backend. It can be enabled by setting the `GLAUTH_FALLBACK_DATASTORE` environment variable. When using `owncloud` make sure to use the full URL to the [ownCloud 10 graph api app](https://github.com/owncloud/graphapi) endpoint, eg.: `GLAUTH_FALLBACK_SERVERS="https://demo.owncloud.com/apps/graphapi/v1.0"`
|
||||
|
||||
@@ -5,22 +5,23 @@ go 1.13
|
||||
require (
|
||||
contrib.go.opencensus.io/exporter/jaeger v0.2.1
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.7.0
|
||||
contrib.go.opencensus.io/exporter/zipkin v0.1.1
|
||||
contrib.go.opencensus.io/exporter/zipkin v0.1.2
|
||||
github.com/GeertJohan/yubigo v0.0.0-20190917122436-175bc097e60e
|
||||
github.com/UnnoTed/fileb0x v1.1.4
|
||||
github.com/glauth/glauth v1.1.3-0.20200228160118-2d4f5d547682
|
||||
github.com/glauth/glauth v1.1.3-0.20201005201919-4d42af8aacbf
|
||||
github.com/go-logr/logr v0.1.0
|
||||
github.com/micro/cli/v2 v2.1.2
|
||||
github.com/micro/go-micro/v2 v2.9.1
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
|
||||
github.com/nmcclain/ldap v0.0.0-20191021200707-3b3b69a7e9e3
|
||||
github.com/oklog/run v1.1.0
|
||||
github.com/openzipkin/zipkin-go v0.2.2
|
||||
github.com/owncloud/ocis/accounts v0.0.0-20200918125107-fcca9faa81c8
|
||||
github.com/openzipkin/zipkin-go v0.2.5
|
||||
github.com/owncloud/ocis/accounts v0.5.2
|
||||
github.com/owncloud/ocis/ocis-pkg v0.0.0-20200918114005-1a0ddd2190ee
|
||||
github.com/prometheus/client_golang v1.7.1
|
||||
github.com/restic/calens v0.2.0
|
||||
github.com/rs/zerolog v1.19.0
|
||||
github.com/spf13/viper v1.7.0
|
||||
github.com/rs/zerolog v1.20.0
|
||||
github.com/spf13/viper v1.7.1
|
||||
go.opencensus.io v0.22.4
|
||||
)
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ contrib.go.opencensus.io/exporter/ocagent v0.7.0/go.mod h1:IshRmMJBhDfFj5Y67nVhM
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.2.0/go.mod h1:TYmVAyE8Tn1lyPcltF5IYYfWp2KHu7lQGIZnj8iZMys=
|
||||
contrib.go.opencensus.io/exporter/zipkin v0.1.1 h1:PR+1zWqY8ceXs1qDQQIlgXe+sdiwCf0n32bH4+Epk8g=
|
||||
contrib.go.opencensus.io/exporter/zipkin v0.1.1/go.mod h1:GMvdSl3eJ2gapOaLKzTKE3qDgUkJ86k9k3yY2eqwkzc=
|
||||
contrib.go.opencensus.io/exporter/zipkin v0.1.2 h1:YqE293IZrKtqPnpwDPH/lOqTWD/s3Iwabycam74JV3g=
|
||||
contrib.go.opencensus.io/exporter/zipkin v0.1.2/go.mod h1:mP5xM3rrgOjpn79MM8fZbj3gsxcuytSqtH0dxSWW1RE=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
@@ -277,8 +279,8 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc=
|
||||
github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY=
|
||||
github.com/glauth/glauth v1.1.3-0.20200228160118-2d4f5d547682 h1:8qan0vNLF7i9Wq07mhTn/60hXEPdGTYJnR82P4X818s=
|
||||
github.com/glauth/glauth v1.1.3-0.20200228160118-2d4f5d547682/go.mod h1:ygO1z1pcp79iBrjbA6vqrsUxIonStjBncosl2a9/Dx8=
|
||||
github.com/glauth/glauth v1.1.3-0.20201005201919-4d42af8aacbf h1:3ejnL7OvxCJ6XiEJjv16odGGUPvZlqjfDLefPVF7Vrw=
|
||||
github.com/glauth/glauth v1.1.3-0.20201005201919-4d42af8aacbf/go.mod h1:ygO1z1pcp79iBrjbA6vqrsUxIonStjBncosl2a9/Dx8=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4=
|
||||
@@ -882,6 +884,8 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go v0.2.2 h1:nY8Hti+WKaP0cRsSeQ026wU03QsM762XBeCXBb9NAWI=
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.5 h1:UwtQQx2pyPIgWYHRg+epgdx1/HnBQTgN3/oIYEJTQzU=
|
||||
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
|
||||
github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
|
||||
github.com/ory/fosite v0.29.0/go.mod h1:0atSZmXO7CAcs6NPMI/Qtot8tmZYj04Nddoold4S2h0=
|
||||
@@ -986,6 +990,8 @@ github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.19.0 h1:hYz4ZVdUgjXTBUmrkrw55j1nHx68LfOKIQk5IYtyScg=
|
||||
github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
|
||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
github.com/rubenv/sql-migrate v0.0.0-20190212093014-1007f53448d7/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
@@ -1059,6 +1065,8 @@ github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
|
||||
github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
|
||||
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/steveyen/gtreap v0.1.0 h1:CjhzTa274PyJLJuMZwIzCO1PfC00oRa8d1Kc78bFXJM=
|
||||
github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7Z4dM9/Y=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
|
||||
@@ -2,12 +2,13 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/owncloud/ocis/glauth/pkg/metrics"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/owncloud/ocis/glauth/pkg/metrics"
|
||||
|
||||
"github.com/owncloud/ocis/glauth/pkg/crypto"
|
||||
|
||||
"contrib.go.opencensus.io/exporter/jaeger"
|
||||
@@ -42,6 +43,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
}
|
||||
|
||||
cfg.Backend.Servers = c.StringSlice("backend-server")
|
||||
cfg.Fallback.Servers = c.StringSlice("fallback-server")
|
||||
|
||||
return ParseConfig(c, cfg)
|
||||
},
|
||||
@@ -143,17 +145,20 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
metrics.BuildInfo.WithLabelValues(cfg.Version).Set(1)
|
||||
|
||||
{
|
||||
cfg := glauthcfg.Config{
|
||||
LDAP: glauthcfg.LDAP{
|
||||
Enabled: cfg.Ldap.Enabled,
|
||||
Listen: cfg.Ldap.Address,
|
||||
},
|
||||
LDAPS: glauthcfg.LDAPS{
|
||||
Enabled: cfg.Ldaps.Enabled,
|
||||
Listen: cfg.Ldaps.Address,
|
||||
Cert: cfg.Ldaps.Cert,
|
||||
Key: cfg.Ldaps.Key,
|
||||
},
|
||||
|
||||
lcfg := glauthcfg.LDAP{
|
||||
Enabled: cfg.Ldap.Enabled,
|
||||
Listen: cfg.Ldap.Address,
|
||||
}
|
||||
lscfg := glauthcfg.LDAPS{
|
||||
Enabled: cfg.Ldaps.Enabled,
|
||||
Listen: cfg.Ldaps.Address,
|
||||
Cert: cfg.Ldaps.Cert,
|
||||
Key: cfg.Ldaps.Key,
|
||||
}
|
||||
bcfg := glauthcfg.Config{
|
||||
LDAP: lcfg, // TODO remove LDAP from the backend config upstream
|
||||
LDAPS: lscfg, // TODO remove LDAP from the backend config upstream
|
||||
Backend: glauthcfg.Backend{
|
||||
Datastore: cfg.Backend.Datastore,
|
||||
BaseDN: cfg.Backend.BaseDN,
|
||||
@@ -165,8 +170,22 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
UseGraphAPI: cfg.Backend.UseGraphAPI,
|
||||
},
|
||||
}
|
||||
fcfg := glauthcfg.Config{
|
||||
LDAP: lcfg, // TODO remove LDAP from the backend config upstream
|
||||
LDAPS: lscfg, // TODO remove LDAP from the backend config upstream
|
||||
Backend: glauthcfg.Backend{
|
||||
Datastore: cfg.Fallback.Datastore,
|
||||
BaseDN: cfg.Fallback.BaseDN,
|
||||
Insecure: cfg.Fallback.Insecure,
|
||||
NameFormat: cfg.Fallback.NameFormat,
|
||||
GroupFormat: cfg.Fallback.GroupFormat,
|
||||
Servers: cfg.Fallback.Servers,
|
||||
SSHKeyAttr: cfg.Fallback.SSHKeyAttr,
|
||||
UseGraphAPI: cfg.Fallback.UseGraphAPI,
|
||||
},
|
||||
}
|
||||
|
||||
if cfg.LDAPS.Enabled {
|
||||
if lscfg.Enabled {
|
||||
// GenCert has side effects as it writes 2 files to the binary running location
|
||||
if err := crypto.GenCert("ldap.crt", "ldap.key", logger); err != nil {
|
||||
logger.Fatal().Err(err).Msgf("Could not generate test-certificate")
|
||||
@@ -182,7 +201,10 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
glauth.AccountsService(as),
|
||||
glauth.GroupsService(gs),
|
||||
glauth.Logger(logger),
|
||||
glauth.Config(&cfg),
|
||||
glauth.LDAP(&lcfg),
|
||||
glauth.LDAPS(&lscfg),
|
||||
glauth.Backend(&bcfg),
|
||||
glauth.Fallback(&fcfg),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -58,15 +58,16 @@ type Backend struct {
|
||||
|
||||
// Config combines all available configuration parts.
|
||||
type Config struct {
|
||||
File string
|
||||
Log Log
|
||||
Debug Debug
|
||||
HTTP HTTP
|
||||
Tracing Tracing
|
||||
Ldap Ldap
|
||||
Ldaps Ldaps
|
||||
Backend Backend
|
||||
Version string
|
||||
File string
|
||||
Log Log
|
||||
Debug Debug
|
||||
HTTP HTTP
|
||||
Tracing Tracing
|
||||
Ldap Ldap
|
||||
Ldaps Ldaps
|
||||
Backend Backend
|
||||
Fallback Backend
|
||||
Version string
|
||||
}
|
||||
|
||||
// New initializes a new configuration with or without defaults.
|
||||
|
||||
@@ -159,14 +159,9 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
|
||||
EnvVars: []string{"GLAUTH_LDAPS_KEY"},
|
||||
Destination: &cfg.Ldaps.Key,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "backend-datastore",
|
||||
Value: "accounts",
|
||||
// TODO bring back config / flat file support
|
||||
Usage: "datastore to use as the backend. one of accounts, ldap or owncloud",
|
||||
EnvVars: []string{"GLAUTH_BACKEND_DATASTORE"},
|
||||
Destination: &cfg.Backend.Datastore,
|
||||
},
|
||||
|
||||
// backend config
|
||||
|
||||
&cli.StringFlag{
|
||||
Name: "backend-basedn",
|
||||
Value: "dc=example,dc=org",
|
||||
@@ -174,13 +169,6 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
|
||||
EnvVars: []string{"GLAUTH_BACKEND_BASEDN"},
|
||||
Destination: &cfg.Backend.BaseDN,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "backend-insecure",
|
||||
Value: false,
|
||||
Usage: "Allow insecure requests to the datastore",
|
||||
EnvVars: []string{"GLAUTH_BACKEND_INSECURE"},
|
||||
Destination: &cfg.Backend.Insecure,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "backend-name-format",
|
||||
Value: "cn",
|
||||
@@ -195,12 +183,6 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
|
||||
EnvVars: []string{"GLAUTH_BACKEND_GROUP_FORMAT"},
|
||||
Destination: &cfg.Backend.GroupFormat,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "backend-server",
|
||||
Value: cli.NewStringSlice("https://demo.owncloud.com"),
|
||||
Usage: `--backend-server http://internal1.example.com [--backend-server http://internal2.example.com]`,
|
||||
EnvVars: []string{"GLAUTH_BACKEND_SERVERS"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "backend-ssh-key-attr",
|
||||
Value: "sshPublicKey",
|
||||
@@ -208,6 +190,27 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
|
||||
EnvVars: []string{"GLAUTH_BACKEND_SSH_KEY_ATTR"},
|
||||
Destination: &cfg.Backend.SSHKeyAttr,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "backend-datastore",
|
||||
Value: "accounts",
|
||||
// TODO bring back config / flat file support
|
||||
Usage: "datastore to use as the backend. one of accounts, ldap or owncloud",
|
||||
EnvVars: []string{"GLAUTH_BACKEND_DATASTORE"},
|
||||
Destination: &cfg.Backend.Datastore,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "backend-insecure",
|
||||
Value: false,
|
||||
Usage: "Allow insecure requests to the datastore",
|
||||
EnvVars: []string{"GLAUTH_BACKEND_INSECURE"},
|
||||
Destination: &cfg.Backend.Insecure,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "backend-server",
|
||||
Value: cli.NewStringSlice("https://demo.owncloud.com/apps/graphapi/v1.0"),
|
||||
Usage: `--backend-server http://internal1.example.com [--backend-server http://internal2.example.com]`,
|
||||
EnvVars: []string{"GLAUTH_BACKEND_SERVERS"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "backend-use-graphapi",
|
||||
Value: true,
|
||||
@@ -215,5 +218,64 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
|
||||
EnvVars: []string{"GLAUTH_BACKEND_USE_GRAPHAPI"},
|
||||
Destination: &cfg.Backend.UseGraphAPI,
|
||||
},
|
||||
|
||||
// fallback config
|
||||
|
||||
&cli.StringFlag{
|
||||
Name: "fallback-basedn",
|
||||
Value: "dc=example,dc=org",
|
||||
Usage: "base distinguished name to expose",
|
||||
EnvVars: []string{"GLAUTH_FALLBACK_BASEDN"},
|
||||
Destination: &cfg.Fallback.BaseDN,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "fallback-name-format",
|
||||
Value: "cn",
|
||||
Usage: "name attribute for entries to expose. typically cn or uid",
|
||||
EnvVars: []string{"GLAUTH_FALLBACK_NAME_FORMAT"},
|
||||
Destination: &cfg.Fallback.NameFormat,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "fallback-group-format",
|
||||
Value: "ou",
|
||||
Usage: "name attribute for entries to expose. typically ou, cn or dc",
|
||||
EnvVars: []string{"GLAUTH_FALLBACK_GROUP_FORMAT"},
|
||||
Destination: &cfg.Fallback.GroupFormat,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "fallback-ssh-key-attr",
|
||||
Value: "sshPublicKey",
|
||||
Usage: "ssh key attribute for entries to expose",
|
||||
EnvVars: []string{"GLAUTH_FALLBACK_SSH_KEY_ATTR"},
|
||||
Destination: &cfg.Fallback.SSHKeyAttr,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "fallback-datastore",
|
||||
Value: "",
|
||||
// TODO bring back config / flat file support
|
||||
Usage: "datastore to use as the fallback. one of accounts, ldap or owncloud",
|
||||
EnvVars: []string{"GLAUTH_FALLBACK_DATASTORE"},
|
||||
Destination: &cfg.Fallback.Datastore,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "fallback-insecure",
|
||||
Value: false,
|
||||
Usage: "Allow insecure requests to the datastore",
|
||||
EnvVars: []string{"GLAUTH_FALLBACK_INSECURE"},
|
||||
Destination: &cfg.Fallback.Insecure,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "fallback-server",
|
||||
Value: cli.NewStringSlice("https://demo.owncloud.com/apps/graphapi/v1.0"),
|
||||
Usage: `--fallback-server http://internal1.example.com [--fallback-server http://internal2.example.com]`,
|
||||
EnvVars: []string{"GLAUTH_FALLBACK_SERVERS"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "fallback-use-graphapi",
|
||||
Value: true,
|
||||
Usage: "use Graph API, only for owncloud datastore",
|
||||
EnvVars: []string{"GLAUTH_FALLBACK_USE_GRAPHAPI"},
|
||||
Destination: &cfg.Fallback.UseGraphAPI,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
110
glauth/pkg/server/glauth/chain.go
Normal file
110
glauth/pkg/server/glauth/chain.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package glauth
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/glauth/glauth/pkg/handler"
|
||||
"github.com/nmcclain/ldap"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
)
|
||||
|
||||
type chainHandler struct {
|
||||
log log.Logger
|
||||
b handler.Handler
|
||||
f handler.Handler
|
||||
}
|
||||
|
||||
func (h chainHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (res ldap.LDAPResultCode, err error) {
|
||||
h.log.Debug().
|
||||
Str("binddn", bindDN).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
Str("handler", "chain").
|
||||
Msg("Bind request")
|
||||
res, err = h.b.Bind(bindDN, bindSimplePw, conn)
|
||||
switch {
|
||||
case err != nil:
|
||||
h.log.Error().
|
||||
Err(err).
|
||||
Str("binddn", bindDN).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
Str("handler", "chain").
|
||||
Msg("Bind request")
|
||||
return h.f.Bind(bindDN, bindSimplePw, conn)
|
||||
case res == ldap.LDAPResultInvalidCredentials:
|
||||
return h.f.Bind(bindDN, bindSimplePw, conn)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h chainHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (res ldap.ServerSearchResult, err error) {
|
||||
h.log.Debug().
|
||||
Str("binddn", bindDN).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
Str("handler", "chain").
|
||||
Msg("Search request")
|
||||
res, err = h.b.Search(bindDN, searchReq, conn)
|
||||
switch {
|
||||
case err != nil:
|
||||
h.log.Error().
|
||||
Err(err).
|
||||
Str("binddn", bindDN).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
Str("handler", "chain").
|
||||
Msg("Search request")
|
||||
return h.f.Search(bindDN, searchReq, conn)
|
||||
case len(res.Entries) == 0:
|
||||
// yes, we only fall back if there are no results in the first backend
|
||||
// this is not supposed to work for searching lots of users, only to look up a single user
|
||||
// searching multiple users would require merging result sets. out of scope for now.
|
||||
return h.f.Search(bindDN, searchReq, conn)
|
||||
}
|
||||
return
|
||||
}
|
||||
func (h chainHandler) Close(boundDN string, conn net.Conn) error {
|
||||
h.log.Debug().
|
||||
Str("boundDN", boundDN).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
Str("handler", "chain").
|
||||
Msg("Close request")
|
||||
if err := h.b.Close(boundDN, conn); err != nil {
|
||||
h.log.Error().
|
||||
Err(err).
|
||||
Str("boundDN", boundDN).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
Str("handler", "chain").
|
||||
Msg("Close request")
|
||||
}
|
||||
if err := h.f.Close(boundDN, conn); err != nil {
|
||||
h.log.Error().
|
||||
Err(err).
|
||||
Str("boundDN", boundDN).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
Str("handler", "chain").
|
||||
Msg("Close request")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add is not yet supported for the chain backend
|
||||
func (h chainHandler) 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 chain backend
|
||||
func (h chainHandler) 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 chain backend
|
||||
func (h chainHandler) Delete(boundDN string, deleteDN string, conn net.Conn) (result ldap.LDAPResultCode, err error) {
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
// NewChainHandler implements a chain backend with two backends
|
||||
func NewChainHandler(log log.Logger, bh handler.Handler, fh handler.Handler) handler.Handler {
|
||||
return chainHandler{
|
||||
log: log,
|
||||
b: bh,
|
||||
f: fh,
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,11 @@ package glauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/glauth/glauth/pkg/config"
|
||||
"github.com/glauth/glauth/pkg/handler"
|
||||
"github.com/glauth/glauth/pkg/stats"
|
||||
ber "github.com/nmcclain/asn1-ber"
|
||||
@@ -25,19 +23,22 @@ const (
|
||||
)
|
||||
|
||||
type ocisHandler struct {
|
||||
as accounts.AccountsService
|
||||
gs accounts.GroupsService
|
||||
log log.Logger
|
||||
cfg *config.Config
|
||||
as accounts.AccountsService
|
||||
gs accounts.GroupsService
|
||||
log log.Logger
|
||||
basedn string
|
||||
nameFormat string
|
||||
groupFormat string
|
||||
}
|
||||
|
||||
func (h ocisHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (ldap.LDAPResultCode, error) {
|
||||
bindDN = strings.ToLower(bindDN)
|
||||
baseDN := strings.ToLower("," + h.cfg.Backend.BaseDN)
|
||||
baseDN := strings.ToLower("," + h.basedn)
|
||||
|
||||
h.log.Debug().
|
||||
Str("handler", "ocis").
|
||||
Str("binddn", bindDN).
|
||||
Str("basedn", h.cfg.Backend.BaseDN).
|
||||
Str("basedn", h.basedn).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
Msg("Bind request")
|
||||
|
||||
@@ -46,8 +47,9 @@ func (h ocisHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (ldap.LDAP
|
||||
// parse the bindDN - ensure that the bindDN ends with the BaseDN
|
||||
if !strings.HasSuffix(bindDN, baseDN) {
|
||||
h.log.Error().
|
||||
Str("handler", "ocis").
|
||||
Str("binddn", bindDN).
|
||||
Str("basedn", h.cfg.Backend.BaseDN).
|
||||
Str("basedn", h.basedn).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
Msg("BindDN not part of our BaseDN")
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
@@ -55,6 +57,7 @@ func (h ocisHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (ldap.LDAP
|
||||
parts := strings.Split(strings.TrimSuffix(bindDN, baseDN), ",")
|
||||
if len(parts) > 2 {
|
||||
h.log.Error().
|
||||
Str("handler", "ocis").
|
||||
Str("binddn", bindDN).
|
||||
Int("numparts", len(parts)).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
@@ -73,6 +76,7 @@ func (h ocisHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (ldap.LDAP
|
||||
})
|
||||
if err != nil || len(res.Accounts) == 0 {
|
||||
h.log.Error().
|
||||
Str("handler", "ocis").
|
||||
Str("username", userName).
|
||||
Str("binddn", bindDN).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
@@ -82,6 +86,7 @@ func (h ocisHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (ldap.LDAP
|
||||
|
||||
stats.Frontend.Add("bind_successes", 1)
|
||||
h.log.Debug().
|
||||
Str("handler", "ocis").
|
||||
Str("binddn", bindDN).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
Msg("Bind success")
|
||||
@@ -90,11 +95,12 @@ func (h ocisHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (ldap.LDAP
|
||||
|
||||
func (h ocisHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) {
|
||||
bindDN = strings.ToLower(bindDN)
|
||||
baseDN := strings.ToLower("," + h.cfg.Backend.BaseDN)
|
||||
baseDN := strings.ToLower("," + h.basedn)
|
||||
searchBaseDN := strings.ToLower(searchReq.BaseDN)
|
||||
h.log.Debug().
|
||||
Str("handler", "ocis").
|
||||
Str("binddn", bindDN).
|
||||
Str("basedn", h.cfg.Backend.BaseDN).
|
||||
Str("basedn", h.basedn).
|
||||
Str("filter", searchReq.Filter).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
Msg("Search request")
|
||||
@@ -109,12 +115,12 @@ func (h ocisHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn ne
|
||||
if !strings.HasSuffix(bindDN, baseDN) {
|
||||
return ldap.ServerSearchResult{
|
||||
ResultCode: ldap.LDAPResultInsufficientAccessRights,
|
||||
}, fmt.Errorf("search error: BindDN %s not in our BaseDN %s", bindDN, h.cfg.Backend.BaseDN)
|
||||
}, fmt.Errorf("search error: BindDN %s not in our BaseDN %s", bindDN, h.basedn)
|
||||
}
|
||||
if !strings.HasSuffix(searchBaseDN, h.cfg.Backend.BaseDN) {
|
||||
if !strings.HasSuffix(searchBaseDN, h.basedn) {
|
||||
return ldap.ServerSearchResult{
|
||||
ResultCode: ldap.LDAPResultInsufficientAccessRights,
|
||||
}, fmt.Errorf("search error: search BaseDN %s is not in our BaseDN %s", searchBaseDN, h.cfg.Backend.BaseDN)
|
||||
}, fmt.Errorf("search error: search BaseDN %s is not in our BaseDN %s", searchBaseDN, h.basedn)
|
||||
}
|
||||
|
||||
var qtype queryType = ""
|
||||
@@ -127,21 +133,23 @@ func (h ocisHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn ne
|
||||
var cf *ber.Packet
|
||||
cf, err = ldap.CompileFilter(searchReq.Filter)
|
||||
if err != nil {
|
||||
h.log.Debug().
|
||||
h.log.Error().
|
||||
Err(err).
|
||||
Str("handler", "ocis").
|
||||
Str("binddn", bindDN).
|
||||
Str("basedn", h.cfg.Backend.BaseDN).
|
||||
Str("basedn", h.basedn).
|
||||
Str("filter", searchReq.Filter).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
Msg("could not compile filter")
|
||||
return ldap.ServerSearchResult{
|
||||
ResultCode: ldap.LDAPResultOperationsError,
|
||||
}, fmt.Errorf("Search Error: error parsing filter: %s", searchReq.Filter)
|
||||
}, fmt.Errorf("Search Error: error compiling filter: %s, error: %s", searchReq.Filter, err.Error())
|
||||
}
|
||||
qtype, query, code, err = parseFilter(cf)
|
||||
if err != nil {
|
||||
return ldap.ServerSearchResult{
|
||||
ResultCode: code,
|
||||
}, fmt.Errorf("Search Error: error parsing filter: %s", searchReq.Filter)
|
||||
}, fmt.Errorf("Search Error: error parsing filter: %s, error: %s", searchReq.Filter, err.Error())
|
||||
}
|
||||
|
||||
// check if the searchBaseDN already has a username and add it to the query
|
||||
@@ -156,8 +164,9 @@ func (h ocisHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn ne
|
||||
|
||||
entries := []*ldap.Entry{}
|
||||
h.log.Debug().
|
||||
Str("handler", "ocis").
|
||||
Str("binddn", bindDN).
|
||||
Str("basedn", h.cfg.Backend.BaseDN).
|
||||
Str("basedn", h.basedn).
|
||||
Str("filter", searchReq.Filter).
|
||||
Str("qtype", string(qtype)).
|
||||
Str("query", query).
|
||||
@@ -170,8 +179,9 @@ func (h ocisHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn ne
|
||||
if err != nil {
|
||||
h.log.Error().
|
||||
Err(err).
|
||||
Str("handler", "ocis").
|
||||
Str("binddn", bindDN).
|
||||
Str("basedn", h.cfg.Backend.BaseDN).
|
||||
Str("basedn", h.basedn).
|
||||
Str("filter", searchReq.Filter).
|
||||
Str("query", query).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
@@ -179,7 +189,7 @@ func (h ocisHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn ne
|
||||
|
||||
return ldap.ServerSearchResult{
|
||||
ResultCode: ldap.LDAPResultOperationsError,
|
||||
}, errors.New("search error: error listing users")
|
||||
}, fmt.Errorf("search error: error listing users")
|
||||
}
|
||||
entries = append(entries, h.mapAccounts(accounts.Accounts)...)
|
||||
case groupsQuery:
|
||||
@@ -189,8 +199,9 @@ func (h ocisHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn ne
|
||||
if err != nil {
|
||||
h.log.Error().
|
||||
Err(err).
|
||||
Str("handler", "ocis").
|
||||
Str("binddn", bindDN).
|
||||
Str("basedn", h.cfg.Backend.BaseDN).
|
||||
Str("basedn", h.basedn).
|
||||
Str("filter", searchReq.Filter).
|
||||
Str("query", query).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
@@ -198,15 +209,17 @@ func (h ocisHandler) Search(bindDN string, searchReq ldap.SearchRequest, conn ne
|
||||
|
||||
return ldap.ServerSearchResult{
|
||||
ResultCode: ldap.LDAPResultOperationsError,
|
||||
}, errors.New("search error: error listing groups")
|
||||
}, fmt.Errorf("search error: error listing groups")
|
||||
}
|
||||
entries = append(entries, h.mapGroups(groups.Groups)...)
|
||||
}
|
||||
|
||||
stats.Frontend.Add("search_successes", 1)
|
||||
h.log.Debug().
|
||||
Str("handler", "ocis").
|
||||
Int("num_entries", len(entries)).
|
||||
Str("binddn", bindDN).
|
||||
Str("basedn", h.cfg.Backend.BaseDN).
|
||||
Str("basedn", h.basedn).
|
||||
Str("filter", searchReq.Filter).
|
||||
Interface("src", conn.RemoteAddr()).
|
||||
Msg("AP: Search OK")
|
||||
@@ -254,11 +267,11 @@ func (h ocisHandler) mapAccounts(accounts []*accounts.Account) []*ldap.Entry {
|
||||
}
|
||||
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s,%s",
|
||||
h.cfg.Backend.NameFormat,
|
||||
h.nameFormat,
|
||||
accounts[i].PreferredName,
|
||||
h.cfg.Backend.GroupFormat,
|
||||
h.groupFormat,
|
||||
"users",
|
||||
h.cfg.Backend.BaseDN,
|
||||
h.basedn,
|
||||
)
|
||||
entries = append(entries, &ldap.Entry{DN: dn, Attributes: attrs})
|
||||
}
|
||||
@@ -284,11 +297,11 @@ func (h ocisHandler) mapGroups(groups []*accounts.Group) []*ldap.Entry {
|
||||
}
|
||||
|
||||
dn := fmt.Sprintf("%s=%s,%s=%s,%s",
|
||||
h.cfg.Backend.NameFormat,
|
||||
h.nameFormat,
|
||||
groups[i].OnPremisesSamAccountName,
|
||||
h.cfg.Backend.GroupFormat,
|
||||
h.groupFormat,
|
||||
"groups",
|
||||
h.cfg.Backend.BaseDN,
|
||||
h.basedn,
|
||||
)
|
||||
|
||||
memberUids := make([]string, len(groups[i].Members))
|
||||
@@ -318,9 +331,20 @@ func parseFilter(f *ber.Packet) (queryType, string, ldap.LDAPResultCode, error)
|
||||
var code ldap.LDAPResultCode
|
||||
var err error
|
||||
switch ldap.FilterMap[f.Tag] {
|
||||
case "Present":
|
||||
if len(f.Children) != 0 {
|
||||
return "", "", ldap.LDAPResultOperationsError, fmt.Errorf("equality match must have no children, got %+v", f)
|
||||
}
|
||||
attribute := strings.ToLower(f.Data.String())
|
||||
|
||||
if attribute == "objectclass" {
|
||||
// TODO implement proper present odata query, for now fall back to listing users
|
||||
return "users", q, code, err
|
||||
}
|
||||
return qtype, q, ldap.LDAPResultUnwillingToPerform, fmt.Errorf("%s filter match for %s not implemented", ldap.FilterMap[f.Tag], attribute)
|
||||
case "Equality Match":
|
||||
if len(f.Children) != 2 {
|
||||
return "", "", ldap.LDAPResultOperationsError, errors.New("equality match must have exactly two children")
|
||||
return "", "", ldap.LDAPResultOperationsError, fmt.Errorf("equality match must have exactly two children")
|
||||
}
|
||||
attribute := strings.ToLower(f.Children[0].Value.(string))
|
||||
value := f.Children[1].Value.(string)
|
||||
@@ -333,6 +357,9 @@ func parseFilter(f *ber.Packet) (queryType, string, ldap.LDAPResultCode, error)
|
||||
qtype = usersQuery
|
||||
case "posixgroup", "groups":
|
||||
qtype = groupsQuery
|
||||
case "*":
|
||||
// TODO not implemented yet
|
||||
qtype = usersQuery
|
||||
default:
|
||||
qtype = ""
|
||||
}
|
||||
@@ -367,7 +394,7 @@ func parseFilter(f *ber.Packet) (queryType, string, ldap.LDAPResultCode, error)
|
||||
return qtype, q, code, err
|
||||
case "Substrings":
|
||||
if len(f.Children) != 2 {
|
||||
return "", "", ldap.LDAPResultOperationsError, errors.New("substrings filter must have exactly two children")
|
||||
return "", "", ldap.LDAPResultOperationsError, fmt.Errorf("substrings filter must have exactly two children")
|
||||
}
|
||||
attribute := strings.ToLower(f.Children[0].Value.(string))
|
||||
if len(f.Children[1].Children) != 1 {
|
||||
@@ -424,7 +451,7 @@ func parseFilter(f *ber.Packet) (queryType, string, ldap.LDAPResultCode, error)
|
||||
return qtype, strings.Join(subQueries, " "+strings.ToLower(ldap.FilterMap[f.Tag])+" "), ldap.LDAPResultSuccess, nil
|
||||
case "Not":
|
||||
if len(f.Children) != 1 {
|
||||
return "", "", ldap.LDAPResultOperationsError, errors.New("not filter match must have exactly one child")
|
||||
return "", "", ldap.LDAPResultOperationsError, fmt.Errorf("not filter match must have exactly one child")
|
||||
}
|
||||
qtype, subQuery, code, err := parseFilter(f.Children[0])
|
||||
if err != nil {
|
||||
@@ -448,15 +475,32 @@ func (h ocisHandler) Close(boundDN string, conn net.Conn) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add is not yet supported for the ocis backend
|
||||
func (h ocisHandler) 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 ocis backend
|
||||
func (h ocisHandler) 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 ocis backend
|
||||
func (h ocisHandler) Delete(boundDN string, deleteDN string, conn net.Conn) (result ldap.LDAPResultCode, err error) {
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
|
||||
// NewOCISHandler implements a glauth backend with ocis-accounts as the datasource
|
||||
func NewOCISHandler(opts ...Option) handler.Handler {
|
||||
options := newOptions(opts...)
|
||||
|
||||
handler := ocisHandler{
|
||||
log: options.Logger,
|
||||
cfg: options.Config,
|
||||
as: options.AccountsService,
|
||||
gs: options.GroupsService,
|
||||
log: options.Logger,
|
||||
as: options.AccountsService,
|
||||
gs: options.GroupsService,
|
||||
basedn: options.BaseDN,
|
||||
nameFormat: options.NameFormat,
|
||||
groupFormat: options.GroupFormat,
|
||||
}
|
||||
return handler
|
||||
}
|
||||
@@ -15,7 +15,13 @@ type Option func(o *Options)
|
||||
type Options struct {
|
||||
Logger log.Logger
|
||||
Context context.Context
|
||||
Config *config.Config
|
||||
LDAP *config.LDAP
|
||||
LDAPS *config.LDAPS
|
||||
Backend *config.Config
|
||||
Fallback *config.Config
|
||||
BaseDN string
|
||||
NameFormat string
|
||||
GroupFormat string
|
||||
AccountsService accounts.AccountsService
|
||||
GroupsService accounts.GroupsService
|
||||
}
|
||||
@@ -45,10 +51,52 @@ func Context(val context.Context) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.Config) Option {
|
||||
// LDAP provides a function to set the LDAP option.
|
||||
func LDAP(val *config.LDAP) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
o.LDAP = val
|
||||
}
|
||||
}
|
||||
|
||||
// LDAPS provides a function to set the LDAPS option.
|
||||
func LDAPS(val *config.LDAPS) Option {
|
||||
return func(o *Options) {
|
||||
o.LDAPS = val
|
||||
}
|
||||
}
|
||||
|
||||
// Backend provides a function to set the backend option.
|
||||
func Backend(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Backend = val
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback provides a strring to set the fallback option.
|
||||
func Fallback(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Fallback = val
|
||||
}
|
||||
}
|
||||
|
||||
// BaseDN provides a strring to set the BaseDN option.
|
||||
func BaseDN(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.BaseDN = val
|
||||
}
|
||||
}
|
||||
|
||||
// NameFormat provides a strring to set the NameFormat option.
|
||||
func NameFormat(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.NameFormat = val
|
||||
}
|
||||
}
|
||||
|
||||
// GroupFormat provides a strring to set the GroupFormat option.
|
||||
func GroupFormat(val string) Option {
|
||||
return func(o *Options) {
|
||||
o.GroupFormat = val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,10 @@ import (
|
||||
// LdapSvc holds the ldap server struct
|
||||
type LdapSvc struct {
|
||||
log logr.Logger
|
||||
c *config.Config
|
||||
ldap *config.LDAP
|
||||
ldaps *config.LDAPS
|
||||
backend *config.Config
|
||||
fallback *config.Config
|
||||
yubiAuth *yubigo.YubiAuth
|
||||
l *ldap.Server
|
||||
}
|
||||
@@ -26,14 +29,17 @@ func Server(opts ...Option) (*LdapSvc, error) {
|
||||
options := newOptions(opts...)
|
||||
|
||||
s := LdapSvc{
|
||||
log: mlogr.New(&options.Logger),
|
||||
c: options.Config,
|
||||
log: mlogr.New(&options.Logger),
|
||||
backend: options.Backend,
|
||||
fallback: options.Fallback,
|
||||
ldap: options.LDAP,
|
||||
ldaps: options.LDAPS,
|
||||
}
|
||||
|
||||
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 len(s.backend.YubikeyClientID) > 0 && len(s.backend.YubikeySecret) > 0 {
|
||||
s.yubiAuth, err = yubigo.NewYubiAuth(s.backend.YubikeyClientID, s.backend.YubikeySecret)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.New("yubikey auth failed")
|
||||
@@ -43,58 +49,101 @@ func Server(opts ...Option) (*LdapSvc, error) {
|
||||
// configure the backend
|
||||
s.l = ldap.NewServer()
|
||||
s.l.EnforceLDAP = true
|
||||
var h handler.Handler
|
||||
switch s.c.Backend.Datastore {
|
||||
var bh handler.Handler
|
||||
|
||||
switch s.backend.Backend.Datastore {
|
||||
/* TODO bring back file config
|
||||
case "config":
|
||||
h = handler.NewConfigHandler(
|
||||
bh = handler.NewConfigHandler(
|
||||
handler.Logger(s.log),
|
||||
handler.Config(s.c),
|
||||
handler.YubiAuth(s.yubiAuth),
|
||||
)
|
||||
*/
|
||||
case "ldap":
|
||||
h = handler.NewLdapHandler(
|
||||
bh = handler.NewLdapHandler(
|
||||
handler.Logger(s.log),
|
||||
handler.Config(s.c),
|
||||
handler.Config(s.backend),
|
||||
)
|
||||
case "owncloud":
|
||||
h = handler.NewOwnCloudHandler(
|
||||
bh = handler.NewOwnCloudHandler(
|
||||
handler.Logger(s.log),
|
||||
handler.Config(s.c),
|
||||
handler.Config(s.backend),
|
||||
)
|
||||
case "accounts":
|
||||
h = NewOCISHandler(
|
||||
bh = NewOCISHandler(
|
||||
AccountsService(options.AccountsService),
|
||||
GroupsService(options.GroupsService),
|
||||
Logger(options.Logger),
|
||||
Config(s.c),
|
||||
BaseDN(s.backend.Backend.BaseDN),
|
||||
NameFormat(s.backend.Backend.NameFormat),
|
||||
GroupFormat(s.backend.Backend.GroupFormat),
|
||||
)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported backend %s - must be 'ldap', 'owncloud' or 'accounts'", s.c.Backend.Datastore)
|
||||
//return nil, fmt.Errorf("unsupported backend %s - must be 'config', 'homed', 'ldap', 'owncloud' or 'accounts'", s.c.Backend.Datastore)
|
||||
return nil, fmt.Errorf("unsupported backend %s - must be 'ldap', 'owncloud' or 'accounts'", s.backend.Backend.Datastore)
|
||||
}
|
||||
s.log.V(3).Info("Using backend", "datastore", s.c.Backend.Datastore)
|
||||
s.l.BindFunc(s.c.Backend.BaseDN, h)
|
||||
s.l.SearchFunc(s.c.Backend.BaseDN, h)
|
||||
s.l.CloseFunc(s.c.Backend.BaseDN, h)
|
||||
s.log.V(3).Info("Using backend", "backend", s.backend.Backend)
|
||||
|
||||
if s.fallback != nil && s.fallback.Backend.Datastore != "" {
|
||||
|
||||
var fh handler.Handler
|
||||
|
||||
switch s.fallback.Backend.Datastore {
|
||||
/* TODO bring back file config
|
||||
case "config":
|
||||
fh = handler.NewConfigHandler(
|
||||
handler.Logger(s.log),
|
||||
handler.Config(s.c),
|
||||
handler.YubiAuth(s.yubiAuth),
|
||||
)
|
||||
*/
|
||||
case "ldap":
|
||||
fh = handler.NewLdapHandler(
|
||||
handler.Logger(s.log),
|
||||
handler.Config(s.fallback),
|
||||
)
|
||||
case "owncloud":
|
||||
fh = handler.NewOwnCloudHandler(
|
||||
handler.Logger(s.log),
|
||||
handler.Config(s.fallback),
|
||||
)
|
||||
case "accounts":
|
||||
fh = NewOCISHandler(
|
||||
AccountsService(options.AccountsService),
|
||||
GroupsService(options.GroupsService),
|
||||
Logger(options.Logger),
|
||||
BaseDN(s.fallback.Backend.BaseDN),
|
||||
NameFormat(s.fallback.Backend.NameFormat),
|
||||
GroupFormat(s.fallback.Backend.GroupFormat),
|
||||
)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported fallback %s - must be 'ldap', 'owncloud' or 'accounts'", s.fallback.Backend.Datastore)
|
||||
}
|
||||
s.log.V(3).Info("Using fallback", "backend", s.fallback.Backend)
|
||||
|
||||
bh = NewChainHandler(options.Logger, bh, fh)
|
||||
}
|
||||
|
||||
s.l.BindFunc(s.backend.Backend.BaseDN, bh)
|
||||
s.l.SearchFunc(s.backend.Backend.BaseDN, bh)
|
||||
s.l.CloseFunc(s.backend.Backend.BaseDN, bh)
|
||||
|
||||
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)
|
||||
s.log.V(3).Info("LDAP server listening", "address", s.ldap.Listen)
|
||||
return s.l.ListenAndServe(s.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)
|
||||
s.log.V(3).Info("LDAPS server listening", "address", s.ldaps.Listen)
|
||||
return s.l.ListenAndServeTLS(
|
||||
s.c.LDAPS.Listen,
|
||||
s.c.LDAPS.Cert,
|
||||
s.c.LDAPS.Key,
|
||||
s.ldaps.Listen,
|
||||
s.ldaps.Cert,
|
||||
s.ldaps.Key,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
10
ocis/go.mod
10
ocis/go.mod
@@ -5,18 +5,18 @@ go 1.13
|
||||
require (
|
||||
contrib.go.opencensus.io/exporter/jaeger v0.2.1
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.7.0
|
||||
contrib.go.opencensus.io/exporter/zipkin v0.1.1
|
||||
contrib.go.opencensus.io/exporter/zipkin v0.1.2
|
||||
github.com/UnnoTed/fileb0x v1.1.4
|
||||
github.com/go-test/deep v1.0.6 // indirect
|
||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
|
||||
github.com/micro/cli/v2 v2.1.2
|
||||
github.com/micro/micro/v2 v2.8.0
|
||||
github.com/openzipkin/zipkin-go v0.2.2
|
||||
github.com/openzipkin/zipkin-go v0.2.5
|
||||
github.com/owncloud/flaex v0.2.0
|
||||
github.com/owncloud/ocis-graph v0.0.0-20200318175820-9a5a6e029db7
|
||||
github.com/owncloud/ocis-graph-explorer v0.0.0-20200210111049-017eeb40dc0c
|
||||
github.com/owncloud/ocis-hello v0.1.0-alpha1.0.20200828085053-37fcf3c8f853
|
||||
github.com/owncloud/ocis-migration v0.2.0 // indirect
|
||||
github.com/owncloud/ocis-proxy v0.7.1-0.20200907105449-201b9a652685 // indirect
|
||||
github.com/owncloud/ocis/accounts v0.4.2-0.20200901074457-6a27781a2741
|
||||
github.com/owncloud/ocis/accounts v0.5.2
|
||||
github.com/owncloud/ocis/glauth v0.0.0-00010101000000-000000000000
|
||||
github.com/owncloud/ocis/konnectd v0.0.0-00010101000000-000000000000
|
||||
github.com/owncloud/ocis/ocis-phoenix v0.0.0-00010101000000-000000000000
|
||||
|
||||
429
ocis/go.sum
429
ocis/go.sum
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,10 @@ func GLAuthCommand(cfg *config.Config) *cli.Command {
|
||||
Category: "Extensions",
|
||||
Flags: flagset.ServerWithConfig(cfg.GLAuth),
|
||||
Action: func(c *cli.Context) error {
|
||||
|
||||
cfg.GLAuth.Backend.Servers = c.StringSlice("backend-server")
|
||||
cfg.GLAuth.Fallback.Servers = c.StringSlice("fallback-server")
|
||||
|
||||
scfg := configureGLAuth(cfg)
|
||||
|
||||
return cli.HandleAction(
|
||||
|
||||
Reference in New Issue
Block a user