下拉重新整理
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)。


核心原則

  1. FHIR Server 可以公開,但 HIS / Oracle / PACS / LIS 不可直接暴露
  2. Rails Container 被攻破時,應限制其只能取得最小必要權限
  3. Production container 不應該可任意修改、安裝套件或寫入系統檔案
  4. 所有外部 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 安裝 bashgccwgetcurlvim
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 loggingconfig.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_PASSWORDMASTER_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 事後稽核與事件偵測

延伸閱讀

© 2025-2026 Nickle Cheng Built with Ruby Ruby on Rails