devops
公開
rails
kamal
docker
security
fhir
harden
on-premise
醫療資訊化
Rails + Kamal 醫療系統安全部署 Harden Guide
Ruby on Rails 搭配 Kamal Deploy 在醫療、FHIR、公開 API 環境的安全部署指南,涵蓋 non-root container、read-only + cap-drop、Secrets 管理、Network 隔離、SMART on FHIR,以及降低 Blast Radius 的優先清單。
|
匯入於 2026-05-27
|
已複製連結
分享連結產生失敗,已改用原始網址
Rails + Kamal 醫療系統安全部署 Harden Guide
本指南整理 Ruby on Rails 搭配 Kamal Deploy 在醫療、FHIR、公開 API 環境中的安全部署建議,重點在於降低 FHIR Server、Rails API、Docker Container 被入侵後的風險與爆炸半徑(Blast Radius)。
核心原則
- FHIR Server 可以公開,但 HIS / Oracle / PACS / LIS 不可直接暴露
- Rails Container 被攻破時,應限制其只能取得最小必要權限
- Production container 不應該可任意修改、安裝套件或寫入系統檔案
- 所有外部 API 應透過 OAuth2 / SMART on FHIR / API Gateway 保護
推薦架構
flowchart LR
I[Internet] --> WAF[WAF]
WAF --> RP[Reverse Proxy\nnginx / Caddy]
RP --> API[Rails / FHIR API\nDMZ]
API --> AD[Adapter / Queue\nKafka / ETL]
AD --> HIS[HIS / Oracle\nPACS / LIS\n內部網段]
HIS --> DB[(Core DB)]
style HIS fill:#fdd,stroke:#c00
style DB fill:#fdd,stroke:#c00
style API fill:#dfd,stroke:#080
- FHIR Server 建議位於 DMZ,不直接連核心 HIS DB
- FHIR API 與 HIS 應透過 Adapter / Queue / Kafka / ETL 隔離
- 核心資料庫不得對 DMZ 層直接開放連線
Dockerfile 安全建議
原則
| 項目 | 建議 |
|---|---|
| 執行身份 | 使用 non-root user 執行 Rails |
| 基礎映像 | 使用 ruby:3.4-slim,避免 full image |
| 禁止工具 | 不在 production image 安裝 bash、gcc、wget、curl、vim |
| Build Toolchain | Production image 不包含 build toolchain |
範例
# 建立專用系統帳號,禁止登入 shell
RUN groupadd --system --gid 1000 rails && \
useradd rails --uid 1000 --gid 1000 \
--create-home --shell /usr/sbin/nologin
# 切換至非 root 身份
USER rails:rails
Kamal deploy.yml 安全設定
關鍵選項
| 選項 | 效果 |
|---|---|
read-only: true |
Container 檔案系統唯讀,無法在 runtime 安裝後門或修改檔案 |
cap-drop: ALL |
移除所有 Linux capabilities,最小化提權風險 |
no-new-privileges: true |
禁止 setuid / setgid 提權 |
tmpfs: /tmp /app/tmp |
以記憶體暫存目錄取代磁碟寫入 |
範例
# deploy.yml
servers:
web:
options:
user: '1000:1000'
read-only: true
cap-drop: 'ALL'
security-opt:
- no-new-privileges:true
tmpfs:
- /tmp
- /app/tmp
Rails 應用層注意事項
啟用 read-only container 後,Rails 預設的磁碟寫入會失效,需對應調整:
| 調整項目 | 建議做法 |
|---|---|
| Log 輸出 | 改用 STDOUT logging(config.logger = Logger.new(STDOUT)) |
| Active Storage | 使用 S3 / MinIO,不使用本地磁碟 |
| tmp 目錄 | 確認 Rails 的 Rails.root/tmp 指向 tmpfs 掛載點 |
| Master Key | 絕對不放入 image,透過環境變數或 Vault 注入 |
Secrets 管理
flowchart LR
S[Secrets\nDB_PASSWORD\nMASTER_KEY\nAPI_TOKEN] --> V{管理方式}
V -->|推薦| VAULT[HashiCorp Vault\n/ AWS KMS]
V -->|可接受| RI[Runtime Injection\n環境變數動態注入]
V -->|禁止| IMG[寫死在 Image ❌]
V -->|禁止| ENV[提交 .env 到 Git ❌]
VAULT --> T[短效 Token\n可 Revoke]
- 禁止將
DB_PASSWORD、MASTER_KEY寫死在 image 或.env - 推薦使用 Vault、KMS、runtime injection
- Access Token 應短效化並可即時 revoke
Network Security
flowchart LR
C[Container] -->|允許| OA[OAuth Server\nSmartAuth]
C -->|允許| IA[Internal Adapter]
C -->|允許| DNS[必要 DNS]
C -->|封鎖| INT[Internet ❌]
C -->|封鎖| HIS[HIS DB 直連 ❌]
- 限制 outbound traffic:Container 不應可直接存取 Internet
- 只允許連線:OAuth Server、Internal Adapter、必要 DNS
- 搭配 iptables / nftables 或 Kubernetes NetworkPolicy 實施
醫療系統額外建議
| 項目 | 建議 |
|---|---|
| FHIR Bulk Export | 特別限制,需額外授權與稽核 |
| API Scope | 避免 patient/*.read 過大 scope,最小授權原則 |
| Audit Log | 所有存取應保留完整 audit log,符合醫療法規要求 |
| Incident Response | 建立明確的資安事件應變流程 |
建議優先完成清單
flowchart TD
P1[1. Rails container 改成 non-root] --> P2[2. Kamal 加上 read-only + cap-drop]
P2 --> P3[3. FHIR Server 與 HIS DB 分離]
P3 --> P4[4. 導入 OAuth2 / SMART on FHIR]
P4 --> P5[5. 限制 API scope]
P5 --> P6[6. 建立 centralized logs]
style P1 fill:#ffd,stroke:#aa0
style P2 fill:#ffd,stroke:#aa0
style P3 fill:#dfd,stroke:#080
style P4 fill:#dfd,stroke:#080
style P5 fill:#ddf,stroke:#008
style P6 fill:#ddf,stroke:#008
| 優先級 | 項目 | 說明 |
|---|---|---|
| 🔴 高 | Rails container non-root | 最基本的容器安全,立即執行 |
| 🔴 高 | Kamal read-only + cap-drop | 大幅縮小爆炸半徑 |
| 🟡 中 | FHIR Server 與 HIS DB 分離 | 架構層隔離,一旦被攻破不影響核心資料 |
| 🟡 中 | OAuth2 / SMART on FHIR | 標準化存取控制 |
| 🟢 一般 | 限制 API scope | 最小授權原則 |
| 🟢 一般 | Centralized logs | 事後稽核與事件偵測 |
延伸閱讀
- team-dev-culture-tech-stack — 團隊技術棧:Rails + Docker + Kamal + FHIR
- ndmc-info-seminar-2026 — 國軍醫院 SMART on FHIR 落地案例
- selsm-nhird-skill-distillation-taiwan — FHIR Adapter 層設計參考