Add optional loopback TLS to API startup
This commit is contained in:
@@ -58,6 +58,18 @@ python -m uvicorn app.main:app --reload --host 127.0.0.1 --port 8000
|
||||
|
||||
6. افتح `http://127.0.0.1:8000/docs`.
|
||||
|
||||
### HTTPS محلي اختياري
|
||||
|
||||
يدعم `start-api.ps1` شهادة PEM ومفتاحها الخاص عند الحاجة إلى اختبار HTTPS على loopback. يجب أن تتضمن الشهادة SAN للاسم `localhost` و/أو العنوان `127.0.0.1`، وأن تكون موثوقة على جهاز العميل؛ يرفض Flutter والمتصفح الشهادة غير الموثوقة ولا نعطل التحقق منها. عرّف المسارين قبل تشغيل الخادم:
|
||||
|
||||
```powershell
|
||||
$env:SOVEREIGNAI_TLS_CERTFILE = 'C:\path\to\localhost-cert.pem'
|
||||
$env:SOVEREIGNAI_TLS_KEYFILE = 'C:\path\to\localhost-key.pem'
|
||||
.\start-api.ps1
|
||||
```
|
||||
|
||||
ثم اضبط عنوان API في التطبيق على `https://localhost:8000`. يبقى الخادم مربوطًا بـ`127.0.0.1` وحاجز loopback فعالًا؛ هذا إعداد تطوير محلي ولا يجهز شهادة عامة أو وصولًا من أجهزة أخرى. لا تضع المفتاح الخاص في Git، واترك المتغيرين فارغين معًا لاستخدام HTTP المحلي الافتراضي.
|
||||
|
||||
## نقاط التجربة
|
||||
- `GET /health` للتأكد من أن الخدمة تعمل.
|
||||
- `POST /v1/chat/completions` لإرسال رسالة.
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
- [x] اختبار مخزن جلسة الويب المؤقت (2/2) وبناء Flutter Web ناجح (2026-10-03). رسالة الدخول توضح انتهاء الجلسة عند إعادة تحميل الصفحة.
|
||||
- [x] إضافة استعادة كلمة المرور عبر SMTP: رمز عشوائي 30 دقيقة بصمة SHA-256، لمرة واحدة، تحديد طلبات حسب البريد والعميل، رسالة API عامة، وتسجيل خروج كل الجلسات القديمة بعد تغيير كلمة المرور. مسارات Flutter للطلب وإدخال الرمز وتغيير كلمة المرور مضافة؛ 12 اختبار مصادقة نجحت، و`flutter analyze` نظيف (2026-10-03). يتطلب الإرسال إعداد متغيرات SMTP الموثقة في README.
|
||||
- [x] اختبار FastAPI محليًا عبر TLS: `scripts/local_tls_smoke.py` ولّد شهادة اختبار مؤقتة، شغّل الخادم على `127.0.0.1`، تحقق من شهادة TLS صراحة وأعاد `/openapi.json` الحالة 200، ورفض شهادة غير موثوقة؛ أُغلق الخادم وحُذفت ملفات الشهادة بعد الاختبار (2026-10-03).
|
||||
- [ ] TLS: تحقق Windows Dart `HttpClient` المستخدم من عميل Flutter من `https://localhost/.../health` وأعاد 200 مع شهادة مؤقتة أضيفت إلى مخزن CurrentUser ثم حُذفت وتأكد غياب بصمتها (2026-10-03). محاولة Edge headless لم تنتهِ خلال 25 ثانية، لذلك ثقة المتصفح غير مؤكدة. إعداد TLS دائم والتحقق من العميل/المتصفح قبل أي وصول شبكي ما زالا مفتوحين؛ شهادة smoke ليست شهادة نشر.
|
||||
- [ ] TLS: تحقق Windows Dart `HttpClient` المستخدم من عميل Flutter من `https://localhost/.../health` وأعاد 200 مع شهادة مؤقتة أضيفت إلى مخزن CurrentUser ثم حُذفت وتأكد غياب بصمتها (2026-10-03). أضيف لـ`start-api.ps1` خيار شهادة/مفتاح PEM عبر `SOVEREIGNAI_TLS_CERTFILE` و`SOVEREIGNAI_TLS_KEYFILE`، ويظل الإعداد الافتراضي HTTP على loopback؛ الزوج مطلوب معًا، ولا يثبت السكربت شهادةً في مخزن الثقة. (2026-10-03: اختبار تشغيل فعلي عبر السكربت على `127.0.0.1:8128` أعاد `/health` الحالة 200 باستخدام الشهادة المحددة، ورفضها عميل HTTPX عند استخدام مخزن الثقة الافتراضي. أوقفت الخدمة وحذفت الشهادة والمفتاح وقاعدة الاختبار المؤقتة.) محاولة Edge headless السابقة لم تنتهِ خلال 25 ثانية، لذلك ثقة المتصفح/واجهة Flutter على شهادة دائمة غير مؤكدة. إعداد إدارة شهادة محلية دائمة والتحقق من العميل/المتصفح قبل أي وصول شبكي ما زالا مفتوحين؛ شهادة smoke ليست شهادة نشر.
|
||||
- [x] حد أولي دائم لتخمين كلمات المرور وإنشاء الحسابات في SQLite مع نافذة انتهاء و`Retry-After`؛ اختبرته مجموعة Python كاملة، وتبقى مراجعة الحدود تحت reverse proxy موثوق قبل أي نشر خارجي.
|
||||
- [x] تعطيل حفظ رمز الجلسة الدائم في الويب؛ استخدام مخزن ذاكرة مؤقت وإظهار ذلك للمستخدم.
|
||||
- [ ] التحقق من متطلبات Linux keyring وتشغيل تخزين الجلسة الأصلي، وبناء macOS وLinux على منصات فعلية.
|
||||
|
||||
@@ -24,4 +24,25 @@ if (-not ($ollama.models.name -contains $model)) {
|
||||
throw "Ollama is running, but model '$model' is missing. Run: ollama pull $model"
|
||||
}
|
||||
$port = if ($env:SOVEREIGNAI_API_PORT) { $env:SOVEREIGNAI_API_PORT } else { '8000' }
|
||||
& (Join-Path $project '.venv\Scripts\python.exe') -m uvicorn app.main:app --app-dir $project --host 127.0.0.1 --port $port
|
||||
$uvicornArgs = @(
|
||||
'-m', 'uvicorn', 'app.main:app',
|
||||
'--app-dir', $project,
|
||||
'--host', '127.0.0.1',
|
||||
'--port', $port
|
||||
)
|
||||
$tlsCertificate = $env:SOVEREIGNAI_TLS_CERTFILE
|
||||
$tlsPrivateKey = $env:SOVEREIGNAI_TLS_KEYFILE
|
||||
if ([string]::IsNullOrWhiteSpace($tlsCertificate) -ne [string]::IsNullOrWhiteSpace($tlsPrivateKey)) {
|
||||
throw 'Set both SOVEREIGNAI_TLS_CERTFILE and SOVEREIGNAI_TLS_KEYFILE, or leave both empty.'
|
||||
}
|
||||
if (-not [string]::IsNullOrWhiteSpace($tlsCertificate)) {
|
||||
if (-not (Test-Path -LiteralPath $tlsCertificate -PathType Leaf) -or
|
||||
-not (Test-Path -LiteralPath $tlsPrivateKey -PathType Leaf)) {
|
||||
throw 'The configured TLS certificate or private-key file does not exist.'
|
||||
}
|
||||
$uvicornArgs += @('--ssl-certfile', $tlsCertificate, '--ssl-keyfile', $tlsPrivateKey)
|
||||
Write-Host 'Local HTTPS enabled with the configured certificate (loopback only).'
|
||||
} else {
|
||||
Write-Host 'Local HTTP enabled (loopback only).'
|
||||
}
|
||||
& (Join-Path $project '.venv\Scripts\python.exe') @uvicornArgs
|
||||
|
||||
Reference in New Issue
Block a user