From 8cef2acfa57c727b74dec11a0b0c37db6e49411a Mon Sep 17 00:00:00 2001 From: cclohmar Date: Tue, 13 Oct 2026 08:12:11 +0000 Subject: [PATCH] feat(service): --cors support + warn when username != opencode - --cors ORIGIN (repeatable) and an interactive CORS prompt; adds --cors to ExecStart - warn when a password is set with a username other than 'opencode' (the web UI always authenticates as opencode) - README: --cors + the 'don't stack two basic-auth layers' note --- README.md | 5 +++++ install-service.sh | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ec4ead6..cb04ddf 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,7 @@ The service step is a **separate program** — `install-service.sh` — so you c ./install-service.sh --password secret # enable OpenCode's HTTP basic auth ./install-service.sh --username admin --password secret ./install-service.sh --no-password # leave the server unsecured +./install-service.sh --cors https://ui.example.com # allow a cross-origin web UI ./install-service.sh --uninstall # stop and remove the service ./install-service.sh --dry-run # show the unit, change nothing ``` @@ -257,6 +258,10 @@ The service step is a **separate program** — `install-service.sh` — so you c **Auth (optional).** OpenCode's server has its own HTTP basic auth — enabled purely by setting `OPENCODE_SERVER_PASSWORD` (username defaults to `opencode`, or set `OPENCODE_SERVER_USERNAME`). The installer offers this interactively, or accept it non-interactively with `--password`. The secret is **never written into the unit**: it goes into a root-only env file (`/etc/opencode/opencode.env`, mode `600`) that the unit pulls in via `EnvironmentFile=`. Use this **or** your proxy's basic auth — they don't conflict. > **The web app is a client.** On first use it shows an **"Add server"** dialog asking for the **server address** and the **password** — the username is *not* asked because it defaults to `opencode`. Enter your server URL and the password you set here. It's remembered after that. Keep the username as `opencode` (a custom one would not be sent by the browser). +> +> **Don't stack two basic-auth layers.** A browser can send only one `Authorization` header. If your reverse proxy already does basic auth (Zoraxy, nginx…), **leave OpenCode's password unset** and leave the dialog's password **blank** — otherwise the client's OpenCode credentials overwrite the browser's proxy credentials and the proxy rejects them. Choose one layer, not both. +> +> If the web UI is served from a **different origin** than the server, start the server with the matching `--cors `. It refuses to run non-interactively unless you pass `--yes`, and backs up any existing unit before replacing it. diff --git a/install-service.sh b/install-service.sh index fc09a21..1b0cb2d 100755 --- a/install-service.sh +++ b/install-service.sh @@ -94,6 +94,7 @@ is_systemd() { # --------------------------------------------------------------------------- HOST=""; PORT=""; WORKDIR=""; SVC_USER=""; SVC_GROUP="" ENABLE="" # 1 | 0 | empty=ask +CORS="" # space-separated extra allowed origins SVC_USERNAME="${OPENCODE_SERVER_USERNAME:-opencode}" PASSWORD="${OPENCODE_SERVER_PASSWORD:-}" PASSWORD_SET=0 @@ -119,6 +120,8 @@ Options: --user USER run the service as USER (default: you) --enable start and enable on boot --no-enable install only; do not start + --cors ORIGIN allow this origin (repeatable); use if the web UI is + served from a different origin than the server --password PW enable basic auth with this password --username USER basic-auth username (default: opencode) --no-password do not set a password (server unsecured) @@ -153,6 +156,8 @@ while [ $# -gt 0 ]; do --env-file=*) ENV_FILE="${1#*=}" ;; --enable) ENABLE=1 ;; --no-enable) ENABLE=0 ;; + --cors) [ $# -ge 2 ] || die "--cors needs an origin"; CORS="${CORS} $2"; shift ;; + --cors=*) CORS="${CORS} ${1#*=}" ;; -y|--yes) ASSUME_YES=1 ;; --uninstall) UNINSTALL=1 ;; --dry-run) DRY_RUN=1 ;; @@ -224,6 +229,9 @@ if [ "$INTERACTIVE" = 1 ] && [ "$DRY_RUN" = 0 ]; then printf " Start now and enable on boot? [Y/n]: "; v=""; tty_read v case "$v" in [Nn]*) ENABLE=0 ;; *) ENABLE=1 ;; esac + printf " Extra allowed origins (CORS), space-separated [none]: "; v=""; tty_read v + [ -n "$v" ] && CORS="${CORS} $v" + if [ "$PASSWORD_SET" = 0 ]; then printf " Protect the server with a password? [y/N]: "; v=""; tty_read v case "$v" in @@ -253,6 +261,9 @@ if [ "$PASSWORD_SET" = 1 ]; then case "$SVC_USERNAME" in *$'\n'*) SVC_USERNAME="opencode" ;; esac + if [ "$SVC_USERNAME" != "opencode" ]; then + warn "Username '${SVC_USERNAME}' set — the OpenCode web UI always authenticates as 'opencode', so the browser login will fail unless you keep username 'opencode'." + fi fi # --------------------------------------------------------------------------- @@ -261,6 +272,10 @@ fi ENV_LINE="" [ "$PASSWORD_SET" = 1 ] && ENV_LINE="EnvironmentFile=-${ENV_FILE}" +# Build the optional --cors flags +CORS_ARGS="" +for o in $CORS; do CORS_ARGS="${CORS_ARGS} --cors ${o}"; done + UNIT="$(mktemp 2>/dev/null || mktemp -t oac-unit)" cat > "$UNIT" <