Skip to content

Commit 059e2a1

Browse files
authored
Merge pull request #109 from linagora/fix/docker-demo-cert-enroll-doc
Fix stale llng-* names in docker-demo-cert enrollment walkthrough
2 parents 93215fa + 6cf6bae commit 059e2a1

1 file changed

Lines changed: 72 additions & 68 deletions

File tree

docker-demo-cert/README.md

Lines changed: 72 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ This Docker Compose demo demonstrates SSH certificate authentication with LemonL
1313

1414
```mermaid
1515
flowchart LR
16-
subgraph Docker["Docker Network (llng-net)"]
16+
subgraph Docker["Docker Network (ob-cert-net)"]
1717
SSO["SSO<br/>(LLNG)<br/>:80"]
1818
Bastion["Bastion<br/>(SSH)<br/>:2222"]
1919
Backend["Backend<br/>(SSH)<br/>:22"]
@@ -99,14 +99,14 @@ ssh -p 2222 dwho@localhost
9999
### 5. From bastion, connect to backend
100100

101101
The backend server requires a signed JWT from the bastion to prove the connection
102-
comes from an authorized bastion server. Use the `llng-ssh-proxy` command:
102+
comes from an authorized bastion server. Use the `ob-ssh-proxy` command:
103103

104104
```bash
105105
# On bastion - the proxy automatically gets a JWT and forwards it
106-
llng-ssh-proxy backend
106+
ob-ssh-proxy backend
107107

108108
# Or using SSH with ProxyCommand
109-
ssh -o ProxyCommand='llng-ssh-proxy %h %p' dwho@backend
109+
ssh -o ProxyCommand='ob-ssh-proxy %h %p' dwho@backend
110110
```
111111

112112
**Note**: Direct SSH connections to the backend (without the bastion JWT) will be rejected,
@@ -153,7 +153,7 @@ sequenceDiagram
153153
3. **Server Trust Configuration**: SSH servers are configured to trust this CA:
154154

155155
```
156-
# In /etc/ssh/sshd_config.d/llng-*.conf
156+
# In /etc/ssh/sshd_config.d/open-bastion-*.conf
157157
TrustedUserCAKeys /etc/ssh/llng_ca.pub
158158
```
159159

@@ -211,15 +211,15 @@ docker compose up -d backend-new
211211

212212
```bash
213213
# Download CA key from the SSO portal
214-
docker exec llng-backend-new curl -sf http://sso/ssh/ca -o /etc/ssh/llng_ca.pub
215-
docker exec llng-backend-new cat /etc/ssh/llng_ca.pub
214+
docker exec ob-cert-backend-new curl -sf http://sso:8080/ssh/ca -o /etc/ssh/llng_ca.pub
215+
docker exec ob-cert-backend-new cat /etc/ssh/llng_ca.pub
216216
```
217217

218218
#### Step 3: Configure sshd for certificate authentication
219219

220220
```bash
221-
docker exec llng-backend-new tee /etc/ssh/sshd_config.d/llng.conf << 'EOF'
222-
# LemonLDAP::NG SSH Configuration
221+
docker exec ob-cert-backend-new tee /etc/ssh/sshd_config.d/open-bastion.conf << 'EOF'
222+
# Open Bastion SSH Configuration
223223
TrustedUserCAKeys /etc/ssh/llng_ca.pub
224224
PubkeyAuthentication yes
225225
PasswordAuthentication no
@@ -230,33 +230,33 @@ PermitRootLogin no
230230
EOF
231231
```
232232

233-
#### Step 4: Configure PAM for LLNG
233+
#### Step 4: Configure Open Bastion PAM module
234234

235235
```bash
236-
docker exec llng-backend-new tee /etc/security/pam_llng.conf << 'EOF'
237-
# LemonLDAP::NG PAM configuration
238-
portal_url = http://sso
236+
docker exec ob-cert-backend-new tee /etc/open-bastion/openbastion.conf << 'EOF'
237+
# Open Bastion PAM configuration
238+
portal_url = http://sso:8080
239239
server_group = backend-new
240240
client_id = pam-access
241241
client_secret = pamsecret
242242
timeout = 10
243243
verify_ssl = false
244244
cache_enabled = true
245-
cache_dir = /var/cache/pam_llng
245+
cache_dir = /var/cache/open-bastion
246246
cache_ttl = 300
247247
log_level = info
248248
EOF
249249

250-
docker exec llng-backend-new chmod 600 /etc/security/pam_llng.conf
250+
docker exec ob-cert-backend-new chmod 600 /etc/open-bastion/openbastion.conf
251251
```
252252

253253
#### Step 5: Configure NSS for dynamic user resolution
254254

255255
```bash
256-
docker exec llng-backend-new tee /etc/open-bastion/nss_openbastion.conf << 'EOF'
256+
docker exec ob-cert-backend-new tee /etc/open-bastion/nss_openbastion.conf << 'EOF'
257257
# Open Bastion NSS configuration
258-
portal_url = http://sso
259-
server_token_file = /etc/security/pam_llng.token
258+
portal_url = http://sso:8080
259+
server_token_file = /etc/open-bastion/token
260260
cache_ttl = 300
261261
min_uid = 10000
262262
max_uid = 60000
@@ -265,32 +265,33 @@ default_shell = /bin/bash
265265
default_home_base = /home
266266
EOF
267267

268-
# Configure nsswitch.conf to use LLNG
269-
docker exec llng-backend-new sed -i 's/^passwd:.*/passwd: files llng/' /etc/nsswitch.conf
270-
docker exec llng-backend-new sed -i 's/^group:.*/group: files llng/' /etc/nsswitch.conf
268+
# Configure nsswitch.conf to use Open Bastion
269+
docker exec ob-cert-backend-new sed -i 's/^passwd:.*/passwd: files openbastion/' /etc/nsswitch.conf
270+
docker exec ob-cert-backend-new sed -i 's/^group:.*/group: files openbastion/' /etc/nsswitch.conf
271271
```
272272

273273
#### Step 6: Configure PAM stack with home directory creation
274274

275275
```bash
276-
docker exec llng-backend-new tee /etc/pam.d/sshd << 'EOF'
277-
# PAM configuration for SSH with LemonLDAP::NG
276+
docker exec ob-cert-backend-new tee /etc/pam.d/sshd << 'EOF'
277+
# PAM configuration for SSH with Open Bastion
278278
auth required pam_permit.so
279-
account required pam_llng.so
279+
account required pam_openbastion.so
280+
session required pam_openbastion.so create_user=true
280281
session required pam_unix.so
281282
session optional pam_mkhomedir.so skel=/etc/skel umask=0022
282283
EOF
283284
```
284285

285-
#### Step 7: Enroll the server with llng-pam-enroll
286+
#### Step 7: Enroll the server with ob-enroll
286287

287288
```bash
288-
docker exec -it llng-backend-new llng-pam-enroll
289+
docker exec -it ob-cert-backend-new ob-enroll
289290
```
290291

291292
The script will:
292293

293-
1. Read configuration from `/etc/security/pam_llng.conf`
294+
1. Read configuration from `/etc/open-bastion/openbastion.conf`
294295
2. Request a device authorization code from the portal
295296
3. Display a **user code** (e.g., `WXYZ-1234`)
296297
4. Wait for administrator approval
@@ -306,14 +307,13 @@ While the script is waiting, open a browser:
306307

307308
After approval, the script will:
308309

309-
- Save the token to `/etc/security/pam_llng.token`
310-
- Update the PAM configuration with `server_token_file`
310+
- Save the token to `/etc/open-bastion/token`
311311
- Verify the enrollment
312312

313313
#### Step 9: Restart sshd to apply configuration
314314

315315
```bash
316-
docker exec llng-backend-new pkill -HUP sshd
316+
docker exec ob-cert-backend-new pkill -HUP sshd
317317
```
318318

319319
#### Step 10: Test the connection
@@ -331,61 +331,65 @@ curl -s -X POST http://localhost:80/ssh/sign \
331331
| jq -r '.certificate' > /tmp/test_key-cert.pub
332332

333333
# 3. Copy key and certificate to bastion
334-
docker exec llng-bastion mkdir -p /home/dwho/.ssh
335-
docker cp /tmp/test_key llng-bastion:/home/dwho/.ssh/id_ed25519
336-
docker cp /tmp/test_key-cert.pub llng-bastion:/home/dwho/.ssh/id_ed25519-cert.pub
337-
docker exec llng-bastion chown -R dwho:dwho /home/dwho/.ssh
338-
docker exec llng-bastion chmod 700 /home/dwho/.ssh
339-
docker exec llng-bastion chmod 600 /home/dwho/.ssh/id_ed25519
334+
docker exec ob-cert-bastion mkdir -p /home/dwho/.ssh
335+
docker cp /tmp/test_key ob-cert-bastion:/home/dwho/.ssh/id_ed25519
336+
docker cp /tmp/test_key-cert.pub ob-cert-bastion:/home/dwho/.ssh/id_ed25519-cert.pub
337+
docker exec ob-cert-bastion chown -R dwho:dwho /home/dwho/.ssh
338+
docker exec ob-cert-bastion chmod 700 /home/dwho/.ssh
339+
docker exec ob-cert-bastion chmod 600 /home/dwho/.ssh/id_ed25519
340340

341341
# 4. Connect from bastion to backend-new
342-
docker exec -u dwho llng-bastion ssh -o StrictHostKeyChecking=no backend-new whoami
342+
docker exec -u dwho ob-cert-bastion ssh -o StrictHostKeyChecking=no backend-new whoami
343343
# Expected: dwho
344344
```
345345

346346
Note: The user `dwho` is resolved dynamically via the NSS module - no local account exists on the server. The home directory is created automatically on first login by `pam_mkhomedir`.
347347

348-
### Summary: The llng-pam-enroll Script
348+
### Summary: The ob-enroll Script
349349

350-
The `llng-pam-enroll` script automates the entire Device Authorization flow:
350+
The `ob-enroll` script automates the entire Device Authorization flow:
351351

352-
1. **Reads configuration** from `/etc/security/pam_llng.conf` (portal URL, client credentials)
352+
1. **Reads configuration** from `/etc/open-bastion/openbastion.conf` (portal URL, client credentials)
353353
2. **Requests device code** from `/oauth2/device` endpoint
354354
3. **Displays instructions** with user code for administrator approval
355355
4. **Polls for token** at configurable intervals
356-
5. **Saves token** securely to `/etc/security/pam_llng.token`
357-
6. **Updates configuration** with `server_token_file` directive
358-
7. **Verifies enrollment** by calling `/pam/authorize`
356+
5. **Saves token** securely to `/etc/open-bastion/token`
357+
6. **Verifies enrollment** by calling `/pam/authorize`
359358

360359
Options:
361360

362361
```bash
363-
llng-pam-enroll --help
362+
ob-enroll --help
364363

365364
Options:
366365
-p, --portal URL LemonLDAP::NG portal URL
367366
-c, --client-id ID OIDC client ID (default: pam-access)
368-
-s, --client-secret S OIDC client secret
367+
-s, --client-secret S OIDC client secret (prefer OB_CLIENT_SECRET env var)
369368
-g, --server-group G Server group name
370-
-t, --token-file FILE Where to save the token
369+
-t, --token-file FILE Where to save the token (default: /etc/open-bastion/token)
370+
-C, --config FILE Read settings from config file
371371
-k, --insecure Skip SSL certificate verification
372372
-q, --quiet Quiet mode
373373
```
374374

375375
### How it works
376376

377-
1. **Server Token**: Each SSH server needs an OAuth2 access token to authenticate its PAM module requests to the LLNG portal.
377+
1. **Server Token**: Each SSH server needs an OAuth2 access token to authenticate
378+
its PAM module requests to the LLNG portal.
378379

379-
2. **Token Configuration**: The token is passed via the `LLNG_SERVER_TOKEN` environment variable and stored in `/etc/security/llng_server_token`.
380+
2. **Token Configuration**: The token is stored in `/etc/open-bastion/token` and
381+
referenced by `server_token_file` in `/etc/open-bastion/openbastion.conf`.
380382

381383
3. **PAM Authorization Flow**:
382384
```
383-
User SSH → sshd → PAM (pam_llng.so) → LLNG /pam/authorize → Allow/Deny
385+
User SSH → sshd → PAM (pam_openbastion.so) → LLNG /pam/authorize → Allow/Deny
384386
```
385387

386388
### Registering a new SSH server (production)
387389

388-
In production, use the Device Authorization Grant (RFC 8628) to register each server:
390+
In production, use the Device Authorization Grant (RFC 8628) to register each server.
391+
The recommended way is to run `ob-enroll` on the server, which takes care of the
392+
entire flow. The raw HTTP exchange below is shown for reference:
389393

390394
```bash
391395
# 1. Request device authorization
@@ -406,16 +410,16 @@ TOKEN_RESP=$(curl -s -X POST https://sso.example.com/oauth2/token \
406410
-d "client_secret=<secret>")
407411

408412
# 4. Save the token
409-
echo $TOKEN_RESP | jq -r '.access_token' > /etc/security/llng_server_token
410-
chmod 600 /etc/security/llng_server_token
413+
echo $TOKEN_RESP | jq -r '.access_token' > /etc/open-bastion/token
414+
chmod 600 /etc/open-bastion/token
411415
```
412416

413417
### PAM Configuration
414418

415-
Each server needs `/etc/security/pam_llng.conf`:
419+
Each server needs `/etc/open-bastion/openbastion.conf`:
416420

417421
```ini
418-
# LemonLDAP::NG PAM configuration
422+
# Open Bastion PAM configuration
419423
portal_url = https://sso.example.com
420424
server_group = production-servers
421425

@@ -424,23 +428,23 @@ client_id = pam-access
424428
client_secret = <secret>
425429

426430
# Server token for authorization
427-
server_token_file = /etc/security/llng_server_token
431+
server_token_file = /etc/open-bastion/token
428432

429433
# HTTP settings
430434
timeout = 10
431435
verify_ssl = true
432436

433437
# Cache settings (for offline mode)
434438
cache_enabled = true
435-
cache_dir = /var/cache/pam_llng
439+
cache_dir = /var/cache/open-bastion
436440
cache_ttl = 300
437441
```
438442

439443
And `/etc/pam.d/sshd`:
440444

441445
```
442446
auth required pam_permit.so
443-
account required pam_llng.so
447+
account required pam_openbastion.so
444448
session required pam_unix.so
445449
```
446450

@@ -469,7 +473,7 @@ sequenceDiagram
469473
### How it works:
470474

471475
1. User SSH to bastion with SSH certificate
472-
2. From bastion, user runs `llng-ssh-proxy backend`
476+
2. From bastion, user runs `ob-ssh-proxy backend`
473477
3. Proxy requests a signed JWT from LLNG `/pam/bastion-token`
474478
4. Proxy connects to backend with JWT in `LLNG_BASTION_JWT` env var
475479
5. Backend verifies JWT signature using cached JWKS (offline capable)
@@ -494,15 +498,15 @@ sequenceDiagram
494498
### Check container logs
495499

496500
```bash
497-
docker logs llng-sso
498-
docker logs llng-bastion
499-
docker logs llng-backend
501+
docker logs ob-cert-sso
502+
docker logs ob-cert-bastion
503+
docker logs ob-cert-backend
500504
```
501505

502506
### Test PAM authorization manually
503507

504508
```bash
505-
docker exec llng-bastion curl -s http://sso/pam/authorize \
509+
docker exec ob-cert-bastion curl -s http://sso/pam/authorize \
506510
-H "Authorization: Bearer <token>" \
507511
-H "Content-Type: application/json" \
508512
-d '{"user":"dwho","server_group":"bastion"}'
@@ -511,7 +515,7 @@ docker exec llng-bastion curl -s http://sso/pam/authorize \
511515
### Verify SSH CA trust
512516

513517
```bash
514-
docker exec llng-bastion cat /etc/ssh/llng_ca.pub
518+
docker exec ob-cert-bastion cat /etc/ssh/llng_ca.pub
515519
curl http://localhost:80/ssh/ca
516520
```
517521

@@ -541,7 +545,7 @@ User authentication sessions are stored in the SSO container:
541545

542546
```bash
543547
# List active sessions
544-
docker exec llng-sso ls -la /var/lib/lemonldap-ng/sessions/
548+
docker exec ob-cert-sso ls -la /var/lib/lemonldap-ng/sessions/
545549

546550
# Session files are named by session ID (the cookie value)
547551
```
@@ -552,7 +556,7 @@ The SSH Certificate Authority keys are stored in the SSO:
552556

553557
```bash
554558
# View CA storage
555-
docker exec llng-sso ls -la /var/lib/lemonldap-ng/ssh/
559+
docker exec ob-cert-sso ls -la /var/lib/lemonldap-ng/ssh/
556560

557561
# Files:
558562
# - ssh-ca (private key)
@@ -566,7 +570,7 @@ When session recording is enabled on the bastion (via `ForceCommand`), recording
566570

567571
```bash
568572
# List recordings
569-
docker exec llng-bastion ls -la /var/lib/llng-sessions/
573+
docker exec ob-cert-bastion ls -la /var/lib/open-bastion/sessions/
570574

571575
# Recordings use 'script' format and can be replayed with:
572576
# scriptreplay timing_file typescript_file
@@ -575,7 +579,7 @@ docker exec llng-bastion ls -la /var/lib/llng-sessions/
575579
Note: In this demo, session recording is disabled to allow ProxyJump. To enable it, add to sshd config:
576580

577581
```
578-
ForceCommand /usr/sbin/llng-session-recorder
582+
ForceCommand /usr/sbin/ob-session-recorder
579583
```
580584

581585
### PAM Authorization Cache
@@ -584,7 +588,7 @@ Each SSH server caches authorization responses for offline mode:
584588

585589
```bash
586590
# Cache location
587-
docker exec llng-bastion ls -la /var/cache/pam_llng/
591+
docker exec ob-cert-bastion ls -la /var/cache/open-bastion/
588592

589593
# Cache entries are encrypted and expire based on cache_ttl (default: 300s)
590594
```

0 commit comments

Comments
 (0)