📍 المقالة الرئيسية للمجموعة: الصحة الرقمية CEDEAO 2026.
القانون الصحي يفرض تتبعاً شاملاً للوصول إلى ملفات المرضى. ARTCI الإيفواري، CDP السنغالي، CDP المغربي قانون 09-08، NESA الإماراتي يطلبون: من اطلع، متى، ماذا، من أين. التخزين 5-10 سنوات حسب البلد. هذا الدرس يفصل تكوين audit trail شامل لـ OpenMRS مع Loki + Grafana للتصور، وتقارير تلقائية للمدققين.
المتطلبات
OpenMRS في الإنتاج. VPS Hetzner CCX13 minimum. Docker + Loki + Grafana. المستوى المتوقع: متقدم. الوقت المقدر: 3-4 ساعات للتكوين الكامل.
الخطوة 1 — تفعيل OpenMRS Audit Module
OpenMRS لديه Audit module مدمج. تفعيله إجباري للإنتاج الصحي:
Admin → Manage Modules → ابحث «auditlog»
# تثبيت إذا غير مثبت
# Status: Started
# تكوين كائنات للتتبع
Admin → Audit Log → Configuration
# تفعيل: Patient, Encounter, Observation, MedicationRequest, User
# Action types: ALL (CREATED, UPDATED, DELETED, VIEWED)
الخطوة 2 — تثبيت Loki
Loki يجمع ويحفظ السجلات. أخف من Elasticsearch، مثالي لـ audit logs. تثبيت عبر Docker:
docker run -d --name loki \
-p 3100:3100 \
-v /var/lib/loki:/loki \
grafana/loki:latest \
-config.file=/etc/loki/local-config.yaml
تكوين retention 1 سنة hot، 5 سنوات cold storage على Backblaze B2:
# /etc/loki/local-config.yaml
limits_config:
retention_period: 8760h # 1 سنة hot
chunk_store_config:
max_look_back_period: 8760h
table_manager:
retention_deletes_enabled: true
retention_period: 43800h # 5 سنوات
storage_config:
aws:
s3: s3://eu-central-003.backblazeb2.com/audit-cold-storage
bucketnames: audit-cold-storage
الخطوة 3 — تثبيت Promtail (collecteur)
Promtail يقرأ سجلات OpenMRS Audit ويرسلها إلى Loki:
# /etc/promtail/config.yml
server:
http_listen_port: 9080
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: openmrs-audit
static_configs:
- targets: [localhost]
labels:
job: openmrs-audit
__path__: /var/log/openmrs/audit.log
pipeline_stages:
- json:
expressions:
user: user
patient: patient
action: action
timestamp: timestamp
الخطوة 4 — تثبيت Grafana للتصور
docker run -d --name grafana \
-p 3000:3000 \
-v grafana-data:/var/lib/grafana \
grafana/grafana:latest
تكوين datasource Loki: Settings → Data Sources → Add → Loki → URL http://loki:3100.
الخطوة 5 — لوحات Grafana للـ Audit
إنشاء dashboards مفيدة للـ DPO (Data Protection Officer):
- Dashboard «Patient Access»: من فتح ملف المريض X في 30 يوماً.
- Dashboard «After Hours Access»: الوصول خارج ساعات العمل (موقع شك).
- Dashboard «Failed Logins»: محاولات اقتحام.
- Dashboard «Bulk Exports»: تصدير > 100 ملف (تسرب محتمل).
- Dashboard «Modifications par utilisateur»: الأطباء النشطون.
كل لوحة بـ LogQL queries:
# مثال: الوصول إلى ملف مريض محدد
{job="openmrs-audit"} | json | patient="UUID-DIALLO"
# الوصول خارج ساعات العمل (22h-6h)
{job="openmrs-audit"} | json | __error__="" | hour(timestamp) >= 22 or hour(timestamp) <= 6
# تصديرات ضخمة
{job="openmrs-audit"} | json | action="EXPORT" | count > 100
الخطوة 6 — تنبيهات في الوقت الفعلي
Grafana Alerting يولد تنبيهات على أحداث حساسة. webhook إلى Mattermost قناة #security:
# Alert rule: Bulk export
expr: count_over_time({job="openmrs-audit"} | json | action="EXPORT" [5m]) > 50
for: 1m
labels:
severity: critical
annotations:
summary: "Suspicious bulk export by {{ $labels.user }}"
# Webhook إلى Mattermost
url: https://chat.votre-hopital.com/hooks/abc123
body: |
{
"text": "🚨 ALERT: {{ .Annotations.summary }}",
"channel": "security"
}
الخطوة 7 — تقرير سنوي للمدقق
سكربت Python يولد تقرير PDF للتدقيق السنوي ARTCI/CDP:
import requests
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
def generate_audit_report(year):
# استفسار Loki للسنة
queries = {
'total_accesses': f'count_over_time({{job="openmrs-audit"}}[1y])',
'unique_users': f'count(count by (user) ({{job="openmrs-audit"}}))',
'bulk_exports': f'count_over_time({{job="openmrs-audit"}} | json | action="EXPORT"[1y])',
'after_hours': f'count_over_time({{job="openmrs-audit"}} | json | hour > 22 or hour < 6 [1y])',
}
results = {}
for key, query in queries.items():
r = requests.get('http://loki:3100/loki/api/v1/query',
params={'query': query, 'time': f'{year}-12-31T23:59:59Z'})
results[key] = r.json()['data']['result'][0]['value'][1]
# توليد PDF
pdf = canvas.Canvas(f"audit-report-{year}.pdf", pagesize=A4)
pdf.drawString(100, 800, f"تقرير التدقيق السنوي {year}")
pdf.drawString(100, 750, f"إجمالي الوصول: {results['total_accesses']}")
pdf.drawString(100, 700, f"المستخدمون الفريدون: {results['unique_users']}")
pdf.drawString(100, 650, f"تصديرات ضخمة: {results['bulk_exports']}")
pdf.save()
الخطوة 8 — حماية ضد التلاعب
Audit logs يجب ألا تكون قابلة للتعديل (write-once). استراتيجيات:
- Object Lock على Backblaze B2: ملفات غير قابلة للحذف لـ 5 سنوات.
- HMAC-SHA256 signature على كل log entry.
- Replication إلى MinIO ثانوي offsite.
# Backblaze B2 Object Lock
b2 update-bucket --fileLockEnabled audit-cold-storage allPrivate
b2 update-bucket --defaultRetention compliance --period "5 years" audit-cold-storage
الخطوة 9 — Anonymization للأبحاث
للدراسات الإحصائية، تصدير anonymized يحترم RGPD/CEDEAO:
def anonymize_patient(patient):
# احذف معرفات مباشرة
del patient['name'], patient['phone'], patient['email']
# هاش NIN
patient['nin_hash'] = hashlib.sha256(patient['nin'].encode()).hexdigest()
del patient['nin']
# تعميم تاريخ الميلاد إلى عام
patient['birth_year'] = patient['birth_date'][:4]
del patient['birth_date']
return patient
الخطوة 10 — DPO Dashboard
Dashboard موحد للـ DPO يعرض:
- نظرة عامة 30 يوم: عدد الوصول، المستخدمون النشطون، الإيقافات.
- تنبيهات حالية (الأحداث المشبوهة).
- تقرير شهري قابل للتصدير.
- إجراء «حذف بيانات مريض» (Right to Erasure GDPR/CDP).
- إجراء «تصدير بيانات مريض» (Data Portability).
الأخطاء الشائعة
| الخطأ | السبب | الحل |
|---|---|---|
| Loki disk full | retention مفقود | config retention_period |
| Promtail لا يقرأ logs | permissions | chmod 644 audit.log + adduser promtail adm |
| Audit logs مفقودة | module غير مفعَّل | Admin Modules → Start auditlog |
| تنبيهات مغرقة | thresholds منخفضة | tuner alert rules |
| تقرير PDF فارغ | queries خاطئة | اختبر LogQL في Grafana Explore |
| Cold storage بطيء | B2 Eu-Central-003 موصى به | اختر datacenter قريب |
التكيف مع السياق
أربع توضيحات. القوانين المحلية. ARTCI كوت ديفوار (قانون 2013-450)، CDP السنغال (قانون 2008-12)، CDP المغرب (09-08)، NESA الإمارات. كلها تطلب audit trail لكن متطلبات الاحتفاظ تختلف (5-10 سنوات). التدقيقات السنوية. ARTCI تطلب تقريراً سنوياً في الـ DPA report. صدِّر مباشرة من Grafana. Right to Erasure. GDPR/CDP يفرضون حذف البيانات عند الطلب. لكن للبيانات الصحية، استثناءات قانونية (الأرشفة الطبية الإلزامية 20 سنة في فرنسا/المغرب). DPO يقرر. Cross-border data flow. CEDEAO Cross Border Act 2025 يتطلب موافقة قبل نقل البيانات خارج CEDEAO. Hetzner ألمانيا = adequacy decision EU، مقبول لكن وثِّق.
دروس الإخوة
الأسئلة المتكررة
كم تكلفة Loki + Grafana؟ Hetzner CX22 (4.51 يورو/شهر) يستضيف الاثنان لـ < 50 GB logs/شهر.
retention 5 سنوات تكلفة؟ 5 GB/سنة × 5 سنوات × 6 USD/TB Backblaze = 0.15 USD/سنة. ضئيل.
HIPAA-compliant؟ AWS لديه «HIPAA-compliant» eligible services. Hetzner ألمانيا يخضع لـ GDPR، يكفي للقواعد الإفريقية.
التدقيقات في الوقت الفعلي؟ Grafana Alerting + webhook Mattermost = response في 30 ثانية على event مشبوه.
للاستزادة
- 🔝 المرجع: الصحة الرقمية CEDEAO
- Loki docs: grafana.com/docs/loki