Skip to content

csphu/radbridge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RadBridge

License: MIT Status Podman PostgreSQL Security

Healthcare Data Bridge: Securely connect on-premises radiology and clinical systems to the cloud

RadBridge is a containerized platform for bridging radiology imaging (DICOM) and clinical data (HL7/FHIR) from on-premises healthcare systems to cloud destinations. Features Orthanc DICOM server, Mirth Connect integration engine, and PostgreSQL—orchestrated with Podman Quadlet and systemd for enterprise-grade security and reliability.

Status: Core infrastructure is production-ready. Healthcare integration layer (Orthanc-Mirth communication) is under active development. See Project Status for details.

Table of Contents

Overview

RadBridge provides a complete on-premises healthcare data bridge designed for:

  • Cloud Migration: Securely stream radiology and clinical data from on-prem to cloud
  • Healthcare Interoperability: HL7, FHIR, and DICOM protocol support
  • Enterprise Security: Rootless containers, capability dropping, secrets management, HIPAA-ready
  • Production Reliability: systemd integration, resource limits, automated restarts
  • Operational Excellence: Centralized logging, health checks, reverse proxy architecture

Perfect for healthcare organizations migrating medical imaging and clinical data to the cloud while maintaining on-premises PACS and EMR integration.

Architecture

flowchart TB
    subgraph external[External Access]
        HTTPS[HTTPS :8443]
        HTTP[HTTP :8080]
        DICOM[DICOM :4242]
    end
    
    subgraph proxy[Reverse Proxy Layer]
        NGINX[Nginx<br/>Alpine 1.27]
    end
    
    subgraph apps[Application Services]
        ORTHANC[Orthanc DICOM Server<br/>26.1.0]
        MIRTH[Mirth Connect<br/>Integration Engine 4.5.0]
        PGADMIN[pgAdmin 4<br/>Database UI]
    end
    
    subgraph data[Data Layer]
        POSTGRES[(PostgreSQL 17<br/>Database)]
        VOLUMEORTHANC[(orthanc_data<br/>Volume)]
        VOLUMEPOSTGRES[(postgres_data<br/>Volume)]
        VOLUMEMIRTH[(mirth_data<br/>Volume)]
    end
    
    HTTP --> NGINX
    NGINX -.->|/orthanc/| ORTHANC
    NGINX -.->|/pgadmin/| PGADMIN
    HTTPS --> MIRTH
    DICOM --> ORTHANC
    MIRTH --> POSTGRES
    PGADMIN --> POSTGRES
    ORTHANC --> VOLUMEORTHANC
    ORTHANC --> POSTGRES
    POSTGRES --> VOLUMEPOSTGRES
    MIRTH --> VOLUMEMIRTH
    
    style HTTP fill:#e1f5ff
    style HTTPS fill:#e1f5ff
    style DICOM fill:#e1f5ff
    style NGINX fill:#90EE90
    style POSTGRES fill:#4169E1,color:#fff
    style ORTHANC fill:#FF6B6B,color:#fff
    style MIRTH fill:#FFA500,color:#fff
Loading

Key Features

Healthcare Integration Stack

  • HL7/FHIR Processing: Mirth Connect 4.5.0 for healthcare message transformation
  • DICOM Server: Orthanc 26.1.0 for medical imaging storage and PACS integration
  • Database Backend: PostgreSQL 17 with separate databases per service

Security-First Design

  • Rootless Containers: Full user namespace isolation
  • Secrets Management: Podman secrets for credential injection (zero plaintext passwords)
  • Capability Dropping: Minimal Linux capabilities per service
  • Network Isolation: Internal network with reverse proxy access control
  • Audit Logging: Comprehensive systemd journal integration

Production Ready

  • systemd Integration: Native service management with Quadlet
  • Resource Limits: CPU, memory, and task quotas prevent resource exhaustion
  • Health Monitoring: Built-in health checks and restart policies
  • Zero-Downtime Updates: Declarative configuration management

Operations & Monitoring

  • Centralized Logging: Structured logs with unique identifiers per service
  • Reverse Proxy: Nginx-based unified access point for web interfaces
  • Database Management: pgAdmin web interface for administration
  • Volume Management: Persistent storage for data files and images

Project Status

Current Status: Infrastructure Foundation Complete - Integration Layer In Progress

RadBridge has a fully functional container orchestration platform with production-grade security hardening. The foundational services (PostgreSQL, Orthanc DICOM server, Mirth Connect, reverse proxy) are deployed and operational.

What's Complete

  • Rootless Podman Quadlet container orchestration
  • PostgreSQL database backend with separate databases per service
  • Orthanc DICOM server with C-STORE/C-FIND/C-MOVE protocol support
  • Mirth Connect integration engine deployment
  • Nginx reverse proxy with unified web access
  • Podman secrets management for credentials
  • Linux capability dropping across all containers -SystemD resource limits and restart policies
  • Structured audit logging with unique identifiers
  • Automated setup and installation scripts

In Development

Orthanc-Mirth Integration Pipeline

The core infrastructure is complete, but the integration layer connecting Orthanc to Mirth is under active development:

  • Orthanc Lua Webhook: Lua script to trigger HTTP notifications when DICOM studies become stable
  • Mirth HTTP Listener Channel: Corresponding channel to receive Orthanc webhooks and process study metadata
  • DICOM Study Routing: Logic to route studies to appropriate cloud destinations
  • HL7/FHIR Transformation: Convert DICOM metadata to HL7v2 or FHIR format for downstream systems
  • Error Handling: Retry logic, dead letter queues, and failure notification

Roadmap

Near Term (Q1 2026)

  • Complete Orthanc Lua webhook implementation
  • Build Mirth channel for study-level notifications
  • Document integration configuration
  • Add sample DICOM test data and workflows

Medium Term (Q2 2026)

  • TLS/SSL termination at nginx reverse proxy
  • Cloud destination connectors (AWS S3, Azure Blob, GCP Storage)
  • FHIR ImagingStudy resource generation
  • Prometheus/Grafana monitoring dashboards

Long Term (2026+)

  • Multi-site deployment capabilities
  • Advanced anonymization and de-identification
  • DICOM modality worklist (MWL) support
  • HL7v2 ADT feed integration
  • Kubernetes deployment options

Important: This is an active development project. The container infrastructure is production-ready, but the healthcare integration workflows are still being built. Contributions are welcome!

Project Structure

radbridge/
├── containers/          # Podman Quadlet container definitions
│   ├── postgres.container
│   ├── orthanc.container
│   ├── mirth.container
│   ├── pgadmin.container
│   ├── nginx.container
│   └── edge.network    # Network configuration
├── config/             # Service configuration files
│   ├── nginx.conf      # Reverse proxy configuration
│   └── init-databases.sh  # Database initialization script
├── scripts/            # Automation scripts
│   ├── install.sh      # Install files to systemd directory
│   └── setup.sh        # Automated setup and deployment
├── docs/               # Documentation
│   ├── CONTRIBUTING.md
│   ├── SECURITY.md
│   └── CHANGELOG.md
├── .github/            # GitHub templates and workflows
│   ├── ISSUE_TEMPLATE/
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── copilot-instructions.md
├── .gitignore
├── Makefile            # Command shortcuts
├── README.md           # This file
├── LICENSE             # MIT License
└── CREDENTIALS.md      # Generated by setup (not in git)

Installation Location

The install.sh script copies files from this repository to ~/.config/containers/systemd/ where Podman Quadlet expects to find them:

  • containers/*.container → Container service definitions
  • containers/edge.network → Network configuration
  • config/nginx.conf → Mounted by nginx container
  • config/init-databases.sh → Mounted by postgres container

Quick Start

Prerequisites

  • Linux with systemd support (tested on Ubuntu 24.04 in WSL)
  • Podman 4.0+ installed
  • User systemd services enabled: loginctl enable-linger $USER

Installation

  1. Clone the repository

    git clone https://github.com/csphu/radbridge.git
    cd radbridge
  2. Install Quadlet files to systemd user directory

    ./scripts/install.sh

    This script copies container definitions from containers/ and configuration files from config/ to ~/.config/containers/systemd/ where systemd can find them.

  3. Run automated setup (recommended)

    ./scripts/setup.sh

    The setup script will:

    • Generate secure random passwords
    • Create Podman secrets
    • Start all services in correct order
    • Save credentials to CREDENTIALS.md

    Or configure manually:

    # Generate and store secure passwords
    POSTGRES_PWD=$(openssl rand -base64 16)
    ORTHANC_DB_PWD=$(openssl rand -base64 16)
    MIRTH_DB_PWD=$(openssl rand -base64 16)
    ORTHANC_ADMIN_PWD=$(openssl rand -base64 16)
    PGADMIN_PWD=$(openssl rand -base64 16)
    
    # Create Podman secrets
    echo -n "$POSTGRES_PWD" | podman secret create postgres_password -
    echo -n "$ORTHANC_DB_PWD" | podman secret create orthanc_db_password -
    echo -n "$MIRTH_DB_PWD" | podman secret create mirth_db_password -
    echo -n "$PGADMIN_PWD" | podman secret create pgadmin_password -
    echo -n "{\"admin\": \"$ORTHANC_ADMIN_PWD\"}" | podman secret create orthanc_registered_users -
    
    # Save credentials
    echo "PostgreSQL root: $POSTGRES_PWD" > CREDENTIALS.md
    echo "Orthanc DB user: $ORTHANC_DB_PWD" >> CREDENTIALS.md
    echo "Mirth DB user: $MIRTH_DB_PWD" >> CREDENTIALS.md
    echo "Orthanc admin: $ORTHANC_ADMIN_PWD" >> CREDENTIALS.md
    echo "pgAdmin: $PGADMIN_PWD" >> CREDENTIALS.md
    
    # Start services manually
    systemctl --user daemon-reload
    systemctl --user start postgres.service pgadmin.service orthanc.service mirth.service nginx.service
  4. Verify deployment

    systemctl --user status postgres orthanc nginx
    journalctl --user -t orthanc-quadlet -n 20
  5. Access services

    See CREDENTIALS.md for passwords.

Services

Reverse Proxy (Nginx 1.27)

Purpose: Unified access point for web management interfaces

Configuration: nginx.conf (mounted read-only)
Image: nginx:1.27-alpine
Resources: CPU 100%, Memory 256MB, Tasks 64

Routing:

  • /orthanc/ → Orthanc Web UI (port 8042)
  • /pgadmin/ → pgAdmin UI (port 80)
  • /health → Health check endpoint

Benefits: Network isolation, centralized logging, TLS termination ready

PostgreSQL 17

Purpose: Relational database backend for all services

Image: postgres:17-alpine
Databases: orthancdb (Orthanc), mirthdb (Mirth)
Users: orthancuser, mirthuser (isolated credentials)
Init Script: init-databases.sh
Resources: CPU 200%, Memory 2GB, Tasks 512

Mirth Connect 4.5.0

Purpose: Healthcare integration engine for HL7, FHIR message processing

Image: nextgenhealthcare/connect:4.5.0
Database: PostgreSQL (mirthdb)
Access: https://localhost:8443
Resources: CPU 150%, Memory 1GB, Tasks 256

Orthanc 26.1.0

Purpose: DICOM medical imaging server with PACS integration

Image: orthancteam/orthanc:26.1.0
Database: PostgreSQL (orthancdb) for metadata indexing
Storage: Volume orthanc_data for DICOM files
Protocols:

  • DICOM C-STORE/C-FIND/C-MOVE (port 4242)
  • Web UI (via nginx reverse proxy)
    Resources: CPU 200%, Memory 2GB, Tasks 512

pgAdmin 4

Purpose: Web-based PostgreSQL administration interface

Image: dpage/pgadmin4:latest
Access: http://localhost:8080/pgadmin/
Email: admin@example.com
Resources: CPU 100%, Memory 512MB, Tasks 128

Network & Ports

Network Architecture

All services communicate over the systemd-edge bridge network defined in edge.network. This provides:

  • Internal DNS: Services resolve each other by container name
  • Isolation: No host network access by default
  • Security: Only explicitly published ports accessible externally

Port Mappings

Port Service Purpose Public
8080 Nginx HTTP reverse proxy (Orthanc, pgAdmin) Yes
8443 Mirth Connect HTTPS web UI and API Yes
4242 Orthanc DICOM protocol (C-STORE, C-FIND, C-MOVE) Yes
5432 PostgreSQL Database connections No - Internal only

Network Isolation Benefits:

  • Web management interfaces not directly exposed
  • Single point of access control
  • Centralized logging and monitoring
  • Foundation for TLS/SSL termination

Deployment

Quick Commands (Makefile)

For convenience, common operations are available via make:

make help          # Show all available commands
make setup         # Run initial setup
make start         # Start all services
make stop          # Stop all services
make status        # Check service status
make logs          # View recent logs
make health        # Run health checks

See Makefile for all available commands.

Service Management

Start all services:

systemctl --user daemon-reload
systemctl --user start postgres.service pgadmin.service mirth.service orthanc.service nginx.service

Check status:

systemctl --user status postgres orthanc mirth

Enable auto-start on boot:

systemctl --user enable postgres.service orthanc.service nginx.service

Stop services:

systemctl --user stop postgres orthanc mirth pgadmin nginx

View logs:

journalctl --user -t orthanc-quadlet -n 50 --no-pager
journalctl --user -t postgres-quadlet -f

Volume Management

List volumes:

podman volume ls

Inspect volume:

podman volume inspect orthanc_data

Backup volume:

podman volume export orthanc_data -o orthanc_backup_$(date +%Y%m%d).tar

Restore volume:

podman volume import orthanc_data orthanc_backup_20260212.tar

Security

For comprehensive security information, see docs/SECURITY.md including:

  • HIPAA compliance considerations
  • Vulnerability reporting procedures
  • Security best practices and hardening checklist
  • Incident response guidelines

Secrets Management

This deployment uses Podman secrets to securely manage passwords and sensitive data. Secrets are stored outside container configuration and injected as environment variables at runtime.

Secret reference:

Secret Name Used By Purpose
postgres_password postgres PostgreSQL root password
orthanc_db_password postgres, orthanc Orthanc database user password
mirth_db_password postgres, mirth Mirth database user password
orthanc_registered_users orthanc Orthanc web UI credentials (JSON)
pgadmin_password pgadmin pgAdmin web UI password

Managing secrets:

# List secrets
podman secret ls

# View secret metadata (not the value)
podman secret inspect postgres_password

# Rotate a secret
systemctl --user stop orthanc.service
podman secret rm orthanc_registered_users
echo -n '{"admin": "newpassword"}' | podman secret create orthanc_registered_users -
systemctl --user start orthanc.service

# Remove a secret
podman secret rm secret_name

How it works: Secrets are referenced in .container files using:

Secret=secret_name,type=env,target=ENVIRONMENT_VARIABLE

Podman injects secret values as environment variables at container startup, ensuring no plaintext passwords in configuration files or logs.

Container Security Hardening

All containers implement defense-in-depth security measures:

Linux Capability Dropping

Containers drop all unnecessary capabilities to minimize attack surface:

PostgreSQL & Mirth:

DropCapability=ALL
AddCapability=CHOWN CAP_DAC_OVERRIDE FOWNER SETGID SETUID

Orthanc & pgAdmin (require network binding):

DropCapability=ALL
AddCapability=CHOWN CAP_DAC_OVERRIDE FOWNER SETGID SETUID NET_BIND_SERVICE

Verify capabilities:

podman inspect orthanc --format '{{.EffectiveCaps}}'

Additional Hardening

All containers include:

  • NoNewPrivileges=true: Prevents privilege escalation
  • SecurityLabelDisable=true: Disables SELinux/AppArmor for performance
  • Rootless mode: User namespace isolation (UID 0 in container → UID 1000 on host)

User Namespace Isolation

Rootless Podman automatically isolates container UIDs:

  • Container root (UID 0) → Host user (UID 1000)
  • Container users (UID 1-65536) → Host UIDs 100000-165535

Verify mapping:

podman unshare cat /proc/self/uid_map
# Output: 0 1000 1 (container root = host user)
#         1 100000 65536 (container users = unprivileged)

Security Hardening Checklist

Production deployment checklist:

Critical Priority

  • Secrets Management: Podman secrets instead of plaintext passwords
  • TLS/SSL Encryption: Enable HTTPS for all web interfaces
  • Remove Git Credential History: Clean committed passwords from git history

High Priority

  • Network Isolation: Services bound to internal network with reverse proxy
  • Firewall Configuration: Configure firewalld/iptables rules
  • Strong Passwords: Random password generation implemented
  • Regular Updates: Automated security updates for container images
  • Audit Logging: Structured logging with unique identifiers

Medium Priority

  • Resource Limits: systemd resource quotas configured
  • Capability Dropping: Minimal Linux capabilities per service
  • Read-only Root Filesystem: Mount containers with read-only root where possible
  • User Namespaces: Rootless Podman user namespace isolation
  • Security Scanning: Regular vulnerability scanning (Trivy, etc.)

Low Priority

  • SELinux/AppArmor: Mandatory access controls
  • Intrusion Detection: Host-based IDS (AIDE, Wazuh)
  • Backup Encryption: Encrypt volume backups at rest
  • Multi-factor Authentication: MFA for web interfaces

Healthcare Compliance

  • HIPAA Compliance: Review technical safeguards for PHI
  • Disaster Recovery: Document and test backup/restore
  • Monitoring & Alerting: Health monitoring (Prometheus/Grafana)
  • Incident Response: Maintain runbooks for security incidents

Healthcare Integration (In Development)

  • Orthanc Lua Webhook: Implement OnStableStudy callback to notify Mirth
  • Mirth HTTP Listener: Create channel to receive Orthanc webhook requests
  • Study Routing Logic: Configure destination routing based on study metadata
  • HL7/FHIR Transformation: Convert DICOM metadata to healthcare standards
  • Error Handling: Implement retry logic and failure notifications
  • Cloud Connectors: Build destination adapters for AWS/Azure/GCP
  • Test Data & Workflows: Provide sample DICOM studies for integration testing

Resource Management

All services have systemd resource limits to prevent resource exhaustion and ensure fair allocation:

Service CPU Quota Memory Max Tasks Max Rationale
PostgreSQL 200% (2 cores) 2 GB 512 Database operations require adequate CPU/memory
Orthanc 200% (2 cores) 2 GB 512 DICOM image processing is CPU/memory intensive
Mirth 150% (1.5 cores) 1 GB 256 Integration engine with moderate resource needs
pgAdmin 100% (1 core) 512 MB 128 Lightweight web UI
Nginx 100% (1 core) 256 MB 64 Reverse proxy with minimal overhead

Managing Resource Limits

View current limits:

systemctl --user show postgres.service --property=CPUQuotaPerSecUSec --property=MemoryMax --property=TasksMax

Adjust limits: Edit the [Service] section in the respective .container file:

[Service]
CPUQuota=150%      # 1.5 CPU cores
MemoryMax=1G       # 1 GB RAM
TasksMax=256       # Max 256 tasks/threads

Then reload and restart:

systemctl --user daemon-reload
systemctl --user restart orthanc.service

Monitor resource usage:

podman stats --no-stream
systemctl --user status orthanc

Monitoring & Logging

Structured Logging

All services use systemd journal with unique identifiers for easy filtering:

Service SyslogIdentifier View Command
PostgreSQL postgres-quadlet journalctl --user -t postgres-quadlet
Orthanc orthanc-quadlet journalctl --user -t orthanc-quadlet
Mirth mirth-quadlet journalctl --user -t mirth-quadlet
pgAdmin pgadmin-quadlet journalctl --user -t pgadmin-quadlet
Nginx nginx-quadlet journalctl --user -t nginx-quadlet

Log Management Commands

View recent logs:

journalctl --user -t orthanc-quadlet -n 50 --no-pager

Follow logs in real-time:

journalctl --user -t nginx-quadlet -f

Filter by time:

journalctl --user -t postgres-quadlet --since "1 hour ago"
journalctl --user -t mirth-quadlet --since "2026-02-12 09:00"

Search logs:

journalctl --user -t orthanc-quadlet | grep ERROR

Export logs:

journalctl --user -t orthanc-quadlet --since today -o json > orthanc_logs.json

Health Monitoring

Service health checks:

# Check systemd service status
systemctl --user is-active postgres orthanc nginx

# Nginx health endpoint
curl http://localhost:8080/health

# Orthanc system information
curl -u admin http://localhost:8080/orthanc/system

# PostgreSQL connection test
podman exec postgres pg_isready -U postgres

Container health:

podman ps --format "{{.Names}}\t{{.Status}}"
podman healthcheck run orthanc  # If healthcheck defined

Credentials

Important: All credentials are stored as Podman secrets. For security reasons, actual passwords are not documented in this README.

  • After initial deployment, credentials are stored in CREDENTIALS.md (excluded from git)
  • To view secrets: podman secret ls
  • To rotate passwords, see the Secret Management section below

Default Access

All web interfaces accessed via nginx reverse proxy:

Direct access:

  • Mirth Connect: https://localhost:8443 (web UI and Administrator client API)
  • Orthanc DICOM: localhost:4242 - for DICOM client connections
  • PostgreSQL: localhost:5432 (user: postgres) - internal only, not published

See CREDENTIALS.md for the actual passwords.

Documentation

Project Documentation

Service Documentation

Orthanc DICOM Server

Generate sample config:

podman exec orthanc Orthanc --config=- > orthanc-sample.jsonc

Mirth Connect

PostgreSQL

Podman & Quadlet

Troubleshooting

Common Issues

Service fails to start:

# Check service status and logs
systemctl --user status orthanc.service
journalctl --user -t orthanc-quadlet -n 50 --no-pager

# Verify dependencies are running
systemctl --user status postgres.service

# Check for port conflicts
ss -tlnp | grep 4242

Secret not found error:

# Verify secret exists
podman secret ls | grep orthanc_registered_users

# Recreate secret if missing
echo -n '{"admin": "password"}' | podman secret create orthanc_registered_users -

Database connection errors:

# Verify PostgreSQL is accepting connections
podman exec postgres pg_isready -U postgres

# Check database exists
podman exec postgres psql -U postgres -c "\l"

# Verify user permissions
podman exec postgres psql -U postgres orthancdb -c "\du"

Permission denied errors:

# Verify volume ownership
podman volume inspect orthanc_data

# Check user namespace mapping
podman unshare cat /proc/self/uid_map

# Restart with proper permissions
systemctl --user restart orthanc.service

Getting Help

  • Issues: Open an issue on GitHub with logs and configuration
  • Discussions: Use GitHub Discussions for questions and ideas
  • Security: Report vulnerabilities privately per docs/CONTRIBUTING.md

Contributing

Contributions are welcome! Please see docs/CONTRIBUTING.md for:

  • Development setup instructions
  • Code style guidelines
  • Pull request process
  • Testing requirements

Priority areas for contribution:

  • Orthanc-Mirth Integration (highest priority): Lua webhook and Mirth channel implementation
  • Cloud Destination Connectors: AWS S3, Azure Blob Storage, GCP Cloud Storage adapters
  • FHIR Resource Generation: Convert DICOM metadata to FHIR ImagingStudy resources
  • Sample Data & Test Workflows: DICOM test datasets and integration examples

Additional areas:

  • TLS/SSL configuration and certificate management
  • Monitoring dashboards (Prometheus, Grafana)
  • Advanced anonymization and de-identification
  • Documentation improvements
  • Security enhancements

See Project Status for the full development roadmap.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

Built with:


Bridging Healthcare Data to the Cloud | Professional radiology and clinical integration platform

About

Healthcare data bridge for securely connecting on-premises radiology DICOM imaging and HL7/FHIR clinical data to cloud destinations. Built with Podman Quadlet, Orthanc, Mirth Connect, and PostgreSQL.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors