Skip to content

Security Hardening Guide

Your trading credentials live inside this container. This guide shows you how to keep them there.

Disclaimer: This is an unofficial community packaging. Not affiliated with, endorsed by, or supported by Futu Securities or moomoo.


The Golden Rules

  1. Never hardcode secrets. As of v10.10+, no password in config — remember-login handles credentials. Mount keys as files.
  2. Least privilege. Run as non-root, drop capabilities, lock down the filesystem.
  3. Network is enemy territory. TLS on everything that crosses a wire. Firewall everything else.
  4. Rotate. Keys wear out. Change them periodically via the Futu OpenAPI dashboard.

Credential Handling

Remember-login (v10.10+)

FutuOpenD v10.10+ uses remember-login — credentials are cached after the first interactive login and reused automatically. Your FUTU_ACCOUNT environment variable identifies the account; no password in config.

Protect the data volume. The Docker volume futuopend-data contains the cached session. If compromised, an attacker could potentially access your trading account. Treat the volume as sensitive.

Your RSA private key — chmod 600, never commit it

Your RSA key is the most sensitive piece here. Guard it accordingly:

chmod 600 secrets/rsa_key.txt

And keep it out of version control — the repo's .gitignore handles this.

FutuOpenD.xml — built-in default is usually sufficient

The image ships with a built-in FutuOpenD.xml at /usr/local/bin/FutuOpenD.xml. The deploy compose mounts your own ./FutuOpenD.xml over it (OpenD resolves ${VAR} placeholders itself).

If you need custom settings, mount your own at container startup — but keep sensitive values out of it. v10.10+ remember-login means no password in the config file.


Network Security

Local binding — the safe default

FutuOpenD binds to 127.0.0.1 by default. Only local processes can reach it — the network can't touch it. This is the right mode for single-machine setups:

<ip>127.0.0.1</ip>
<api_port>11111</api_port>

Remote access — TLS is non-negotiable

If another machine needs to connect, the WebSocket traffic must be encrypted. Generate a certificate and key, then:

<ip>0.0.0.0</ip>
<websocket_private_key>/run/secrets/ws_key_nopass.pem</websocket_private_key>
<websocket_cert>/run/secrets/ws_cert.pem</websocket_cert>

Generate a self-signed cert for testing:

openssl req -x509 -newkey rsa:4096 \
  -keyout secrets/key.pem -out secrets/cert.pem \
  -days 365 -nodes -subj "/CN=futuopend"

# Strip the password — FutuOpenD doesn't handle encrypted keys
openssl rsa -in secrets/key.pem -out secrets/key_nopass.pem

Better yet for remote access: use a VPN (WireGuard, Tailscale) or an SSH tunnel. Encryption without managing certificates.

Firewall — default deny

# Allow only your client subnet
iptables -A INPUT -p tcp --dport 11111 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 11111 -j DROP

Or isolate FutuOpenD inside a Docker internal network so it can't reach the outside world except where you explicitly allow:

services:
  futuopend:
    networks:
      - futu-internal

networks:
  futu-internal:
    internal: true   # No external egress by default

Container Hardening

Read-only filesystem + dropped capabilities

This is the production baseline. Add it to your docker-compose.yaml:

services:
  futuopend:
    security_opt:
      - no-new-privileges:true
    read_only: true
    tmpfs:
      - /tmp
    cap_drop:
      - ALL

What each line does:

Option What it does
no-new-privileges Prevents the container from gaining privileges via suid binaries
read_only Filesystem is immutable except for mounted volumes
tmpfs Temp data lives in RAM, not on disk
cap_drop: ALL Strips every Linux capability the process doesn't need

Non-root user

The image already creates a futuopend user (UID 1000) and switches to it. No action needed.

External secrets managers

For larger deployments, pull secrets at runtime:

Tool How it works
HashiCorp Vault Vault Agent sidecar injects secrets into the container
AWS Secrets Manager aws-secrets-manager-sidecar pulls keys at startup
Azure Key Vault akv-sidecar handles injection

Monitoring & Audit

  • Start with log_level=debug during setup. Once everything is stable, switch to info.
  • Ship logs somewhere centralized — ELK, Loki, CloudWatch.
  • Set up alerts on authentication failures.
  • Watch resource usage. Sudden spikes in CPU or memory can be an early warning.

Dependency Updates

Rebuild the image periodically to pull fresh OS security patches:

cd ../futuopend
docker build --no-cache -t shing1211/futuopend:latest .

Pin the FutuOpenD version in production. Auto-upgrades at the wrong time can break your trading system.


Security Checklist

Run through this before going live:

  • [ ] RSA private key has no password and chmod 600
  • [ ] FutuOpenD.xml does not contain hardcoded credentials (v10.10+ uses remember-login)
  • [ ] Remote access uses TLS (both websocket_private_key and websocket_cert set)
  • [ ] Firewall restricts port 11111 to known client IPs
  • [ ] Container runs with read_only: true, cap_drop: ALL, and no-new-privileges: true
  • [ ] Logs are forwarded to a central system and reviewed for auth failures
  • [ ] FutuOpenD version is pinned (not latest)
  • [ ] Separate keys used for dev and production
  • [ ] Docker data volume (futuopend-data) is backed up and secured

This project is an unofficial community packaging. Not affiliated with, endorsed by, or supported by Futu Securities or moomoo. All trademarks belong to their respective owners.