Skip to content

A secure proxy service that makes browser-only calendar links (like Office 365 shared calendars) accessible to calendar clients that can't authenticate or handle custom headers

License

Notifications You must be signed in to change notification settings

OidaTiftla/calendar-proxy

Repository files navigation

Calendar-Proxy

A tiny proxy to make browser-only calendar links (for example Office 365 reachcalendar.ics) usable by calendar clients that can't authenticate or set custom User-Agent headers.

Self-hosting

Run a single container (secure defaults):

docker run -d --restart=unless-stopped \
    --name calendar-proxy \
    -p 8000:8000 \
    --read-only \
    --cap-drop=ALL \
    --security-opt=no-new-privileges:true \
    --memory=512m \
    --cpus=0.5 \
    --tmpfs=/tmp:noexec,nosuid,size=100m \
    --tmpfs=/var/tmp:noexec,nosuid,size=50m \
    --env-file .env \
    calendar-proxy

.env (example, keep this file private):

PROXY_TOKENS=longtoken123
ALLOWED_HOSTS=example.com,calendar.example.org
LOG_LEVEL=WARNING
DISABLE_ACCESS_LOG=true

Notes:

  • Prefer --env-file over inline -e to avoid leaking secrets in process lists or shell history.
  • Container runs as non-root user (UID 1000) for enhanced security.
  • Hardened with --read-only, capability drops, no-new-privileges, resource limits, and temporary filesystems.
  • Use REDIS_URL when running multiple containers for shared rate-limiting.
  • For production, consider using docker-compose.production.yml which includes additional security measures.

Required / recommended env vars

  • PROXY_TOKENS (required): comma-separated secret tokens used in subscription URLs.
  • ALLOWED_HOSTS (recommended): comma-separated allowed upstream hostnames.
  • REDIS_URL (optional): redis://... for cross-process rate limiting.
  • RATE_LIMIT_PER_MIN (default 60): per-token request limit per minute.
  • UPSTREAM_USER_AGENT (default: Safari on macOS): User-Agent header sent to upstream servers.
  • MAX_RESPONSE_BYTES (default: 5242880 bytes / 5MB): maximum response size to prevent memory exhaustion.
  • CONNECT_TIMEOUT (default: 10 seconds): timeout for establishing upstream connections.
  • READ_TIMEOUT (default: 20 seconds): timeout for reading upstream response data.
  • ALLOWED_CONTENT_TYPES (default: text/calendar,text/plain,application/octet-stream): comma-separated allowed upstream content types.
  • LOG_LEVEL (default: INFO): application log level (DEBUG, INFO, WARNING, ERROR).
  • DISABLE_ACCESS_LOG (default: true): set to false to enable logging of URLs and accesses.

Build from source

docker build -t calendar-proxy .

Production deployment

For production use, a hardened docker-compose.production.yml is provided:

# Copy and customize the production compose file
cp docker-compose.production.yml docker-compose.yml

# Create secure environment file
echo "PROXY_TOKENS=$(openssl rand -hex 40)" > .env

# Deploy with enhanced security
docker-compose up -d

The production configuration includes:

  • Non-root execution: Runs as UID/GID 1000
  • Read-only filesystem: Prevents runtime modifications
  • Capability restrictions: Drops all capabilities except essential ones
  • Resource limits: Memory (512MB) and CPU (0.5 cores) constraints
  • No new privileges: Prevents privilege escalation
  • Temporary filesystems: Secure /tmp and /var/tmp mounts
  • Enhanced logging: Production-ready log levels

How the proxy works (short)

  • Endpoint: GET /sub/{token}/{b64url}/{name}.ics
    • token: secret in the path.
    • b64url: URL-safe base64 (no padding) of the full upstream URL.
    • name.ics: final filename for clients.
    • Optional ua query param: override User-Agent used to fetch upstream.

Security & runtime behaviour:

  • Container security: Runs as non-root user, read-only filesystem, dropped capabilities, resource limits.
  • Validates token against PROXY_TOKENS.
  • Resolves b64url hostname and refuses private/loopback/link-local/multicast/reserved IPs (SSRF protection).
  • Enforces ALLOWED_HOSTS when set.
  • Validates upstream content types against ALLOWED_CONTENT_TYPES.
  • Per-token rate limiting (Redis if REDIS_URL set, otherwise in-process fallback with automatic cleanup).
  • Streams upstream response, enforces timeouts and a maximum response byte cap.
  • Strips client cookies and Authorization; forwards only safe upstream headers (e.g. Content-Type, Content-Disposition).
  • Sanitizes URLs in logs to prevent exposure of sensitive information (when DISABLE_ACCESS_LOG=true).

Why macOS Calendar can't directly subscribe to Microsoft Office 365 links (very short)

  1. Office 365 shared URLs are browser-centric
  • Shared reachcalendar.ics links are designed to be opened by a browser, which may provide cookies or other auth automatically; macOS Calendar does not.
  1. User-Agent restrictions
  • Microsoft checks User-Agent and rejects unsupported clients. Requests from macOS CalendarAgent often get a "Outlook is not supported on this browser" page instead of an ICS.
  1. No auth flow support
  • Some shared calendars require Microsoft authentication (cookies/OAuth). macOS Calendar cannot perform those interactive browser flows.

Workarounds: use this proxy (fetch with a browser-like UA), import into a service that fetches server-side, or download & import manually.

Quick examples

Generate a URL-safe base64 without padding (shell):

python - <<'PY'
import base64
u='https://example.com/cal.ics'
print(base64.urlsafe_b64encode(u.encode()).decode().rstrip('='))
PY

Or without Python (using base64 + tr):

echo -n 'https://example.com/cal.ics' | base64 -w0 | tr '+/' '-_' | tr -d '='

Subscription URL:

https://proxy.example.com/sub/<token>/<b64url>/Work.ics

Open http://<host>:8000/ in a browser to use the included index.html UI for building links.

Endpoints

  • GET /sub/{token}/{b64url}/{name}.ics — subscription proxy endpoint.
  • GET /healthz — health check JSON response.
  • GET /version — version information JSON response (includes version, build date, and commit).
  • GET / — web UI for building calendar subscription URLs.
  • GET /static/{path} — static files (CSS, etc.) for the web UI.

If you want an admin UI for token management, Redis-backed caching, or a docker-compose.yml with Redis, tell me which and I'll add it.

About

A secure proxy service that makes browser-only calendar links (like Office 365 shared calendars) accessible to calendar clients that can't authenticate or handle custom headers

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors 2

  •  
  •