All materials
docker-compose.yml
ymldocker-compose.yml
# VERIFICATION: Run `docker compose up -d` and verify all services are healthy before use.
# Expected services: tenant-wifi-1, tenant-wifi-2, bms-doha, bms-alwakra, cctv-manager, access-control, grafana, loki, alloy
# Expected ports: grafana:3001, loki:3100, bms-doha:8080, bms-alwakra:8081, cctv-manager:9090, access-control:7070
services:
# Tenant Wi-Fi simulation - Building 1 (Doha)
tenant-wifi-1:
image: alpine:3.19
container_name: tenant-wifi-1
command: >
sh -c "apk add --no-cache nmap curl netcat-openbsd && tail -f /dev/null"
networks:
- default
labels:
zone: "tenant"
building: "doha-1"
restart: unless-stopped
# Tenant Wi-Fi simulation - Building 2 (Al Wakra)
tenant-wifi-2:
image: alpine:3.19
container_name: tenant-wifi-2
command: >
sh -c "apk add --no-cache nmap curl netcat-openbsd && tail -f /dev/null"
networks:
- default
labels:
zone: "tenant"
building: "alwakra-1"
restart: unless-stopped
# Building Management System - Doha (modern, supports authentication)
bms-doha:
image: python:3.11-alpine
container_name: bms-doha
command: >
sh -c "pip install flask && python -c \"
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route('/status')
def status():
return jsonify({'building': 'Doha Tower 1', 'hvac': 'operational', 'fire_suppression': 'armed', 'elevators': 'running'})
@app.route('/auth', methods=['POST'])
def auth():
token = request.headers.get('Authorization')
if token == 'Bearer maskan-bms-2024':
return jsonify({'authenticated': True})
return jsonify({'authenticated': False}), 401
@app.route('/control', methods=['POST'])
def control():
token = request.headers.get('Authorization')
if token != 'Bearer maskan-bms-2024':
return jsonify({'error': 'unauthorized'}), 401
return jsonify({'result': 'command accepted'})
app.run(host='0.0.0.0', port=8080)
\""
ports:
- "8080:8080"
networks:
- default
labels:
zone: "building-systems"
building: "doha-1"
restart: unless-stopped
# Building Management System - Al Wakra (LEGACY - no authentication)
bms-alwakra:
image: python:3.11-alpine
container_name: bms-alwakra
command: >
sh -c "pip install flask && python -c \"
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route('/status')
def status():
return jsonify({'building': 'Al Wakra Complex', 'hvac': 'operational', 'fire_suppression': 'armed', 'access_doors': '14 units'})
@app.route('/control', methods=['POST'])
def control():
return jsonify({'result': 'command accepted', 'warning': 'no authentication configured'})
app.run(host='0.0.0.0', port=8080)
\""
ports:
- "8081:8080"
networks:
- default
labels:
zone: "building-systems"
building: "alwakra-1"
legacy: "true"
restart: unless-stopped
# CCTV Management System (with VPN simulation port)
cctv-manager:
image: python:3.11-alpine
container_name: cctv-manager
command: >
sh -c "apk add --no-cache socat && pip install flask && (socat TCP-LISTEN:1194,fork,reuseaddr SYSTEM:'echo VPN-connected' &) && python -c \"
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/cameras')
def cameras():
return jsonify({'cameras': 48, 'buildings': 12, 'nvrs': 6, 'remote_access': 'vpn_enabled'})
@app.route('/feeds')
def feeds():
return jsonify({'live_feeds': 48, 'recording': True, 'retention_days': 30})
app.run(host='0.0.0.0', port=9090)
\""
ports:
- "9090:9090"
- "1194:1194"
networks:
- default
labels:
zone: "surveillance"
vendor_access: "third-party-vpn"
restart: unless-stopped
# Electronic Access Control System
access-control:
image: python:3.11-alpine
container_name: access-control
command: >
sh -c "pip install flask && python -c \"
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route('/doors')
def doors():
return jsonify({'total_doors': 156, 'buildings': 12, 'locked': 148, 'unlocked': 8})
@app.route('/unlock', methods=['POST'])
def unlock():
token = request.headers.get('Authorization')
if token != 'Bearer maskan-acl-2024':
return jsonify({'error': 'unauthorized'}), 401
return jsonify({'result': 'door unlocked'})
app.run(host='0.0.0.0', port=7070)
\""
ports:
- "7070:7070"
networks:
- default
labels:
zone: "access-control"
restart: unless-stopped
# Grafana - Monitoring Dashboard
grafana:
image: grafana/grafana:10.2.3
container_name: grafana
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=maskan2024
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
volumes:
- grafana-data:/var/lib/grafana
networks:
- default
labels:
zone: "management"
restart: unless-stopped
# Loki - Log Aggregation
loki:
image: grafana/loki:2.9.3
container_name: loki
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
networks:
- default
labels:
zone: "management"
restart: unless-stopped
# Alloy - Log Collection
alloy:
image: grafana/alloy:v1.0.0
container_name: alloy
volumes:
- ./alloy-config.yaml:/etc/alloy/config.alloy
- /var/run/docker.sock:/var/run/docker.sock:ro
command: run /etc/alloy/config.alloy
networks:
- default
depends_on:
- loki
labels:
zone: "management"
restart: unless-stopped
volumes:
grafana-data:
networks:
default:
driver: bridge
name: maskan-default