First commit
This commit is contained in:
51
Dockerfile
Normal file
51
Dockerfile
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# 1) Install Supervisor, slapd, ldap-utils, Apache2+PHP, wget, unzip, envsubst
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
supervisor \
|
||||||
|
slapd ldap-utils \
|
||||||
|
wget unzip gnupg \
|
||||||
|
apache2 libapache2-mod-php php php-ldap php-mbstring php-xml \
|
||||||
|
gettext-base \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# 2) Download and install Hydra binary (v1.16.4 as example)
|
||||||
|
RUN HYDRA_VERSION=v1.16.4 && \
|
||||||
|
wget -qO /tmp/hydra.tar.gz \
|
||||||
|
https://github.com/ory/hydra/releases/download/${HYDRA_VERSION}/hydra_${HYDRA_VERSION}_linux_amd64.tar.gz && \
|
||||||
|
mkdir -p /usr/local/bin/hydra_tmp && \
|
||||||
|
tar -xzf /tmp/hydra.tar.gz -C /usr/local/bin/hydra_tmp && \
|
||||||
|
mv /usr/local/bin/hydra_tmp/hydra /usr/local/bin/hydra && \
|
||||||
|
chmod +x /usr/local/bin/hydra && \
|
||||||
|
rm -rf /usr/local/bin/hydra_tmp /tmp/hydra.tar.gz
|
||||||
|
|
||||||
|
# 3) Install LAM (osixia/ldap-account-manager) under /var/www/lam
|
||||||
|
RUN LAM_VERSION=1.2.1 && \
|
||||||
|
wget -qO /tmp/lam.zip https://github.com/osixia/ldap-account-manager/releases/download/${LAM_VERSION}/ldap-account-manager_${LAM_VERSION}.zip && \
|
||||||
|
unzip /tmp/lam.zip -d /var/www/html && \
|
||||||
|
mv /var/www/html/ldap-account-manager /var/www/html/lam && \
|
||||||
|
rm /tmp/lam.zip
|
||||||
|
|
||||||
|
# 4) Enable Apache modules required by LAM
|
||||||
|
RUN a2enmod php8.2 ldap rewrite
|
||||||
|
|
||||||
|
# 5) Copy Supervisor config and entrypoint
|
||||||
|
COPY supervisord.conf /etc/supervisor/supervisord.conf
|
||||||
|
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||||
|
|
||||||
|
# 6) Copy all template files into /templates
|
||||||
|
COPY templates/ /templates/
|
||||||
|
|
||||||
|
# 7) Create empty folder for LAM config (will be filled at runtime)
|
||||||
|
RUN mkdir -p /var/www/lam/config
|
||||||
|
RUN chown -R www-data:www-data /var/www/lam
|
||||||
|
|
||||||
|
# 8) Expose ports
|
||||||
|
EXPOSE 389 636 4444 4445 80
|
||||||
|
|
||||||
|
# 9) At runtime, entrypoint.sh does all the envsubst + slapd reconfiguration + supervisord
|
||||||
|
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
180
README.md
Normal file
180
README.md
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
# Hydra-LDAP-LAM All-in-One Docker Image
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This repository contains all necessary configuration and templates to build a single Docker image that bundles and configures:
|
||||||
|
|
||||||
|
- **OpenLDAP** (slapd) as the LDAP directory.
|
||||||
|
- **Ory Hydra** (an OAuth 2.0 / OIDC server).
|
||||||
|
- **LDAP Account Manager (LAM)** under Apache2 + PHP as the web-based LDAP UI.
|
||||||
|
|
||||||
|
The container is orchestrated using **Supervisor** to launch and manage all three services. All credentials and configuration values are provided at runtime via environment variables, ensuring no sensitive data is baked into the image.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Repository Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
hydra-ldap-lam/
|
||||||
|
├─ Dockerfile
|
||||||
|
├─ entrypoint.sh
|
||||||
|
├─ supervisord.conf
|
||||||
|
└─ templates/
|
||||||
|
├─ bootstrap.ldif.tpl
|
||||||
|
├─ hydra-config.yaml.tpl
|
||||||
|
└─ lam.conf.php.tpl
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Dockerfile**: Builds the Docker image, installs dependencies, and copies template files.
|
||||||
|
- **entrypoint.sh**: Entry-point script that performs runtime configuration substitution (`envsubst`), bootstraps LDAP, and starts Supervisor.
|
||||||
|
- **supervisord.conf**: Supervisor configuration to run `slapd`, `hydra`, and Apache2 in the foreground.
|
||||||
|
- **templates/**:
|
||||||
|
- **bootstrap.ldif.tpl**: LDIF template for initializing the LDAP data (domain, organization, and a sample user).
|
||||||
|
- **hydra-config.yaml.tpl**: Hydra configuration template pointing to in-memory SQLite and defining login/consent URLs.
|
||||||
|
- **lam.conf.php.tpl**: LAM configuration template specifying LDAP bind settings.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- [Docker](https://docs.docker.com/get-docker/) (version 20.10+ recommended)
|
||||||
|
- (Optional) A local or remote Gitea instance for committing the `README.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Building the Image
|
||||||
|
|
||||||
|
1. Clone (or place) this directory locally:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <your-gitea-repo-url>
|
||||||
|
cd hydra-ldap-lam
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Build the Docker image:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t hydra-ldap-lam:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
This will produce an image named `hydra-ldap-lam:latest` containing:
|
||||||
|
|
||||||
|
- OpenLDAP (slapd)
|
||||||
|
- Ory Hydra binary
|
||||||
|
- Apache2 + PHP + LAM (LDAP Account Manager)
|
||||||
|
- Supervisor and supporting scripts
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Running the Container
|
||||||
|
|
||||||
|
To run the container, you must supply all required environment variables. Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d --name hydra_ldap_lam -e LDAP_DOMAIN="example.com" -e LDAP_ORGANISATION="Example Corp" -e LDAP_ADMIN_PASSWORD="adminpassword" -e LDAP_USER_PASSWORD="password123" -e LAM_BIND_DN="cn=admin,dc=example,dc=com" -e LAM_BIND_PASSWORD="adminpassword" -e HYDRA_SECRETS_SYSTEM="a-very-long-32+-character-secret" -e HYDRA_ISSUER_URL="http://127.0.0.1:4444" -p 389:389 -p 636:636 -p 4444:4444 -p 4445:4445 -p 80:80 hydra-ldap-lam:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
- `-p 389:389`: Exposes LDAP (OpenLDAP) on host port 389.
|
||||||
|
- `-p 636:636`: Exposes LDAPS (OpenLDAP over TLS) on host port 636 (if TLS is configured).
|
||||||
|
- `-p 4444:4444`: Exposes Hydra Public (OAuth2) endpoints.
|
||||||
|
- `-p 4445:4445`: Exposes Hydra Admin endpoints (client registration, introspection, etc.).
|
||||||
|
- `-p 80:80`: Exposes Apache + LAM (web UI).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
|--------------------------|------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `LDAP_DOMAIN` | LDAP base domain (e.g., `example.com`). Used to initialize LDAP DIT (`dc=example,dc=com`). |
|
||||||
|
| `LDAP_ORGANISATION` | Organization name (e.g., `"Example Corp"`). Populates LDAP `o` attribute. |
|
||||||
|
| `LDAP_ADMIN_PASSWORD` | Password for the LDAP admin user (`cn=admin,dc=${LDAP_DOMAIN}`). |
|
||||||
|
| `LDAP_USER_PASSWORD` | Password for the sample LDAP user (`uid=jdoe`). |
|
||||||
|
| `LAM_BIND_DN` | LDAP Bind DN for LAM (e.g., `cn=admin,dc=${LDAP_DOMAIN}`). |
|
||||||
|
| `LAM_BIND_PASSWORD` | Password for the above `LAM_BIND_DN`. |
|
||||||
|
| `HYDRA_SECRETS_SYSTEM` | A 32+ character random secret for Hydra’s HMAC and session encryption. |
|
||||||
|
| `HYDRA_ISSUER_URL` | The issuer URL for Hydra (e.g., `http://127.0.0.1:4444`). Hydra’s config will use this to set `urls.self.base`. |
|
||||||
|
|
||||||
|
> **Note**: For production, consider replacing `LDAP_USER_PASSWORD` with an SSHA hash in `bootstrap.ldif.tpl`, and configuring a proper database (e.g., Postgres) for Hydra instead of in-memory SQLite.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Service URLs and Testing
|
||||||
|
|
||||||
|
1. **LDAP (OpenLDAP)**
|
||||||
|
- Host: `ldap://localhost:389`
|
||||||
|
- Admin Bind DN: `cn=admin,dc=${LDAP_DOMAIN}`
|
||||||
|
- Example query:
|
||||||
|
```bash
|
||||||
|
ldapsearch -x -H ldap://localhost:389 -D "cn=admin,dc=example,dc=com" -w adminpassword -b "dc=example,dc=com" "(uid=jdoe)"
|
||||||
|
```
|
||||||
|
- Should return the entry for `cn=John Doe, uid=jdoe`.
|
||||||
|
|
||||||
|
2. **LDAP Account Manager (LAM)**
|
||||||
|
- URL: `http://localhost/lam`
|
||||||
|
- Login with:
|
||||||
|
- Bind DN: `cn=admin,dc=${LDAP_DOMAIN}`
|
||||||
|
- Password: `${LAM_BIND_PASSWORD}`
|
||||||
|
- Browse the DIT at `dc=${LDAP_DOMAIN} → ou=users → cn=John Doe`.
|
||||||
|
|
||||||
|
3. **Hydra Admin API**
|
||||||
|
- Base URL: `http://localhost:4445`
|
||||||
|
- Register a client (example):
|
||||||
|
```bash
|
||||||
|
docker exec -it hydra_ldap_lam hydra clients create --endpoint http://127.0.0.1:4445 --id my-client --secret my-secret --grant-types authorization_code,refresh_token --response-types code,id_token --scope openid,offline --callbacks http://localhost/callback
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Hydra OAuth2 Public Endpoints**
|
||||||
|
- Authorization endpoint:
|
||||||
|
```
|
||||||
|
http://localhost:4444/oauth2/auth?response_type=code&client_id=my-client &scope=openid&redirect_uri=http://localhost/callback&state=xyz
|
||||||
|
```
|
||||||
|
- Token endpoint (example):
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:4444/oauth2/token -u my-client:my-secret -d grant_type=authorization_code -d code=<AUTH_CODE> -d redirect_uri=http://localhost/callback
|
||||||
|
```
|
||||||
|
- Successful response returns `access_token`, `id_token`, etc.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Customization & Next Steps
|
||||||
|
|
||||||
|
- **Switch to a Persistent Database for Hydra**
|
||||||
|
Modify `templates/hydra-config.yaml.tpl` to use a `${HYDRA_DSN}` (e.g., `postgres://hydrabox:secret@postgres:5432/hydra?sslmode=disable`) and pass `-e HYDRA_DSN="your-database-url"` at runtime. Add a separate Postgres container or external DB.
|
||||||
|
|
||||||
|
- **Enable TLS for OpenLDAP**
|
||||||
|
- Generate or supply certificates and adjust `/etc/ldap/slapd.d/` configuration to enable LDAPS (`ldaps://`).
|
||||||
|
- Update `templates/bootstrap.ldif.tpl` or dynamically configure TLS.
|
||||||
|
|
||||||
|
- **Secure Apache (LAM)**
|
||||||
|
- Obtain a TLS certificate (e.g., via Let’s Encrypt) and configure Apache’s SSL vhost.
|
||||||
|
- Adjust `entrypoint.sh` and the Dockerfile to copy in certificates or mount them at runtime.
|
||||||
|
|
||||||
|
- **Multiple LDAP Users / Additional LDIFs**
|
||||||
|
- Add more `.tpl` LDIF files under `templates/` (e.g., `custom-groups.ldif.tpl`) and apply them in `entrypoint.sh`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
1. **Modify Templates**
|
||||||
|
- Update `templates/bootstrap.ldif.tpl` to add more objects (groups, additional users).
|
||||||
|
- Update `templates/lam.conf.php.tpl` for advanced LAM settings (database, theming, etc.).
|
||||||
|
- Update `templates/hydra-config.yaml.tpl` for production-ready features (HTTPS, introspection limits, etc.).
|
||||||
|
|
||||||
|
2. **Rebuild the Docker Image**
|
||||||
|
```bash
|
||||||
|
docker build -t hydra-ldap-lam:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Run with New Environment Variables**
|
||||||
|
```bash
|
||||||
|
docker run -d -e LDAP_DOMAIN="yourdomain.com" -e LDAP_ORGANISATION="Your Org" -e LDAP_ADMIN_PASSWORD="YourAdminPassword" -e LDAP_USER_PASSWORD="YourUserPassword" -e LAM_BIND_DN="cn=admin,dc=yourdomain,dc=com" -e LAM_BIND_PASSWORD="YourAdminPassword" -e HYDRA_SECRETS_SYSTEM="your-long-32+-char-secret" -e HYDRA_ISSUER_URL="https://your-hydra-host" -p 389:389 -p 636:636 -p 4444:4444 -p 4445:4445 -p 80:80 hydra-ldap-lam:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is provided “as-is” under the MIT License. See [LICENSE](LICENSE) for details.
|
||||||
|
|
41
entrypoint.sh
Normal file
41
entrypoint.sh
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 1) Ensure required env vars are set:
|
||||||
|
: "${LDAP_DOMAIN:?Need to set LDAP_DOMAIN (e.g. example.com)}"
|
||||||
|
: "${LDAP_ORGANISATION:?Need to set LDAP_ORGANISATION}"
|
||||||
|
: "${LDAP_ADMIN_PASSWORD:?Need to set LDAP_ADMIN_PASSWORD}"
|
||||||
|
: "${LDAP_USER_PASSWORD:?Need to set LDAP_USER_PASSWORD}"
|
||||||
|
: "${LAM_BIND_DN:?Need to set LAM_BIND_DN}"
|
||||||
|
: "${LAM_BIND_PASSWORD:?Need to set LAM_BIND_PASSWORD}"
|
||||||
|
: "${HYDRA_SECRETS_SYSTEM:?Need to set HYDRA_SECRETS_SYSTEM}"
|
||||||
|
: "${HYDRA_ISSUER_URL:?Need to set HYDRA_ISSUER_URL}"
|
||||||
|
|
||||||
|
# 2) Reconfigure slapd via debconf:
|
||||||
|
debconf-set-selections <<EOF
|
||||||
|
slapd slapd/no_configuration boolean false
|
||||||
|
slapd slapd/password1 password ${LDAP_ADMIN_PASSWORD}
|
||||||
|
slapd slapd/password2 password ${LDAP_ADMIN_PASSWORD}
|
||||||
|
slapd slapd/domain string ${LDAP_DOMAIN}
|
||||||
|
slapd shared/organization string "${LDAP_ORGANISATION}"
|
||||||
|
slapd slapd/backend select MDB
|
||||||
|
slapd slapd/purge_database boolean true
|
||||||
|
slapd slapd/move_old_database boolean true
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# (Re)configure slapd non-interactively:
|
||||||
|
DEBIAN_FRONTEND=noninteractive dpkg-reconfigure -f noninteractive slapd
|
||||||
|
|
||||||
|
# 3) Bootstrap the LDIF into slapd:
|
||||||
|
envsubst < /templates/bootstrap.ldif.tpl > /tmp/bootstrap.ldif
|
||||||
|
ldapadd -Y EXTERNAL -H ldapi:/// -D "cn=admin,dc=${LDAP_DOMAIN}" -w "${LDAP_ADMIN_PASSWORD}" -f /tmp/bootstrap.ldif || true
|
||||||
|
# (ignore “already exists” errors if rerunning)
|
||||||
|
|
||||||
|
# 4) Render Hydra’s config:
|
||||||
|
envsubst < /templates/hydra-config.yaml.tpl > /etc/hydra/config.yaml
|
||||||
|
|
||||||
|
# 5) Render LAM’s PHP config:
|
||||||
|
envsubst < /templates/lam.conf.php.tpl > /var/www/lam/config/lam.conf.php
|
||||||
|
|
||||||
|
# 6) Finally, launch supervisord (which starts slapd, hydra, and apache2):
|
||||||
|
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
26
supervisord.conf
Normal file
26
supervisord.conf
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
; supervisord.conf
|
||||||
|
[supervisord]
|
||||||
|
nodaemon=true
|
||||||
|
logfile=/var/log/supervisord.log
|
||||||
|
loglevel=info
|
||||||
|
|
||||||
|
[program:slapd]
|
||||||
|
command=/usr/sbin/slapd -h "ldap://0.0.0.0:389 ldaps://0.0.0.0:636" -u openldap -g openldap -d -1
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
stdout_logfile=/var/log/slapd.log
|
||||||
|
stderr_logfile=/var/log/slapd.err
|
||||||
|
|
||||||
|
[program:hydra]
|
||||||
|
command=/usr/local/bin/hydra serve all --config /etc/hydra/config.yaml
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
stdout_logfile=/var/log/hydra.log
|
||||||
|
stderr_logfile=/var/log/hydra.err
|
||||||
|
|
||||||
|
[program:apache2]
|
||||||
|
command=/usr/sbin/apache2ctl -D FOREGROUND
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
stdout_logfile=/var/log/apache2.log
|
||||||
|
stderr_logfile=/var/log/apache2.err
|
19
templates/bootstrap.ldif.tpl
Normal file
19
templates/bootstrap.ldif.tpl
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
dn: dc=${LDAP_DOMAIN}
|
||||||
|
objectClass: top
|
||||||
|
objectClass: dcObject
|
||||||
|
objectClass: organization
|
||||||
|
o: ${LDAP_ORGANISATION}
|
||||||
|
dc: ${LDAP_DOMAIN}
|
||||||
|
|
||||||
|
dn: ou=users,dc=${LDAP_DOMAIN}
|
||||||
|
objectClass: organizationalUnit
|
||||||
|
ou: users
|
||||||
|
|
||||||
|
dn: cn=John Doe,ou=users,dc=${LDAP_DOMAIN}
|
||||||
|
objectClass: inetOrgPerson
|
||||||
|
cn: John Doe
|
||||||
|
sn: Doe
|
||||||
|
givenName: John
|
||||||
|
uid: jdoe
|
||||||
|
mail: jdoe@${LDAP_DOMAIN}
|
||||||
|
userPassword: ${LDAP_USER_PASSWORD}
|
24
templates/hydra-config.yaml.tpl
Normal file
24
templates/hydra-config.yaml.tpl
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
serve:
|
||||||
|
public:
|
||||||
|
port: 4444
|
||||||
|
admin:
|
||||||
|
port: 4445
|
||||||
|
|
||||||
|
dsn: memory
|
||||||
|
|
||||||
|
log:
|
||||||
|
level: debug
|
||||||
|
|
||||||
|
oauth2:
|
||||||
|
expose_internal_errors: true
|
||||||
|
skip_consent_screen: false
|
||||||
|
|
||||||
|
urls:
|
||||||
|
self:
|
||||||
|
base: ${HYDRA_ISSUER_URL}/
|
||||||
|
login:
|
||||||
|
auth_url: http://127.0.0.1/login.php?login_challenge={{login_challenge}}
|
||||||
|
consent:
|
||||||
|
consent_url: http://127.0.0.1/consent.php?consent_challenge={{consent_challenge}}
|
||||||
|
post_logout_redirect:
|
||||||
|
to: http://127.0.0.1/logout.php
|
20
templates/lam.conf.php.tpl
Normal file
20
templates/lam.conf.php.tpl
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
// templates/lam.conf.php.tpl
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LDAP Account Manager (LAM) configuration
|
||||||
|
* We do NOT store any passwords here. We read them from env vars at runtime.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$config->db['type'] = "none"; // No SQL DB for LAM (read-only mode)
|
||||||
|
$config->auth_mode = "session"; // LAM’s own login screen (used for Hydra’s login)
|
||||||
|
|
||||||
|
$config->ldap_host = "ldap://127.0.0.1";
|
||||||
|
$config->ldap_port = 389;
|
||||||
|
$config->ldap_base = "dc=${LDAP_DOMAIN}";
|
||||||
|
$config->ldap_bind_id = "${LAM_BIND_DN}";
|
||||||
|
$config->ldap_bind_pass = "${LAM_BIND_PASSWORD}";
|
||||||
|
|
||||||
|
$config->language = "en";
|
||||||
|
$config->realm = "IdentityManagement";
|
||||||
|
?>
|
Reference in New Issue
Block a user