diff --git a/README.md b/README.md
@@ -4,8 +4,8 @@ Your own VLESS + REALITY proxy, with optional Cloudflare WARP egress, in one
container on any Linux box that has Docker.
```
-curl -fsSLO https://raw.githubusercontent.com/sqzer-x/transtation/v1.0.0/install.sh
-sha256sum install.sh # compare against the hash in the v1.0.0 release notes
+curl -fsSLO https://raw.githubusercontent.com/sqzer-x/transtation/v1.0.1/install.sh
+sha256sum install.sh # compare against the hash in the v1.0.1 release notes
less install.sh # please actually read it
sudo sh install.sh
```
@@ -45,7 +45,7 @@ transtation exists because of a narrower set of preferences:
Two-step form above is the recommended one. If you are going to pipe it anyway:
```
-curl -fsSL https://raw.githubusercontent.com/sqzer-x/transtation/v1.0.0/install.sh | sudo sh
+curl -fsSL https://raw.githubusercontent.com/sqzer-x/transtation/v1.0.1/install.sh | sudo sh
```
The installer needs root, refuses anything other than x86_64/aarch64, installs
@@ -59,7 +59,7 @@ If you already run Docker and prefer to do it yourself:
# /opt/transtation/docker-compose.yml
services:
proxy:
- image: ghcr.io/sqzer-x/transtation:v1.0.0
+ image: ghcr.io/sqzer-x/transtation:v1.0.1
container_name: transtation
restart: unless-stopped
ports: ["443:8443"]
@@ -128,7 +128,7 @@ install -Dm600 /dev/stdin /etc/transtation/uri # paste the vless:// link, then
docker run -d --name transtation-client --restart unless-stopped \
-p 127.0.0.1:1080:1080 \
-v /etc/transtation:/etc/transtation:ro \
- ghcr.io/sqzer-x/transtation-client:v1.0.0
+ ghcr.io/sqzer-x/transtation-client:v1.0.1
```
```
@@ -162,7 +162,7 @@ docker run -d --name transtation-client --restart unless-stopped \
-e MODE=tun \
-e DIRECT_SUFFIXES=example.com,example.net \
-v /etc/transtation:/etc/transtation:ro \
- ghcr.io/sqzer-x/transtation-client:v1.0.0
+ ghcr.io/sqzer-x/transtation-client:v1.0.1
```
Exactly three extra flags. `--privileged` is **not** needed and must not be
diff --git a/host/transtation-killswitch b/host/transtation-killswitch
@@ -43,9 +43,18 @@ server_endpoint() {
*) die "cannot parse a host:port out of $URI_FILE" ;;
esac
case "$_h" in
+ *:*)
+ # An IPv6 literal would be spliced into an `ip daddr` rule and nft
+ # would reject the whole ruleset, leaving the host with no killswitch
+ # at all rather than a broken one. transtation is IPv4-only end to end
+ # anyway: the client resolves the server with getent ahostsv4 and the
+ # tun DNS strategy is ipv4_only.
+ die "the server address in $URI_FILE is IPv6 ($_h).
+ transtation is IPv4-only for now, so the killswitch cannot be built for it.
+ Use an IPv4 address or a name with an A record, or set SERVER_IP yourself." ;;
*[!0-9.]*)
- _h=$(getent ahostsv4 "$_h" | awk 'NR==1 {print $1}')
- [ -n "$_h" ] || die "cannot resolve the server address in $URI_FILE"
+ _h=$(getent ahostsv4 "$_h" 2>/dev/null | awk 'NR==1 {print $1}')
+ [ -n "$_h" ] || die "cannot resolve the server address in $URI_FILE to an IPv4 address"
;;
esac
SERVER_IP=${SERVER_IP:-$_h}
diff --git a/install.sh b/install.sh
@@ -15,12 +15,12 @@
# last line. If the download is truncated, nothing runs.
set -eu
-VERSION=${TT_VERSION:-v1.0.0}
+VERSION=${TT_VERSION:-v1.0.1}
IMAGE_SERVER=${TT_IMAGE_SERVER:-ghcr.io/sqzer-x/transtation}
IMAGE_CLIENT=${TT_IMAGE_CLIENT:-ghcr.io/sqzer-x/transtation-client}
# Filled in by the release workflow. A tag is mutable -- the same stolen-token
# compromise we already concede for Xray's .dgst would let someone retag
-# v1.0.0, and the hash-verified installer you read would vouch for nothing.
+# v1.0.1, and the hash-verified installer you read would vouch for nothing.
SERVER_DIGEST=${TT_SERVER_DIGEST:-}
CLIENT_DIGEST=${TT_CLIENT_DIGEST:-}
@@ -408,14 +408,21 @@ uninstall() {
}
main() {
- case "${1:-server}" in
- server) shift 2>/dev/null || true; install_server ;;
- client) shift; install_client "$@" ;;
+ # `shift` with no positional parameters is a special-builtin error, and a
+ # POSIX shell exits on those -- `|| true` does not save you. dash is
+ # /bin/sh on Debian and Ubuntu, so `sh install.sh` with no arguments used
+ # to die here, silently, on the primary target. bash (Arch's /bin/sh) is
+ # forgiving, which is exactly why local testing never saw it.
+ _cmd=${1:-server}
+ if [ $# -gt 0 ]; then shift; fi
+ case "$_cmd" in
+ server) install_server ;;
+ client) install_client "$@" ;;
--uninstall | uninstall) uninstall ;;
-h | --help)
sed -n '2,10p' "$0" 2>/dev/null || say "see https://github.com/sqzer-x/transtation"
;;
- *) die "unknown command: $1 (expected: server, client, --uninstall)" ;;
+ *) die "unknown command: $_cmd (expected: server, client, --uninstall)" ;;
esac
}
diff --git a/test.sh b/test.sh
@@ -105,6 +105,20 @@ no_geodata() {
check "server image default user is not root" image_user_nonroot
check "server image ships no geoip/geosite data" no_geodata
+echo
+echo "== the installer runs under a real POSIX /bin/sh, not just bash =="
+# `shift` with no positional parameters is a special-builtin error and a POSIX
+# shell EXITS on it, `|| true` notwithstanding. dash is /bin/sh on Debian and
+# Ubuntu, so `sh install.sh` with no arguments died there silently while every
+# bash-based test passed. Run the real thing, as a non-root user, in both
+# shells, and require the root message rather than merely a non-zero exit.
+posix_sh_ok() {
+ _o=$(docker run --rm -v "$PWD":/w:ro -u 65534 "$1" sh -c "cd /w && $2 install.sh" 2>&1) || true
+ case "$_o" in *"needs root"*) return 0 ;; *) return 1 ;; esac
+}
+check "install.sh survives dash (Debian, Ubuntu) with no arguments" posix_sh_ok debian:stable-slim dash
+check "install.sh survives busybox ash (Alpine) with no arguments" posix_sh_ok alpine:3.23 sh
+
echo
echo "== client config renders and sing-box accepts it =="
docker run --rm --network none \
|