HH-620: expose SSClash Web UI on port 9091
Docker image / Test (pull_request) Successful in 36s
Docker image / Build and publish (pull_request) Successful in 2m19s

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
2026-08-24 16:12:38 +08:00
co-authored by multica-agent
parent 39f1841bc8
commit 8d6e7c0098
5 changed files with 18 additions and 10 deletions
+2
View File
@@ -1,5 +1,7 @@
IMAGE_NAME=mohomo-docker:local
CONTAINER_NAME=mohomo-docker
SUBSCRIPTION_URL=https://subscription.example.invalid/mihomo
WEB_BIND=0.0.0.0
WEB_PORT=9091
PROXY_BIND=0.0.0.0
PROXY_PORT=7890
+2 -2
View File
@@ -77,12 +77,12 @@ COPY config/config.yaml /usr/local/share/ssclash/config.yaml
ENV SSCLASH_ROOT=/opt/clash \
SSCLASH_TMP=/tmp/ssclash \
SSCLASH_PLATFORM=linux \
SSCLASH_ADDR=127.0.0.1:9091 \
SSCLASH_ADDR=0.0.0.0:9091 \
SAFE_PATHS=/usr/local/share/ssclash
USER ssclash
VOLUME ["/opt/clash"]
EXPOSE 7890/tcp 7890/udp
EXPOSE 9091/tcp 7890/tcp 7890/udp
HEALTHCHECK --interval=15s --timeout=5s --start-period=20s --retries=4 \
CMD curl --fail --silent --show-error http://127.0.0.1:9090/version >/dev/null
ENTRYPOINT ["/usr/local/bin/bootstrap"]
+4 -4
View File
@@ -1,6 +1,6 @@
# mohomo-docker
Minimal Mihomo service with the ACL4SSR `Online Full MultiMode` routing model. The host exposes only mixed proxy port `7890`; SSClash and Mihomo's controller remain loopback-only inside the container.
Minimal Mihomo service with the ACL4SSR `Online Full MultiMode` routing model. The host exposes the SSClash Web UI on port `9091` and the mixed proxy on port `7890`; Mihomo's controller remains private to the container.
## Quick start
@@ -11,14 +11,14 @@ docker compose up -d --build
docker compose logs -f ssclash
```
The subscription endpoint must return a Clash/Mihomo proxy-provider YAML document (`proxies:`). Use an HTTPS endpoint when its URL contains a credential. Clients connect to either endpoint:
The subscription endpoint must return a Clash/Mihomo proxy-provider YAML document (`proxies:`). Use an HTTPS endpoint when its URL contains a credential. Open `http://<server>:9091` to manage SSClash. Proxy clients connect to either endpoint:
```text
HTTP proxy: http://<server>:7890
SOCKS5 proxy: socks5://<server>:7890
```
`PROXY_BIND` and `PROXY_PORT` are optional deployment overrides. Configure Mihomo proxy authentication before publishing port `7890` outside a trusted network.
`WEB_BIND`, `WEB_PORT`, `PROXY_BIND`, and `PROXY_PORT` are optional deployment overrides; both services bind all host interfaces by default. Set the SSClash administrator password and place the Web UI behind HTTPS and additional access control before exposing it to the Internet. Configure Mihomo proxy authentication before publishing port `7890` outside a trusted network.
## Update and secret handling
@@ -55,7 +55,7 @@ The GitHub Actions workflow builds `linux/amd64`, runs tests first, publishes on
./tests/container-smoke.sh
```
The unit suite checks atomic rollback, URL redaction, server-only listeners, local ACL4SSR providers, and at least 65% bootstrap coverage. The container smoke test builds the image, validates the generated configuration, checks that only port `7890` is published, and verifies that the subscription credential is neither persisted nor logged.
The unit suite checks atomic rollback, URL redaction, server-only listeners, local ACL4SSR providers, and at least 65% bootstrap coverage. The container smoke test builds the image, validates the generated configuration, reaches the Web UI through its published port, checks that only ports `7890` and `9091` are published, and verifies that the subscription credential is neither persisted nor logged.
## License boundary
+1
View File
@@ -9,6 +9,7 @@ services:
environment:
SUBSCRIPTION_URL: ${SUBSCRIPTION_URL:?set SUBSCRIPTION_URL in .env}
ports:
- "${WEB_BIND:-0.0.0.0}:${WEB_PORT:-9091}:9091/tcp"
- "${PROXY_BIND:-0.0.0.0}:${PROXY_PORT:-7890}:7890/tcp"
- "${PROXY_BIND:-0.0.0.0}:${PROXY_PORT:-7890}:7890/udp"
volumes:
+9 -4
View File
@@ -67,6 +67,7 @@ docker run --detach \
--volume "$volume:/opt/clash" \
--publish 127.0.0.1::7890/tcp \
--publish 127.0.0.1::7890/udp \
--publish 127.0.0.1::9091/tcp \
"$image" >/dev/null
attempt=0
@@ -81,11 +82,15 @@ until [ "$(docker inspect --format '{{.State.Health.Status}}' "$container")" = h
done
published=$(docker port "$container")
printf '%s\n' "$published" | grep -E '^7890/(tcp|udp)' >/dev/null
if printf '%s\n' "$published" | grep -vE '^7890/(tcp|udp)' >/dev/null; then
echo "container published a port other than 7890" >&2
for port in 7890/tcp 7890/udp 9091/tcp; do
printf '%s\n' "$published" | grep -F "$port ->" >/dev/null
done
if printf '%s\n' "$published" | grep -vE '^(7890/(tcp|udp)|9091/tcp)' >/dev/null; then
echo "container published a port other than 7890 or 9091" >&2
exit 1
fi
web_port=$(docker port "$container" 9091/tcp | awk -F: 'NR == 1 { print $NF }')
curl --fail --silent --show-error "http://127.0.0.1:${web_port}/" >/dev/null
docker exec "$container" grep -Fx 'OPERATING_MODE=server' /opt/clash/.ssclash/settings >/dev/null
docker exec "$container" grep -Fx 'PROXY_MODE=none' /opt/clash/.ssclash/settings >/dev/null
@@ -104,4 +109,4 @@ if docker logs "$container" 2>&1 | grep -F "$secret" >/dev/null; then
exit 1
fi
echo "container smoke test passed: only port 7890 published; subscription credential not persisted or logged"
echo "container smoke test passed: only ports 7890 and 9091 published; subscription credential not persisted or logged"