Skip to content

Security: shing1211/futuopend-deploy

Security

docs/security.md

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. Use MD5 for passwords, mount keys as files, pass nothing in plaintext.
  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

Your password — hash it, never store it plaintext

The <login_pwd_md5> tag takes a one-way MD5 hash. Your actual password never touches the disk.

Generate it:

# Linux
echo -n "your_password" | md5sum | cut -d' ' -f1

# macOS
echo -n "your_password" | md5 -r

# Python (works everywhere)
python3 -c "import hashlib; print(hashlib.md5(b'your_password').hexdigest())"

The -n suppresses the trailing newline. Omit it and the hash is wrong.

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 — treat it like a secret

It contains your account ID and credential paths. Lock it down:

chmod 600 secrets/FutuOpenD.xml

In production, pull it from a secrets manager — HashiCorp Vault, AWS Secrets Manager. Don't leave it sitting on a filesystem longer than needed.


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 is not committed to version control
  • 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
  • Password hash is not reused across environments

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.

There aren't any published security advisories