{"id":19341730,"url":"https://github.com/stephane-martin/bouncer","last_synced_at":"2025-04-23T03:32:02.112Z","repository":{"id":57605433,"uuid":"89038065","full_name":"stephane-martin/bouncer","owner":"stephane-martin","description":"Authentication with Nginx and LDAP backend","archived":false,"fork":false,"pushed_at":"2017-05-14T15:44:52.000Z","size":1825,"stargazers_count":5,"open_issues_count":4,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-02T07:11:31.983Z","etag":null,"topics":["authentication","consul","golang","ldap","linux","nginx","service-discovery"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stephane-martin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-22T02:12:49.000Z","updated_at":"2022-05-02T08:25:10.000Z","dependencies_parsed_at":"2022-08-28T02:24:52.496Z","dependency_job_id":null,"html_url":"https://github.com/stephane-martin/bouncer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stephane-martin%2Fbouncer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stephane-martin%2Fbouncer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stephane-martin%2Fbouncer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stephane-martin%2Fbouncer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stephane-martin","download_url":"https://codeload.github.com/stephane-martin/bouncer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250365744,"owners_count":21418741,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","consul","golang","ldap","linux","nginx","service-discovery"],"created_at":"2024-11-10T03:32:16.971Z","updated_at":"2025-04-23T03:32:00.092Z","avatar_url":"https://github.com/stephane-martin.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Motivation\n\nNginx does not provide natively LDAP authentication. But it provides a generic\nauthentication module, that performs HTTP requests to a backend service to check if \na user is allowed to access the ressource\n(see [ngx_http_auth_request_module](http://nginx.org/en/docs/http/ngx_http_auth_request_module.html)).\n\n`bouncer` provides such a backend.\n\n# Features\n\n## Consul integration\n\n- `bouncer` configuration can be defined in Consul KV\n- if your LDAP directories are services registered in Consul, `bouncer` can find them\n- `bouncer` can register itself as a Consul service\n\n## LDAP authentication\n\n`bouncer` can authenticate users based on a LDAP directory. Two authentication\nschemes are supported: \"direct LDAP bind\" or \"LDAP search and bind\".\n\nMultiple LDAP directories can be defined. In that case, `bouncer` will\nload-balance the LDAP requests among them.\n\nOn the front-end side, the user credentials can be given as \"HTTP Basic\"\nauthentication headers, or via a classical form/session cookie.\n\n## Backend services\n\nYour backend services don't have to deal with authentication anymore. Instead\nthey rely on nginx and `bouncer` to perform the authentication separately.\nThe services are given information about the current user by HTTP headers.\n\n## Check-health and statistics\n\nFor reliable monitoring, `bouncer` provides a simple health-check and some basic statistics.\n\n## Logging\n\n- logs are structured (using logrus), in a text or JSON format\n- logs and request logs are clearly separated\n- logs can be sent to syslog\n\n# Install\n\n## Prerequisites\n\n- Nginx, compiled with the `ngx_http_auth_request_module` module.\n- LDAP server(s)\n- Redis is needed for some features (statistics, watching request flow)\n- Linux (should work on other UNIX too)\n- Only compiles on Go \u003e= 1.8\n\n## Get it\n\n`go get -u github.com/stephane-martin/bouncer`\n\nThe dependencies are vendored.\n\n# Configuration\n\n## Configuration sources\n\n### File\n\n**See [the configuration example](https://github.com/stephane-martin/bouncer/blob/master/bouncer.example.toml)**.\n\nThe configuration file has to be `bouncer.toml`. It is looked for in `/etc` and\nin the directory specified by the commandline flag `--config=XXX`.\n\n### Environment variables\n\nIt is also possible to configure `bouncer` through environment variables.\nSee the function in [envmapping.go](https://github.com/stephane-martin/bouncer/blob/master/conf/envmapping.go)\nfor the mappings.\n\n```\nNAL_CACHE_EXPIRES=\"3M\" NAL_REALM=\"My Realm\" ... bouncer serve\n```\n\n### Consul\n\nThe configuration can be stored in the key-value part of Consul. Put the parameters under\nthe `bouncer/conf/` prefix in Consul KV, and run `bouncer` with\nthe `--consul` flag.\n\nFor example:\n\n```\nconsul kv put -http-addr=127.0.0.1:8500 bouncer/conf/cache/expires 3m\nconsul kv put -http-addr=127.0.0.1:8500 bouncer/conf/http/realm 'My Realm'\nbouncer serve --consul=http://127.0.0.1:8500\n```\n\n## Configuring the LDAP servers\n\nMultiple LDAP directories can be configured, for example one master and some\nslaves. In that case the LDAP requests used to authenticate users will be\nrandomly load-balanced through the directories. Moreover, if one LDAP directory\nis not responding, then another will be tried.\n\nConfiguring the LDAP directories has two steps :\n- configure the defaults parameters that would apply to all directories\n- configure the specific paramaters (mostly the `host`) for each directory\n\n### In the configuration file\n\nThe default parameters are inside the `[defaultldap]` section. Here you define\nthe LDAP parameters than you want to share among all the directories.\n\nEach individual LDAP directory must then be defined in an additional `[[ldap]]`\nsection.\n\nbouncer does not reload automatically when the configuration file is\nmodified. You need to send a SIGHUP to the process.\n\n### In Consul KV\n\nIn Consul KV you define the default LDAP parameters under the prefix `bouncer/conf/defaultldap`.\n\nThe individual LDAP servers can be defined under `bouncer/ldap/[ID]/`,\nwhere `[ID]` is a meaningless identifier.\n\nbouncer automatically reloads when the configuration items in Consul KV\nare modified.\n\n```\nconsul kv put -http-addr=127.0.0.1:8500 bouncer/conf/defaultldap/port 389\n...\n\nconsul kv put -http-addr=127.0.0.1:8500 bouncer/ldap/1/host 127.0.0.1\n\nconsul kv put -http-addr=127.0.0.1:8500 bouncer/ldap/2/host 10.1.1.1\nconsul kv put -http-addr=127.0.0.1:8500 bouncer/ldap/2/port 636\nconsul kv put -http-addr=127.0.0.1:8500 bouncer/ldap/2/tls_type tls\nconsul kv put -http-addr=127.0.0.1:8500 bouncer/ldap/2/insecure true\n```\n\n### By Consul discovery\n\nIf your LDAP servers are registered in Consul as service with health\nchecks, you don't need to (statically) define the LDAP servers in bouncer\nconfiguration. Instead, just tell bouncer where to find the LDAP\nservices. bouncer will try the discovery if you provide the\n`--ldap-service-name` commandline option. Only LDAP servers that are registered\nin Consul with a passing health-check will be discovered.\n\n```\nbouncer serve --consul=http://127.0.0.1:8500 --ldap-service-name slapd --ldap-datacenter ldapdc\n```\n\nThis means: look for services called `slapd`, that's defined in the `ldapdc`\nConsul datacenter. The `slapd` hosts and ports, discovered in Consul, completed by\n`defaultldap` parameters, will be used as LDAP servers to perform the \nauthentication.\n\nIt is also possible to filter the discovered LDAP services by a Consul tag with `--ldap-tag`.\n\nTo print the currently discovered and visible LDAP servers from Consul:\n\n```\nbouncer discovered --consul=http://127.0.0.1:8500 --ldap-service-name slapd --ldap-datacenter ldapdc\n```\n\nThe discovery is dynamic: LDAP servers will be added/removed to the list when\ntheir Concul health-checks succeed/fail.\n\n## Check configuration\n\nTo check bouncer configuration, use the `print-config` command:\n\n```\n# Without consul\nbouncer print-config\n\n# With consul (merge configuration from file and Consul KV)\nbouncer print-config --consul=http://127.0.0.1:8500\n\n# Also print the current discovered LDAP directories:\nbouncer print-config --consul=http://127.0.0.1:8500 --discover --ldap-service-name slapd --ldap-datacenter ldapdc\n```\n\n# Main commands\n\n## Running\n\n### Launching `bouncer`\n\n`bouncer` listens on two ports: one for the main Auth service, the other\none for the API service. The ports are configurable.\n\nTo start listening, use `bouncer serve`.\n\n### Logging\n\nThere are two kinds of logs: the 'normal logs', that trace `bouncer`\nactivity, and the 'request logs', that trace the received requests and their\nresults.\n\nBy default, the normals logs are written to `stderr`, and the request logs to\n`stdout`.\n\nIf the `--syslog` flag is provided, they will both be sent to the\nlocal syslog daemon. (They use different syslog tags).\n\nTo write the normal logs to a file, use `--logfile=XXX`. Similarly use\n`--req-logfile=YYY` for the request logs.\n\nLogs are written in text format by default. Use the `--json` flag to write them\nas JSON instead.\n\nLog verbosity can be set for the normal logs and the request logs respectively\nwith the `--loglevel` and `--req-loglevel` flags. Successful authentications\nwill only be logged in the request logs at level 'DEBUG'.\n\n### Redis and the request logs\n\nIf Redis is enabled in configuration, the request logs:\n\n-   will be written to some Redis sorted sets. (We can calculate some metrics from\nRedis afterwards).\n-   will be erased from Redis after some configurable period.\n-   will be published as a Redis PUBSUB. (We can synchronously expose the request\nlogs this way).\n\n### Registering in Consul\n\n`bouncer` can register itself as a Consul service:\n`bouncer serve --consul=XXX --register`.\n\n## Stopping\n\nSend SIGTERM or SIGINT to the `bouncer` process.\n\n## Reload the configuration file\n\nSend SIGHUP to the `bouncer` process.\n\n## Watch the flow of requests\n\nIf Redis in enabled, you can watch the request logs as they are generated with\nthe `monitor` command. For example, try:\n\n```\nbouncer serve --req-loglevel=DEBUG\n```\n\nThen in another terminal:\n\n```\nbouncer monitor --json\n```\n\n# HTTP endpoints\n\n## Auth service\n\nThe Auth service listens on address/port defined by configuration parameters\n`http.bind_addr` and `http.port`.\n\nIt provides the following endpoints:\n\n-   `/nginx`: where Nginx should send the authentication subrequests.\n-   `/auth`: authentication through HTTP POST: in the incoming request,\n    `username` and `password` should be set as HTTP `application/x-www-form-urlencoded`\n    parameters. Returns 200, 401, 403... like the /nginx endpoint.\n-   `/health`: simple health-check endpoint. Returns 200 if everything looks OK.\n\n## API service\n\nThe API service listens on address/port defined by configuration parameters\n`api.bind_addr` and `api.port`.\n\nIt provides the following endpoints:\n\n-   `/status`: returns 200 and a dummy message if `bouncer` is running\n-   `/health`: simple health-check endpoint. Returns 200 if everything looks OK.\n-   `/conf`: returns the current configuration\n-   `/reload`: POST there to trigger a configuration reload\n-   `/stats`: if Redis is enabled, some metrics are returned in a JSON format.\n-   `/events`: if Redis is enabled, the request logs are posted there as Server\n    Side Events.\n\n# How to get user information in the backend services\n\nThe backend services that are protected by bouncer get information\nabout the authenticated user through the following ways:\n\n-   The `Authorization` HTTP header is passed. Optionaly, the user password\n    can be masked in that header if `http.mask_password` is true.\n-   The username is passed in the `X-REMOTE-USER` HTTP header.\n-   If a RSA private key is defined in `bouncer` configuration, a signed\n    JWT token is passed in the `X-REMOTE-JWT` header.\n\nTo define a RSA private key, provide the path to a PEM-encoded file in\n`signature.private_key_path`, or directly the PEM-encoded key in\n`signature.private_key_content`.\n\n(To generate such a private key, you can use `bouncer generate-rsa-keys`.)\n\n# HTTP Basic Authentication: Nginx configuration example\n\nAdapt it to your needs.\n\n\n```nginx\nserver {\n    location /backend_service_to_protect {\n        auth_request /nginx;\n\n        # gather info from HTTP response headers\n        auth_request_set $remote $upstream_http_x_remote_user;\n        auth_request_set $auth $upstream_http_authorization;\n        auth_request_set $backendjwt $upstream_http_x_remote_jwt;\n\n        # pass info to the backend service\n        proxy_set_header REMOTE_USER $remote;\n        proxy_set_header REMOTE-USER $remote;\n        proxy_set_header X-REMOTE-USER $remote;\n        proxy_set_header X-REMOTE-JWT $backendjwt;\n        proxy_set_header Authorization $auth;\n\n    }\n\n    location = /nginx {\n        internal;\n        proxy_pass http://BOUNCER_HOST:BOUNCER_PORT;\n        proxy_pass_request_body off;\n        proxy_set_header Content-Length \"\";\n\n        # pass some information to bouncer about the incoming request\n        # (useful for the request logs)\n        proxy_set_header X-Forwarded-Server $http_host;\n        proxy_set_header X-Forwarded-Host $http_host:443;\n        proxy_set_header X-Original-URI $request_uri;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header Forwarded \"for=$remote_addr; proto=https\";\n        proxy_set_header X-Forwarded-Port 443;\n        proxy_set_header X-Forwarded-Proto https;\n    }\n}\n```\n\n# Cookie-based Authentication: Nginx configuration example\n\n```nginx\nserver {\n    listen 4343 ssl http2;\n    server_name myapp.example.org;\n\n    ...\n\n    location / {\n        error_page 401 =200 /nal-login-page;\n        auth_request /nginx;\n\n        # gather info from HTTP response headers\n        auth_request_set $remote $upstream_http_x_remote_user;\n        auth_request_set $auth $upstream_http_authorization;\n        auth_request_set $backendjwt $upstream_http_x_remote_jwt;\n\n        # pass info to the backend service\n        proxy_set_header REMOTE_USER $remote;\n        proxy_set_header REMOTE-USER $remote;\n        proxy_set_header X-REMOTE-USER $remote;\n        proxy_set_header X-REMOTE-JWT $backendjwt;\n        proxy_set_header Authorization $auth;\n    }\n\n    location /login {\n        proxy_pass http://BOUNCER_HOST:BOUNCER_PORT/nal-login-page;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto https;\n        proxy_set_header X-Forwarded-Host $http_host:443;\n        proxy_set_header X-Forwarded-Server $http_host;\n        proxy_set_header X-Forwarded-Port 443;\n        proxy_set_header Forwarded \"for=$remote_addr; proto=https\";\n        proxy_set_header X-Scheme https;\n        proxy_set_header X-Forwarded-Ssl on;\n        proxy_set_header X-Url-Scheme https;\n        proxy_set_header X-Original-Uri $request_uri;\n        proxy_set_header X-Login-Uri $uri;\n    }\n\n    location /logout {\n        proxy_pass http://BOUNCER_HOST:BOUNCER_PORT/nal-logout-page;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto https;\n        proxy_set_header X-Forwarded-Host $http_host:443;\n        proxy_set_header X-Forwarded-Server $http_host;\n        proxy_set_header X-Forwarded-Port 443;\n        proxy_set_header Forwarded \"for=$remote_addr; proto=https\";\n        proxy_set_header X-Scheme https;\n        proxy_set_header X-Forwarded-Ssl on;\n        proxy_set_header X-Url-Scheme https;\n        proxy_set_header X-Original-Uri $request_uri;\n    }\n\n    location = /nginx {\n        internal;\n        proxy_pass http://BOUNCER_HOST:BOUNCER_PORT;\n        proxy_pass_request_body off;\n        proxy_set_header Content-Length \"\";\n        proxy_set_header X-Forwarded-Server $http_host;\n        proxy_set_header X-Forwarded-Host $http_host:443;\n        proxy_set_header X-Original-URI $request_uri;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header Forwarded \"for=$remote_addr; proto=https\";\n        proxy_set_header X-Forwarded-Port 443;\n        proxy_set_header X-Forwarded-Proto https;\n    }\n\n\n}\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstephane-martin%2Fbouncer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstephane-martin%2Fbouncer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstephane-martin%2Fbouncer/lists"}