Skip to content

Commit 9e3b371

Browse files
committed
feat(lab-04): FreePBX SSO Integration (OpenLDAP + Keycloak OIDC)
1 parent cac64ce commit 9e3b371

3 files changed

Lines changed: 317 additions & 30 deletions

File tree

.github/workflows/ci.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,35 @@ jobs:
152152
- name: Cleanup
153153
if: always()
154154
run: docker compose -f docker/docker-compose.advanced.yml down -v
155+
156+
lab-04-smoke:
157+
name: Lab 04 -- FreePBX SSO Integration (OpenLDAP + Keycloak OIDC)
158+
runs-on: ubuntu-latest
159+
needs: validate
160+
continue-on-error: true
161+
steps:
162+
- uses: actions/checkout@v4
163+
- name: Install tools
164+
run: sudo apt-get install -y curl default-mysql-client netcat-openbsd ldap-utils
165+
- name: Validate SSO compose
166+
run: docker compose -f docker/docker-compose.sso.yml config -q && echo "SSO compose valid"
167+
- name: Start SSO stack
168+
run: docker compose -f docker/docker-compose.sso.yml up -d
169+
- name: Wait for MariaDB
170+
run: timeout 120 bash -c 'until docker exec freepbx-s04-db mysqladmin ping -uroot -pRootLab04! --silent; do sleep 5; done'
171+
- name: Wait for OpenLDAP
172+
run: timeout 120 bash -c 'until docker exec freepbx-s04-ldap ldapsearch -x -H ldap://localhost -b dc=lab,dc=local -D cn=admin,dc=lab,dc=local -w LdapLab04! cn=admin >/dev/null 2>&1; do sleep 5; done'
173+
- name: Wait for Keycloak
174+
run: timeout 300 bash -c 'until curl -sf http://localhost:8440/realms/master; do sleep 10; done'
175+
- name: Wait for Mailhog
176+
run: timeout 60 bash -c 'until curl -sf http://localhost:8640/api/v2/messages; do sleep 5; done'
177+
- name: Wait for FreePBX web
178+
run: timeout 300 bash -c 'until curl -sf http://localhost:8340/admin/config.php; do sleep 10; done'
179+
- name: Run Lab 10-04 test script
180+
run: bash tests/labs/test-lab-10-04.sh --no-cleanup
181+
- name: Collect logs on failure
182+
if: failure()
183+
run: docker compose -f docker/docker-compose.sso.yml logs
184+
- name: Cleanup
185+
if: always()
186+
run: docker compose -f docker/docker-compose.sso.yml down -v

docker/docker-compose.sso.yml

Lines changed: 182 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,195 @@
1-
# Lab 04 — SSO Integration: freepbx with Keycloak OIDC authentication
2-
---
1+
# =============================================================================
2+
# IT-Stack: FreePBX — Lab 04: SSO Integration
3+
# Module 10 · Phase 3 · Lab 04
4+
# =============================================================================
5+
# Services: MariaDB · OpenLDAP · Keycloak · Mailhog · FreePBX
6+
# Ports: FreePBX web:8340 SIP:5164 AMI:5039 Keycloak:8440
7+
# LDAP:3890 Mailhog:8640
8+
# Credentials:
9+
# DB root: RootLab04!
10+
# FreePBX DB: asteriskuser / AsteriskLab04!
11+
# FreePBX UI: admin / Admin04!
12+
# Keycloak: admin / Admin04!
13+
# LDAP admin: cn=admin,dc=lab,dc=local / LdapLab04!
14+
# What's new vs Lab 03:
15+
# + OpenLDAP directory (osixia/openldap) with ou=users,ou=groups
16+
# + Keycloak 24 dev-mode with LDAP user federation
17+
# + FreePBX LDAP_ENABLED + LDAP_HOST env wiring
18+
# + Keycloak it-stack realm + freepbx OIDC client (created via API in test)
19+
# =============================================================================
20+
21+
name: it-stack-freepbx-lab04
22+
323
services:
4-
freepbx:
5-
image: tiredofit/freepbx:16
6-
container_name: it-stack-freepbx
24+
25+
# ── MariaDB ──────────────────────────────────────────────────────────────
26+
freepbx-s04-db:
27+
image: mariadb:10.11
28+
container_name: freepbx-s04-db
729
restart: unless-stopped
8-
ports:
9-
- "5060:$firstPort"
1030
environment:
11-
- IT_STACK_ENV=lab-04-sso
12-
- KEYCLOAK_URL=
13-
- KEYCLOAK_REALM=
14-
- KEYCLOAK_CLIENT_ID=freepbx
15-
- KEYCLOAK_CLIENT_SECRET=
31+
MYSQL_ROOT_PASSWORD: RootLab04!
32+
MYSQL_DATABASE: asterisk
33+
MYSQL_USER: asteriskuser
34+
MYSQL_PASSWORD: AsteriskLab04!
35+
volumes:
36+
- freepbx-s04-db-data:/var/lib/mysql
37+
healthcheck:
38+
test: ["CMD", "mysqladmin", "ping", "-uroot", "-pRootLab04!", "--silent"]
39+
interval: 10s
40+
timeout: 5s
41+
retries: 15
1642
networks:
17-
- it-stack-net
43+
- freepbx-s04-net
44+
deploy:
45+
resources:
46+
limits:
47+
memory: 512M
48+
cpus: "0.5"
1849

19-
# Local Keycloak for SSO lab (replace with lab-id1 in real env)
20-
keycloak:
21-
image: quay.io/keycloak/keycloak:24
22-
container_name: it-stack-freepbx-keycloak
50+
# ── OpenLDAP ─────────────────────────────────────────────────────────────
51+
freepbx-s04-ldap:
52+
image: osixia/openldap:1.5.0
53+
container_name: freepbx-s04-ldap
54+
restart: unless-stopped
55+
environment:
56+
LDAP_ORGANISATION: "IT-Stack Lab"
57+
LDAP_DOMAIN: lab.local
58+
LDAP_ADMIN_PASSWORD: LdapLab04!
59+
LDAP_CONFIG_PASSWORD: ConfigLab04!
60+
LDAP_BASE_DN: dc=lab,dc=local
61+
LDAP_READONLY_USER: "true"
62+
LDAP_READONLY_USER_USERNAME: readonly
63+
LDAP_READONLY_USER_PASSWORD: ReadOnly04!
64+
ports:
65+
- "3890:389"
66+
volumes:
67+
- freepbx-s04-ldap-data:/var/lib/ldap
68+
- freepbx-s04-ldap-config:/etc/ldap/slapd.d
69+
healthcheck:
70+
test: ["CMD-SHELL", "ldapsearch -x -H ldap://localhost -b dc=lab,dc=local -D cn=admin,dc=lab,dc=local -w LdapLab04! cn=admin > /dev/null 2>&1 || exit 1"]
71+
interval: 10s
72+
timeout: 5s
73+
retries: 15
74+
networks:
75+
- freepbx-s04-net
76+
deploy:
77+
resources:
78+
limits:
79+
memory: 256M
80+
cpus: "0.25"
81+
82+
# ── Keycloak ─────────────────────────────────────────────────────────────
83+
freepbx-s04-kc:
84+
image: quay.io/keycloak/keycloak:24.0.3
85+
container_name: freepbx-s04-kc
86+
restart: unless-stopped
2387
command: start-dev
2488
environment:
2589
KEYCLOAK_ADMIN: admin
26-
KEYCLOAK_ADMIN_PASSWORD: admin
90+
KEYCLOAK_ADMIN_PASSWORD: Admin04!
91+
KC_HEALTH_ENABLED: "true"
92+
KC_DB: dev-file
93+
KC_HOSTNAME_STRICT: "false"
94+
KC_HOSTNAME_STRICT_HTTPS: "false"
95+
KC_HTTP_ENABLED: "true"
2796
ports:
28-
- "8080:8080"
97+
- "8440:8080"
98+
healthcheck:
99+
test: ["CMD-SHELL", "curl -sf http://localhost:8080/realms/master || exit 1"]
100+
interval: 15s
101+
timeout: 10s
102+
retries: 20
103+
start_period: 30s
29104
networks:
30-
- it-stack-net
105+
- freepbx-s04-net
106+
deploy:
107+
resources:
108+
limits:
109+
memory: 1G
110+
cpus: "1.0"
31111

112+
# ── Mailhog ──────────────────────────────────────────────────────────────
113+
freepbx-s04-mail:
114+
image: mailhog/mailhog:latest
115+
container_name: freepbx-s04-mail
116+
restart: unless-stopped
117+
ports:
118+
- "8640:8025"
119+
networks:
120+
- freepbx-s04-net
121+
deploy:
122+
resources:
123+
limits:
124+
memory: 128M
125+
cpus: "0.1"
126+
127+
# ── FreePBX ──────────────────────────────────────────────────────────────
128+
freepbx-s04-app:
129+
image: tiredofit/freepbx:16
130+
container_name: freepbx-s04-app
131+
restart: unless-stopped
132+
depends_on:
133+
freepbx-s04-db:
134+
condition: service_healthy
135+
freepbx-s04-ldap:
136+
condition: service_healthy
137+
ports:
138+
- "8340:80"
139+
- "5164:5060/udp"
140+
- "5164:5060/tcp"
141+
- "5039:5038"
142+
environment:
143+
DB_HOST: freepbx-s04-db
144+
DB_PORT: "3306"
145+
DB_NAME: asterisk
146+
DB_USER: asteriskuser
147+
DB_PASS: AsteriskLab04!
148+
DB_ROOT_PASSWORD: RootLab04!
149+
ADMIN_PASSWORD: Admin04!
150+
WEBROOT: /var/www/html
151+
# LDAP integration for directory sync and user authentication
152+
LDAP_ENABLED: "TRUE"
153+
LDAP_HOST: freepbx-s04-ldap
154+
LDAP_PORT: "389"
155+
LDAP_ADMIN_DN: cn=admin,dc=lab,dc=local
156+
LDAP_ADMIN_PASS: LdapLab04!
157+
LDAP_BASE_DN: dc=lab,dc=local
158+
# Keycloak OIDC references (admin UI SSO)
159+
KEYCLOAK_URL: http://freepbx-s04-kc:8080
160+
KEYCLOAK_REALM: it-stack
161+
KEYCLOAK_CLIENT_ID: freepbx
162+
# Mail relay
163+
SMTP_HOST: freepbx-s04-mail
164+
SMTP_PORT: "1025"
165+
VOICEMAIL_DOMAIN: lab.local
166+
volumes:
167+
- freepbx-s04-data:/data
168+
- freepbx-s04-recordings:/var/spool/asterisk/monitor
169+
- freepbx-s04-moh:/var/lib/asterisk/moh
170+
- freepbx-s04-voicemail:/var/spool/asterisk/voicemail
171+
- freepbx-s04-logs:/var/log/asterisk
172+
networks:
173+
- freepbx-s04-net
174+
deploy:
175+
resources:
176+
limits:
177+
memory: 1G
178+
cpus: "1.0"
179+
180+
# ── Networks ─────────────────────────────────────────────────────────────────
32181
networks:
33-
it-stack-net:
182+
freepbx-s04-net:
183+
name: freepbx-s04-net
34184
driver: bridge
185+
186+
# ── Volumes ──────────────────────────────────────────────────────────────────
187+
volumes:
188+
freepbx-s04-db-data:
189+
freepbx-s04-ldap-data:
190+
freepbx-s04-ldap-config:
191+
freepbx-s04-data:
192+
freepbx-s04-recordings:
193+
freepbx-s04-moh:
194+
freepbx-s04-voicemail:
195+
freepbx-s04-logs:

tests/labs/test-lab-10-04.sh

Lines changed: 103 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,113 @@ info "Phase 3: Functional Tests (Lab 04 — SSO Integration)"
5252
# fail "Health endpoint not reachable"
5353
# fi
5454

55-
warn "Functional tests for Lab 10-04 pending implementation"
55+
# ── 3a: Keycloak realm + OIDC client ─────────────────────────────────────────
56+
info "Creating it-stack realm and freepbx OIDC client via Keycloak API..."
5657

57-
# ── PHASE 4: Cleanup ──────────────────────────────────────────────────────────
58-
info "Phase 4: Cleanup"
59-
docker compose -f "${COMPOSE_FILE}" down -v --remove-orphans
60-
info "Cleanup complete"
58+
KC_TOKEN=$(curl -sf -X POST "http://localhost:8440/realms/master/protocol/openid-connect/token" \
59+
-H "Content-Type: application/x-www-form-urlencoded" \
60+
-d "grant_type=password&client_id=admin-cli&username=admin&password=Admin04!" \
61+
| grep -o '"access_token":"[^"]*' | cut -d'"' -f4 || echo "")
62+
63+
if [ -n "${KC_TOKEN}" ]; then
64+
pass "Keycloak admin token obtained"
65+
else
66+
fail "Failed to get Keycloak admin token"
67+
KC_TOKEN=""
68+
fi
69+
70+
if [ -n "${KC_TOKEN}" ]; then
71+
HTTP_STATUS=$(curl -sf -o /dev/null -w "%{http_code}" \
72+
-X POST "http://localhost:8440/admin/realms" \
73+
-H "Authorization: Bearer ${KC_TOKEN}" \
74+
-H "Content-Type: application/json" \
75+
-d '{"realm":"it-stack","enabled":true,"displayName":"IT-Stack Lab"}' || echo "000")
76+
if [ "${HTTP_STATUS}" = "201" ] || [ "${HTTP_STATUS}" = "409" ]; then
77+
pass "Keycloak it-stack realm created (status: ${HTTP_STATUS})"
78+
else
79+
fail "Failed to create it-stack realm (status: ${HTTP_STATUS})"
80+
fi
81+
fi
82+
83+
if [ -n "${KC_TOKEN}" ]; then
84+
HTTP_STATUS=$(curl -sf -o /dev/null -w "%{http_code}" \
85+
-X POST "http://localhost:8440/admin/realms/it-stack/clients" \
86+
-H "Authorization: Bearer ${KC_TOKEN}" \
87+
-H "Content-Type: application/json" \
88+
-d '{"clientId":"freepbx","enabled":true,"protocol":"openid-connect","publicClient":false,"redirectUris":["http://localhost:8340/*"]}' || echo "000")
89+
if [ "${HTTP_STATUS}" = "201" ] || [ "${HTTP_STATUS}" = "409" ]; then
90+
pass "Keycloak freepbx OIDC client created (status: ${HTTP_STATUS})"
91+
else
92+
fail "Failed to create freepbx OIDC client (status: ${HTTP_STATUS})"
93+
fi
94+
fi
95+
96+
if curl -sf "http://localhost:8440/realms/it-stack/.well-known/openid-configuration" | grep -q 'issuer'; then
97+
pass "Keycloak OIDC discovery endpoint returns issuer"
98+
else
99+
fail "Keycloak OIDC discovery endpoint missing issuer"
100+
fi
101+
102+
# ── 3b: LDAP integration ──────────────────────────────────────────────────────
103+
info "Testing LDAP integration..."
104+
105+
if docker exec freepbx-s04-ldap ldapsearch -x -H ldap://localhost \
106+
-b dc=lab,dc=local -D cn=admin,dc=lab,dc=local -w LdapLab04! \
107+
'(objectClass=*)' dn 2>/dev/null | grep -q 'dn:'; then
108+
pass "LDAP base DC dc=lab,dc=local has entries"
109+
else
110+
fail "LDAP base DC search returned no entries"
111+
fi
112+
113+
if docker exec freepbx-s04-app curl -sf http://freepbx-s04-kc:8080/realms/master > /dev/null 2>&1; then
114+
pass "Keycloak reachable from FreePBX container"
115+
else
116+
fail "Keycloak not reachable from FreePBX container"
117+
fi
118+
119+
if docker exec freepbx-s04-app env | grep -q 'LDAP_ENABLED=TRUE'; then
120+
pass "LDAP_ENABLED=TRUE set in FreePBX container"
121+
else
122+
fail "LDAP_ENABLED not set in FreePBX container"
123+
fi
124+
125+
if docker exec freepbx-s04-app env | grep -q 'LDAP_HOST=freepbx-s04-ldap'; then
126+
pass "LDAP_HOST correctly set to freepbx-s04-ldap"
127+
else
128+
fail "LDAP_HOST not set correctly in FreePBX container"
129+
fi
130+
131+
# ── 3c: VoIP ports ────────────────────────────────────────────────────────────
132+
if nc -z localhost 5164 2>/dev/null; then
133+
pass "SIP port 5164 reachable"
134+
else
135+
warn "SIP port 5164 not yet reachable (FreePBX may still be initializing)"
136+
fi
137+
138+
if nc -z localhost 5039 2>/dev/null; then
139+
pass "AMI port 5039 reachable"
140+
else
141+
warn "AMI port 5039 not yet reachable"
142+
fi
143+
144+
# ── 3d: All 5 containers running ──────────────────────────────────────────────
145+
for svc in freepbx-s04-db freepbx-s04-ldap freepbx-s04-kc freepbx-s04-mail freepbx-s04-app; do
146+
if docker ps --format '{{.Names}}' | grep -q "^${svc}$"; then
147+
pass "Container ${svc} running"
148+
else
149+
fail "Container ${svc} not running"
150+
fi
151+
done
61152

62153
# ── Results ───────────────────────────────────────────────────────────────────
63154
echo ""
64-
echo -e "${CYAN}======================================${NC}"
65-
echo -e " Lab ${LAB_ID} Complete"
66-
echo -e " ${GREEN}PASS: ${PASS}${NC} | ${RED}FAIL: ${FAIL}${NC}"
67-
echo -e "${CYAN}======================================${NC}"
155+
echo -e "${CYAN}============================================================${NC}"
156+
echo -e " Lab ${LAB_ID} Complete"
157+
echo -e " ${GREEN}PASS: ${PASS}${NC} | ${RED}FAIL: ${FAIL}${NC}"
158+
echo -e "${CYAN}============================================================${NC}"
159+
160+
# Cleanup
161+
docker compose -f "${COMPOSE_FILE}" down -v --remove-orphans 2>/dev/null || true
68162

69163
if [ "${FAIL}" -gt 0 ]; then
70164
exit 1

0 commit comments

Comments
 (0)