From 36c62726f9a1ae6a7990ff74442cf2e64659d970 Mon Sep 17 00:00:00 2001 From: Konrad Rokicki Date: Fri, 4 Sep 2026 13:35:27 -0400 Subject: [PATCH] Document the HTTPS service proxy and the per-app opt-out Nothing in the docs mentioned that a server can republish running services at a per-job HTTPS subdomain, so an app author testing against a configured server had no explanation for why the URL their job published was not the URL the user opened. Adds an "Access Over HTTPS" section to the Services page covering the rewrite, the Host header an app sees behind the proxy, and the new `service_proxy: false` opt-out for a service that cannot work at a rewritten address. The opt-out carries a caution: it gives up encryption on every hop, including the browser's, so it is not a preference switch. Also documents `service_proxy_domain` for administrators on the Server Configuration page, adds the manifest reference row, and tells end users on the Apps and Jobs page what the per-job address is and that it dies with the service. --- .../docs/authoring/manifest-reference.mdx | 1 + src/content/docs/authoring/server-config.mdx | 18 +++++++++- src/content/docs/authoring/services.mdx | 36 ++++++++++++++++++- src/content/docs/workflows/apps-and-jobs.mdx | 3 ++ 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/content/docs/authoring/manifest-reference.mdx b/src/content/docs/authoring/manifest-reference.mdx index 148011b..7db7f86 100644 --- a/src/content/docs/authoring/manifest-reference.mdx +++ b/src/content/docs/authoring/manifest-reference.mdx @@ -41,6 +41,7 @@ Each runnable defines a single command that users can launch. If a manifest has | `working_dir` | string | no | Where the command runs: `manifest` (the manifest's directory inside the cloned project), `repo` (the cloned project's root), or `work` (the job's work directory). Defaults to `work` for container runnables and `manifest` otherwise (see [Execution Environment](/authoring/execution/#working-directory)) | | `auto_url` | boolean | no | Service runnables only. When `true`, Fileglancer publishes the service URL for you once `$FG_SERVICE_PORT` is accepting connections — bind your service to `$FG_SERVICE_PORT` and you need no URL-writing code (see [Services](/authoring/services/)) | | `service_url_suffix` | string | no | Service runnables with `auto_url`. Text appended to `http://$FG_HOSTNAME:$FG_SERVICE_PORT` when publishing — a path and/or query, e.g. `/?access_token=${FG_SERVICE_TOKEN}` for one-click auth. May contain literal URL text and the placeholders `${FG_SERVICE_TOKEN}`, `${FG_SERVICE_PORT}`, `${FG_HOSTNAME}` (braces required); nothing else (see [Services](/authoring/services/)) | +| `service_proxy` | boolean | no | Service runnables only. Defaults to `true`. Set it to `false` when your service cannot work behind the server's HTTPS proxy and must keep its direct `http://host:port` URL (see [Services](/authoring/services/#opting-out-of-the-proxy)) | | `requirements` | list of strings | no | Additional tool requirements specific to this runnable, merged with manifest-level requirements | A manifest with two runnables: diff --git a/src/content/docs/authoring/server-config.mdx b/src/content/docs/authoring/server-config.mdx index 4ed4bb5..fc7142b 100644 --- a/src/content/docs/authoring/server-config.mdx +++ b/src/content/docs/authoring/server-config.mdx @@ -1,6 +1,6 @@ --- title: Server Configuration -description: App execution settings controlled by the Fileglancer server administrator — adding tools to $PATH, worker environment passthrough, and the unknown-job cutoff — plus where container images are cached. +description: App execution settings controlled by the Fileglancer server administrator — adding tools to $PATH, worker environment passthrough, the unknown-job cutoff, and the HTTPS service proxy — plus where container images are cached. --- import { Aside } from '@astrojs/starlight/components'; @@ -54,6 +54,22 @@ apps: Users can clear a stuck job sooner by cancelling it from the Jobs page. +## Service Proxy (`service_proxy_domain`) + +A service job binds a port on a compute node and publishes `http://host:port`. Reaching that address means plain HTTP, and it only works from a network that can route to the compute nodes directly. Setting `service_proxy_domain` makes Fileglancer republish every running service at a per-job HTTPS subdomain of that zone instead: + +```yaml title="config.yaml" +session_secret_key: "" +apps: + service_proxy_domain: "services.example.org" +``` + +Users then open `https://job-42-k7m2qhxr.services.example.org/...`, and the path and query of the service's own URL — including any access token — are carried across unchanged. The subdomain label is signed with `session_secret_key`, which is required whenever the proxy domain is set, so the per-job addresses cannot be guessed or enumerated. + +Leave `service_proxy_domain` empty to disable it; each service's direct URL is then published unchanged. Turning it on also needs a wildcard DNS record, a wildcard TLS certificate for the zone, and a matching reverse-proxy server block. Those steps are described in `docs/ServiceProxy.md` in the Fileglancer repository. + +Individual apps can decline to be republished with `service_proxy: false` on the runnable — see [Opting out of the proxy](/authoring/services/#opting-out-of-the-proxy). + ## Container Cache Directory By default, Apptainer container images (SIF files) are cached at `~/.fileglancer/apptainer_cache/`. There is no server-level setting for this; users override it individually in the Preferences page under "Container cache directory". A value starting with `~/` is expanded to the user's home directory. diff --git a/src/content/docs/authoring/services.mdx b/src/content/docs/authoring/services.mdx index 8fd5995..ad114f4 100644 --- a/src/content/docs/authoring/services.mdx +++ b/src/content/docs/authoring/services.mdx @@ -26,7 +26,7 @@ From the cluster's perspective, a service is just a long-running batch job. The 1. The user launches a service-type runnable → the job enters PENDING. 2. The cluster picks it up → RUNNING. 3. The service starts and publishes its URL: either Fileglancer writes `SERVICE_URL_PATH` for `auto_url` services, or the service writes that file itself. -4. On the next poll (every few seconds), Fileglancer reads the file and displays the URL in the UI. +4. On the next poll (every few seconds), Fileglancer reads the file and displays the URL in the UI — rewritten to an HTTPS address if the server has a service proxy configured (see [Access Over HTTPS](#access-over-https)). 5. The user clicks **Open Service** → the service opens in a new browser tab. 6. When done, the user clicks **Stop Service** → the job is killed and the URL disappears. @@ -106,6 +106,40 @@ echo "http://$(hostname):${PORT}" > "$SERVICE_URL_PATH" The URL must start with `http://` or `https://`. Fileglancer validates this before displaying it. If the file doesn't exist or contains an invalid URL, no link is shown. +## Access Over HTTPS + +Your service publishes a plain `http://host:port` URL, but that is not always what the user's browser opens. When the Fileglancer server has a service proxy configured, it republishes each running service at a per-job HTTPS subdomain: + +``` +https://job-42-k7m2qhxr.services.example.org/lab?token=abc123 +``` + +The path, query, and fragment of the URL you published are carried across unchanged, so a `service_url_suffix` that splices in `${FG_SERVICE_TOKEN}` keeps working exactly as written. Nothing in your manifest needs to change, and your job cannot tell whether the server does this — always publish the direct URL and let Fileglancer rewrite it. + +Two things are worth knowing when you test an app on a server with the proxy enabled: + +- **Your service sees the proxy subdomain in the `Host` header**, not the compute node's name. Servers that check the request origin on WebSocket connections generally accept this, because the proxy passes `Host` and `Origin` through as the same value. If yours rejects it, look for an allowed-origin or base-URL option rather than turning the check off. +- **A service that pins an absolute URL** — an OAuth callback registered against one fixed host and port, say — may not work at a rewritten address at all. + +### Opting out of the proxy + +If your service genuinely cannot work at a rewritten URL, set `service_proxy: false` on the runnable: + +```yaml title="runnables.yaml" +runnables: + - id: serve + name: Start Server + type: service + service_proxy: false + command: ./serve.sh +``` + +Fileglancer then publishes that runnable's direct `http://host:port` URL even on a server where every other service is republished, and the proxy refuses to serve it. + + + ## Service Lifecycle - **Startup**: with `auto_url`, Fileglancer writes `SERVICE_URL_PATH` once `$FG_SERVICE_PORT` accepts connections. Without `auto_url`, write the URL to `SERVICE_URL_PATH` yourself as soon as the service is ready. Until the file exists, the UI shows "Service is starting up…". diff --git a/src/content/docs/workflows/apps-and-jobs.mdx b/src/content/docs/workflows/apps-and-jobs.mdx index a268e8b..42854a1 100644 --- a/src/content/docs/workflows/apps-and-jobs.mdx +++ b/src/content/docs/workflows/apps-and-jobs.mdx @@ -191,6 +191,9 @@ Some apps define **service** entry points — long-running processes like Jupyte - While a service is pulling a container image or starting, the detail view shows a progress banner - Once the service is ready and has published its URL, a green banner appears with an **Open Service** button that opens the service in a new tab - To stop a service, click **Stop Service** from the detail view or select **Stop Service** from the actions menu in the jobs table +- The address behind **Open Service** is only valid while the service is running. Once you stop it, or its walltime expires, the page reports that the service is no longer available + +On servers configured for it, services open over HTTPS at a per-job address like `https://job-42-k7m2qhxr.services.example.org/`, rather than at the compute node's own hostname. The address is unique to that launch and stops working when the service stops. Ask your administrator whether your server is set up this way; on one that is not, services open over plain HTTP at the compute node instead, which requires a network connection that can reach cluster nodes directly. ## Managing Apps