Ask about HTTPS on a fresh install
A fresh install asks whether to serve HTTPS, with the reasons, before touching anything. --yes keeps the default, --no-https answers it, and an update keeps the saved mode. The prompt reads /dev/tty so `curl | bash` still works; without a terminal the default is explained instead. Also state why a production process is serving plain HTTP (no TLS vars), instead of only giving that hint in development.
This commit is contained in:
@@ -115,7 +115,7 @@ Inspect live data with `bun llm-code-assistant/query-db.ts "<sql>"` - a read-onl
|
|||||||
|
|
||||||
`install.sh` is the only supported install and update path: `curl -fsSL https://url.moduletrace.com/cobble | sudo bash`. It is idempotent, so the same script is the updater, and `--uninstall [--purge]` is the way back out. It installs Docker when missing (Linux), creates the `cobble` network, writes `$COBBLE_HOME/install.conf` and a generated `.env` (mode 600), and starts one `cobble` container. Existing secrets are never regenerated; the database is backed up to `$COBBLE_HOME/backups` before a container is replaced. The default home is `/opt/cobble`, configurable with `--home`, and everything that must survive an update lives in its `data/` directory (`db`, `media`, `computers`).
|
`install.sh` is the only supported install and update path: `curl -fsSL https://url.moduletrace.com/cobble | sudo bash`. It is idempotent, so the same script is the updater, and `--uninstall [--purge]` is the way back out. It installs Docker when missing (Linux), creates the `cobble` network, writes `$COBBLE_HOME/install.conf` and a generated `.env` (mode 600), and starts one `cobble` container. Existing secrets are never regenerated; the database is backed up to `$COBBLE_HOME/backups` before a container is replaced. The default home is `/opt/cobble`, configurable with `--home`, and everything that must survive an update lives in its `data/` directory (`db`, `media`, `computers`).
|
||||||
|
|
||||||
HTTPS is the default. `src/scripts/generate-local-certs.ts` (run inside the app image, since a container's interfaces are not the host's; the host's IPs and name are passed with `--host`) writes a local CA and a 397-day leaf into `$COBBLE_HOME/certs`; the certificate, key and CA are mounted read only into `/app/certs` (never the CA's key) and pointed at by `TLS_CERT_FILE`, `TLS_KEY_FILE` and `TLS_CA_FILE` (process-only envs). `src/utils/resolve-tls-options.ts` turns them into `Bun.serve`'s `tls`; a half-set or unreadable pair stops the process instead of falling back to HTTP, and `isSecureRequest` then sees an `https:` URL so cookies get `Secure`. `GET /ca.crt` (`serve-local-ca.ts`) serves the local CA for devices to trust, and `GET /ca.mobileconfig` (`build-ca-profile.ts`) serves the same CA as an iOS/macOS configuration profile for one-tap install (the trust switch after it is Apple's, not ours). The CA is kept across updates; the leaf is re-issued when the `sans.txt` names change or it is within 30 days of expiring, which a rerun does. `--no-https` removes the mount and envs (for a proxy or tunnel), `--https` turns it back on, `--https-cert`/`--https-key` copy in a real certificate, `--https-host` adds names or addresses, `--https-regen` replaces the CA. The installer and the image's `HEALTHCHECK` use `curl -k` when TLS is on. `bun run gen:certs` generates the same files into `certs/` (gitignored), `bun run dev` uses them when `TLS_*` are unset (development only), and `bun run dev:phone` generates and starts in one command. A TLS port cannot answer plain HTTP, so development also starts a redirect listener one port above (`DEV_HTTP_PORT`) that 308s to the HTTPS page (`startHttpRedirect`, tested by `build-https-redirect-url`); production publishes only the HTTPS port.
|
HTTPS is the default. `src/scripts/generate-local-certs.ts` (run inside the app image, since a container's interfaces are not the host's; the host's IPs and name are passed with `--host`) writes a local CA and a 397-day leaf into `$COBBLE_HOME/certs`; the certificate, key and CA are mounted read only into `/app/certs` (never the CA's key) and pointed at by `TLS_CERT_FILE`, `TLS_KEY_FILE` and `TLS_CA_FILE` (process-only envs). `src/utils/resolve-tls-options.ts` turns them into `Bun.serve`'s `tls`; a half-set or unreadable pair stops the process instead of falling back to HTTP, and `isSecureRequest` then sees an `https:` URL so cookies get `Secure`. `GET /ca.crt` (`serve-local-ca.ts`) serves the local CA for devices to trust, and `GET /ca.mobileconfig` (`build-ca-profile.ts`) serves the same CA as an iOS/macOS configuration profile for one-tap install (the trust switch after it is Apple's, not ours). The CA is kept across updates; the leaf is re-issued when the `sans.txt` names change or it is within 30 days of expiring, which a rerun does. `--no-https` removes the mount and envs (for a proxy or tunnel), `--https` turns it back on, `--https-cert`/`--https-key` copy in a real certificate, `--https-host` adds names or addresses, `--https-regen` replaces the CA. A fresh install asks about HTTPS once, with the reasons, before doing any work (`ask_https`: updates keep the saved mode, `--yes` skips it, any `--https*` flag answers it; it reads `/dev/tty`, never stdin, so `curl | bash` still works, and without a terminal it prints the notice and keeps the default). The installer and the image's `HEALTHCHECK` use `curl -k` when TLS is on. `bun run gen:certs` generates the same files into `certs/` (gitignored), `bun run dev` uses them when `TLS_*` are unset (development only), and `bun run dev:phone` generates and starts in one command. A TLS port cannot answer plain HTTP, so development also starts a redirect listener one port above (`DEV_HTTP_PORT`) that 308s to the HTTPS page (`startHttpRedirect`, tested by `build-https-redirect-url`); production publishes only the HTTPS port.
|
||||||
|
|
||||||
The container runs with `--network cobble`, `-p BIND:PORT`, `.env` at `/app/.env` read only, the host data directory at `/data`, the docker socket at `/var/run/docker.sock`, `MAIN_SERVER=1` and `PORT`, `--init`, a `unless-stopped` restart policy and bounded json-file logs. `/data` must stay a bind mount: `resolveHostPath` turns paths under the app's own mounts into host paths for computer containers, and a named volume would break them. `DATA_DIR=/data` and `DB_DIR=/data/db` come from the Dockerfile; `entrypoint.sh` keeps bun-sqlite's schema record on the same volume.
|
The container runs with `--network cobble`, `-p BIND:PORT`, `.env` at `/app/.env` read only, the host data directory at `/data`, the docker socket at `/var/run/docker.sock`, `MAIN_SERVER=1` and `PORT`, `--init`, a `unless-stopped` restart policy and bounded json-file logs. `/data` must stay a bind mount: `resolveHostPath` turns paths under the app's own mounts into host paths for computer containers, and a named volume would break them. `DATA_DIR=/data` and `DB_DIR=/data/db` come from the Dockerfile; `entrypoint.sh` keeps bun-sqlite's schema record on the same volume.
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,13 @@ missing):
|
|||||||
curl -fsSL https://url.moduletrace.com/cobble | sudo bash
|
curl -fsSL https://url.moduletrace.com/cobble | sudo bash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
HTTPS is on by default, and a fresh install asks before continuing: it
|
||||||
|
encrypts the connection, and phones need it to install the app and receive
|
||||||
|
notifications. The installer creates a local CA for the machine, so each
|
||||||
|
device trusts it once. Answer no, or pass `--no-https`, if a proxy or tunnel
|
||||||
|
already handles HTTPS; `--yes` accepts the default without asking. Updates
|
||||||
|
keep the mode already chosen.
|
||||||
|
|
||||||
Open the address it prints, usually `https://<your-ip>:7227`. The
|
Open the address it prints, usually `https://<your-ip>:7227`. The
|
||||||
certificate is signed by a local CA the installer made, so the browser warns
|
certificate is signed by a local CA the installer made, so the browser warns
|
||||||
until you trust it: open `https://<your-ip>:7227/ca.crt` on the device and
|
until you trust it: open `https://<your-ip>:7227/ca.crt` on the device and
|
||||||
@@ -56,11 +63,13 @@ thing that should reach it (with `--no-https`).
|
|||||||
|
|
||||||
## HTTPS
|
## HTTPS
|
||||||
|
|
||||||
The installer serves HTTPS by default. It generates a local CA and a
|
The installer serves HTTPS by default, and a fresh install asks before
|
||||||
certificate for this machine into `$COBBLE_HOME/certs` and mounts them into
|
continuing (`--yes` skips the question, `--no-https` answers it, an update
|
||||||
the container read only. The CA is kept across updates; the certificate is
|
keeps the saved mode). It generates a local CA and a certificate for this
|
||||||
re-issued when the machine's addresses change or it is within 30 days of
|
machine into `$COBBLE_HOME/certs` and mounts them into the container read
|
||||||
expiring, and a rerun of the updater does both.
|
only. The CA is kept across updates; the certificate is re-issued when the
|
||||||
|
machine's addresses change or it is within 30 days of expiring, and a rerun
|
||||||
|
of the updater does both.
|
||||||
|
|
||||||
Trust the CA once on each device. Until it is trusted the browser warns, and
|
Trust the CA once on each device. Until it is trusted the browser warns, and
|
||||||
the installed app, its service worker and Web Push stay off:
|
the installed app, its service worker and Web Push stay off:
|
||||||
@@ -127,6 +136,7 @@ curl -fsSL https://url.moduletrace.com/cobble | sudo bash -s -- --uninstall --pu
|
|||||||
| `--no-pull` | Use an image that is already on the machine |
|
| `--no-pull` | Use an image that is already on the machine |
|
||||||
| `--skip-backup` | Update without a database backup |
|
| `--skip-backup` | Update without a database backup |
|
||||||
| `--no-docker-install` | Fail instead of installing Docker |
|
| `--no-docker-install` | Fail instead of installing Docker |
|
||||||
|
| `--yes`, `-y` | Accept the defaults without asking (HTTPS on) |
|
||||||
| `--uninstall` | Stop and remove the app container |
|
| `--uninstall` | Stop and remove the app container |
|
||||||
| `--purge` | With `--uninstall`, also delete the data directory |
|
| `--purge` | With `--uninstall`, also delete the data directory |
|
||||||
|
|
||||||
|
|||||||
+66
-2
@@ -77,13 +77,17 @@ Options:
|
|||||||
--force Recreate the container even when it is current
|
--force Recreate the container even when it is current
|
||||||
--skip-backup Do not back up the database before an update
|
--skip-backup Do not back up the database before an update
|
||||||
--no-docker-install Fail instead of installing Docker when it is missing
|
--no-docker-install Fail instead of installing Docker when it is missing
|
||||||
|
--yes, -y Accept the defaults without asking (HTTPS on)
|
||||||
--uninstall Stop and remove the app container
|
--uninstall Stop and remove the app container
|
||||||
--purge With --uninstall, also delete the data directory
|
--purge With --uninstall, also delete the data directory
|
||||||
--help Show this help
|
--help Show this help
|
||||||
|
|
||||||
HTTPS:
|
HTTPS:
|
||||||
It is on by default. A local CA and a certificate for this machine are
|
It is on by default, and a fresh install asks before continuing. It
|
||||||
generated into HOME/certs. Trust the CA on each device first:
|
encrypts the connection, and phones need it to install the app and
|
||||||
|
receive notifications; a proxy or tunnel that already handles HTTPS is
|
||||||
|
the reason to answer no. A local CA and a certificate for this machine
|
||||||
|
are generated into HOME/certs, so each device trusts the CA once:
|
||||||
https://<this machine>:<port>/ca.crt, or HOME/certs/ca.crt.
|
https://<this machine>:<port>/ca.crt, or HOME/certs/ca.crt.
|
||||||
iPhone and iPad install it in one tap from
|
iPhone and iPad install it in one tap from
|
||||||
https://<this machine>:<port>/ca.mobileconfig.
|
https://<this machine>:<port>/ca.mobileconfig.
|
||||||
@@ -144,7 +148,9 @@ esac
|
|||||||
ENV_FILE="$HOME_DIR/.env"
|
ENV_FILE="$HOME_DIR/.env"
|
||||||
STATE_FILE="$HOME_DIR/install.conf"
|
STATE_FILE="$HOME_DIR/install.conf"
|
||||||
|
|
||||||
|
HAD_STATE=0
|
||||||
if [ -f "$STATE_FILE" ]; then
|
if [ -f "$STATE_FILE" ]; then
|
||||||
|
HAD_STATE=1
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
. "$STATE_FILE"
|
. "$STATE_FILE"
|
||||||
fi
|
fi
|
||||||
@@ -170,9 +176,11 @@ SKIP_BACKUP=0
|
|||||||
PURGE=0
|
PURGE=0
|
||||||
HTTPS_DISABLED=0
|
HTTPS_DISABLED=0
|
||||||
HTTPS_FORCE=0
|
HTTPS_FORCE=0
|
||||||
|
HTTPS_EXPLICIT=0
|
||||||
HTTPS_HOSTS_GIVEN=0
|
HTTPS_HOSTS_GIVEN=0
|
||||||
HTTPS_CERT_GIVEN=0
|
HTTPS_CERT_GIVEN=0
|
||||||
HTTPS_REGEN=0
|
HTTPS_REGEN=0
|
||||||
|
ASSUME_YES=0
|
||||||
HTTPS_MANAGED=1
|
HTTPS_MANAGED=1
|
||||||
CERTS_CHANGED=0
|
CERTS_CHANGED=0
|
||||||
CERT_EXPIRY=""
|
CERT_EXPIRY=""
|
||||||
@@ -226,10 +234,12 @@ while [ $# -gt 0 ]; do
|
|||||||
--https)
|
--https)
|
||||||
HTTPS_ENABLED=1
|
HTTPS_ENABLED=1
|
||||||
HTTPS_FORCE=1
|
HTTPS_FORCE=1
|
||||||
|
HTTPS_EXPLICIT=1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--no-https)
|
--no-https)
|
||||||
HTTPS_DISABLED=1
|
HTTPS_DISABLED=1
|
||||||
|
HTTPS_EXPLICIT=1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--https-host)
|
--https-host)
|
||||||
@@ -239,20 +249,28 @@ while [ $# -gt 0 ]; do
|
|||||||
HTTPS_HOSTS="${2:-}"
|
HTTPS_HOSTS="${2:-}"
|
||||||
HTTPS_HOSTS_GIVEN=1
|
HTTPS_HOSTS_GIVEN=1
|
||||||
fi
|
fi
|
||||||
|
HTTPS_EXPLICIT=1
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
--https-cert)
|
--https-cert)
|
||||||
HTTPS_CERT_SOURCE="${2:-}"
|
HTTPS_CERT_SOURCE="${2:-}"
|
||||||
HTTPS_CERT_GIVEN=1
|
HTTPS_CERT_GIVEN=1
|
||||||
|
HTTPS_EXPLICIT=1
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
--https-key)
|
--https-key)
|
||||||
HTTPS_KEY_SOURCE="${2:-}"
|
HTTPS_KEY_SOURCE="${2:-}"
|
||||||
HTTPS_CERT_GIVEN=1
|
HTTPS_CERT_GIVEN=1
|
||||||
|
HTTPS_EXPLICIT=1
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
--https-regen)
|
--https-regen)
|
||||||
HTTPS_REGEN=1
|
HTTPS_REGEN=1
|
||||||
|
HTTPS_EXPLICIT=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--yes | -y)
|
||||||
|
ASSUME_YES=1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--force)
|
--force)
|
||||||
@@ -460,6 +478,50 @@ health_url() {
|
|||||||
printf '%s://%s:%s/api/health' "$(health_scheme)" "$(health_host)" "$PORT"
|
printf '%s://%s:%s/api/health' "$(health_scheme)" "$(health_host)" "$PORT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# A fresh install asks about HTTPS and states why; updates keep the saved
|
||||||
|
# mode, and any --https* flag (or --yes) skips the question. The prompt
|
||||||
|
# reads /dev/tty, never stdin: with `curl | bash` stdin is the script.
|
||||||
|
ask_https() {
|
||||||
|
local answer=""
|
||||||
|
|
||||||
|
if [ "$HAD_STATE" = 1 ] || [ "$HTTPS_EXPLICIT" = 1 ] || [ "$ACTION" = "uninstall" ] || [ "$ASSUME_YES" = 1 ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! { : </dev/tty; } 2>/dev/null; then
|
||||||
|
printf '\nHTTPS is on by default: it encrypts the connection, and phones need it to install the app and receive notifications. Each device trusts the local certificate once. Pass --no-https if a proxy or tunnel already handles HTTPS.\n\n' >&2
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '\n' >&2
|
||||||
|
printf 'Cobble can serve HTTPS, and that is the default.\n' >&2
|
||||||
|
printf 'It encrypts the connection, and phones need it to install the app\n' >&2
|
||||||
|
printf 'and receive notifications. The installer creates a local CA for\n' >&2
|
||||||
|
printf 'this machine, so each device trusts it once (on an iPhone: install\n' >&2
|
||||||
|
printf 'the profile, then turn it on under Certificate Trust Settings).\n' >&2
|
||||||
|
printf '\n' >&2
|
||||||
|
printf 'Answer no if a proxy or tunnel already handles HTTPS for Cobble.\n' >&2
|
||||||
|
printf 'Serve HTTPS? [Y/n] ' >&2
|
||||||
|
|
||||||
|
exec 3</dev/tty
|
||||||
|
read -r answer <&3 || answer=""
|
||||||
|
exec 3<&-
|
||||||
|
|
||||||
|
case "$answer" in
|
||||||
|
[nN]*)
|
||||||
|
HTTPS_ENABLED=0
|
||||||
|
HTTPS_HOSTS=""
|
||||||
|
HTTPS_CERT_SOURCE=""
|
||||||
|
HTTPS_KEY_SOURCE=""
|
||||||
|
HTTPS_MANAGED=1
|
||||||
|
printf '\nPlain HTTP. Put HTTPS in front before using it outside this network.\n' >&2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf '\nHTTPS on. Trust the certificate on each device before using the app.\n' >&2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
set_env() {
|
set_env() {
|
||||||
local key="$1" value="$2" escaped
|
local key="$1" value="$2" escaped
|
||||||
escaped="$(printf '%s' "$value" | sed -e 's/[\/&]/\\&/g')"
|
escaped="$(printf '%s' "$value" | sed -e 's/[\/&]/\\&/g')"
|
||||||
@@ -862,6 +924,8 @@ uninstall() {
|
|||||||
|
|
||||||
command -v curl >/dev/null 2>&1 || fail "curl is required"
|
command -v curl >/dev/null 2>&1 || fail "curl is required"
|
||||||
|
|
||||||
|
ask_https
|
||||||
|
|
||||||
ensure_docker
|
ensure_docker
|
||||||
|
|
||||||
if [ "$ACTION" = "uninstall" ]; then
|
if [ "$ACTION" = "uninstall" ]; then
|
||||||
|
|||||||
+4
-2
@@ -86,9 +86,11 @@ if (
|
|||||||
bunext.bunextLog.info(`Using the development certificate in certs/`);
|
bunext.bunextLog.info(`Using the development certificate in certs/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cert_file && !key_file && process.env.NODE_ENV != "production") {
|
if (!cert_file && !key_file) {
|
||||||
bunext.bunextLog.info(
|
bunext.bunextLog.info(
|
||||||
"Serving plain HTTP (run `bun run dev:phone`, or `bun run gen:certs` then restart, for HTTPS)",
|
process.env.NODE_ENV == "production"
|
||||||
|
? "Serving plain HTTP (set TLS_CERT_FILE and TLS_KEY_FILE for HTTPS)"
|
||||||
|
: "Serving plain HTTP (run `bun run dev:phone`, or `bun run gen:certs` then restart, for HTTPS)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user