harden runtime config and docs

This commit is contained in:
root 2026-06-27 20:58:07 +00:00
parent c148c1b36b
commit 991a0f62b4
13 changed files with 147 additions and 62 deletions

View File

@ -311,9 +311,13 @@ The example contains the filesystem and FastCGI settings:
BIN_DIRECTORY=/var/cache/uce/work
TMP_UPLOAD_PATH=/var/lib/uce/uploads
SESSION_PATH=/var/lib/uce/sessions
SESSION_COOKIE_SECURE=1
FCGI_SOCKET_PATH=/run/uce/fastcgi.sock
FCGI_SOCKET_MODE=0666
FCGI_PORT=9993
CLI_SOCKET_PATH=/run/uce/cli.sock
CLI_SOCKET_MODE=0600
PRECOMPILE_FILES_IN=
SITE_DIRECTORY=/var/www/html
@ -328,7 +332,7 @@ For nginx deployments, the most important setting is:
- `FCGI_SOCKET_PATH=/run/uce/fastcgi.sock`
That is the Unix socket nginx should use for normal `.uce` requests.
That is the Unix socket nginx should use for normal `.uce` requests. `CLI_SOCKET_PATH` is for local admin/test execution through `scripts/uce-cli`; keep `CLI_SOCKET_MODE=0600` unless a trusted Unix group explicitly needs access.
`FCGI_PORT` is optional if nginx is talking to the Unix socket. Leave it set if you also want a TCP FastCGI listener, or remove it if you want the socket to be the only FastCGI entry point.
@ -350,6 +354,7 @@ Recommended deployment notes:
- keep `HTTP_PORT` bound to localhost only at the firewall or by network policy; nginx should be the public entry point
- keep `BIN_DIRECTORY`, `TMP_UPLOAD_PATH`, and `SESSION_PATH` on writable local storage
- use `SESSION_COOKIE_SECURE=1` for HTTPS-only deployments; leave it `0` only for local/plain-HTTP development
- after editing `/etc/uce/settings.cfg`, restart `uce.service`
### 4. Install and enable the systemd service
@ -362,8 +367,8 @@ scripts/systemd/manage-uce-service.sh setup
That script:
- installs `scripts/systemd/uce.service` as `/etc/systemd/system/uce.service`
- installs `etc/uce/settings.cfg` to `/etc/uce/settings.cfg` if it does not already exist
- installs `scripts/systemd/uce.service` as `/etc/systemd/system/uce.service`, rewriting the repository-root path in the unit to the checkout you ran it from
- installs `etc/uce/settings.cfg` to `/etc/uce/settings.cfg` if it does not already exist, likewise rewriting checkout-root paths
- reloads systemd
- enables the service at boot
- starts the runtime immediately

View File

@ -11,20 +11,20 @@ A typical deployment has four pieces:
3. `uce.service`, a systemd service that builds/starts/restarts the runtime.
4. nginx or Apache as the public HTTP server.
Recommended filesystem layout for a source checkout:
Recommended filesystem layout for a source checkout (replace paths as needed):
```text
/opt/uce/ UCE repository/runtime root
/var/www/html/ public web root served by nginx/Apache
/etc/uce/settings.cfg runtime configuration
/run/uce/fastcgi.sock FastCGI socket used by nginx/Apache
/run/uce/cli.sock local CLI/admin/test socket
/var/cache/uce/work generated source, wasm modules, caches
/var/lib/uce/uploads multipart upload scratch space
/var/lib/uce/sessions session files
/path/to/uce-root UCE repository/runtime root (for example `/opt/uce` or `/Code/uce.openfu.com/uce`)
/var/www/html/ public web root served by nginx/Apache
/etc/uce/settings.cfg runtime configuration
/run/uce/fastcgi.sock FastCGI socket used by nginx/Apache
/run/uce/cli.sock local CLI/admin/test socket
/var/cache/uce/work generated source, wasm modules, caches
/var/lib/uce/uploads multipart upload scratch space
/var/lib/uce/sessions session files
```
For packaged installs, the runtime may live under `/usr/lib/uce` instead of `/opt/uce`. Keep the public web root at `/var/www/html` or another normal web-root path, not under the runtime source tree.
For packaged installs, the runtime may live under `/usr/lib/uce` instead of your checkout root. Keep the public web root at `/var/www/html` or another normal web-root path, not under the runtime source tree.
## Build requirements
@ -62,10 +62,11 @@ apt install -y nginx
apt install -y apache2
```
Build UCE from the repository root:
Build UCE from your repository root:
```bash
cd /opt/uce
repo_root=/path/to/uce-root
cd "$repo_root"
bash scripts/build_core_wasm.sh
bash scripts/build_linux.sh
```
@ -104,7 +105,8 @@ The expected directories are:
Install the WASI SDK:
```bash
cd /opt/uce
repo_root=/path/to/uce-root
cd "$repo_root"
scripts/install_wasi_sdk.sh
scripts/install_wasi_sdk.sh --check-only
```
@ -163,7 +165,7 @@ systemctl restart uce.service
## Runtime configuration
Create `/etc/uce/settings.cfg` from `etc/uce/settings.cfg` and adjust paths if your runtime is not under `/opt/uce`. Replace any checkout-specific paths such as `WASM_CORE_PATH` with `/opt/uce/bin/wasm/core.wasm` or your actual runtime path.
Create `/etc/uce/settings.cfg` from `etc/uce/settings.cfg` and adjust paths to your checkout root. Replace checkout-specific settings such as `WASM_CORE_PATH` with `<UCE_REPO_ROOT>/bin/wasm/core.wasm` or leave it relative to `COMPILER_SYS_PATH` as appropriate.
Minimum useful settings:
@ -171,9 +173,12 @@ Minimum useful settings:
BIN_DIRECTORY=/var/cache/uce/work
TMP_UPLOAD_PATH=/var/lib/uce/uploads
SESSION_PATH=/var/lib/uce/sessions
SESSION_COOKIE_SECURE=1
FCGI_SOCKET_PATH=/run/uce/fastcgi.sock
FCGI_SOCKET_MODE=0666
CLI_SOCKET_PATH=/run/uce/cli.sock
CLI_SOCKET_MODE=0600
SITE_DIRECTORY=/var/www/html
HTTP_DOCUMENT_ROOT=/var/www/html
@ -183,7 +188,7 @@ PROACTIVE_COMPILE_CHECK_INTERVAL=60
WASM_COMPILE_SCRIPT=scripts/compile_wasm_unit
WASM_BACKEND_VERBOSE=0
WASM_CORE_PATH=/opt/uce/bin/wasm/core.wasm
WASM_CORE_PATH=<UCE_REPO_ROOT>/bin/wasm/core.wasm
WASM_MEMORY_LIMIT_BYTES=536870912
WASM_EPOCH_DEADLINE_TICKS=200
WASM_EPOCH_PERIOD_MS=50
@ -199,11 +204,13 @@ WS_BROKER_OUTBOUND_TIMEOUT_SECONDS=30
Important settings:
- `FCGI_SOCKET_PATH` is the Unix socket used for normal `.uce` requests. Set it explicitly and keep this value and the web-server `fastcgi_pass` path identical. The reference config uses `/run/uce/fastcgi.sock`; if you choose `/run/uce.sock`, use it in both places.
- `CLI_SOCKET_PATH` is a local HTTP-over-Unix socket used by `scripts/uce-cli` and test/admin units.
- `CLI_SOCKET_PATH` is a local HTTP-over-Unix socket used by `scripts/uce-cli` and test/admin units. Keep it private (`CLI_SOCKET_MODE=0600`) unless you intentionally delegate admin/test execution to a trusted Unix group (`0660`).
- `FCGI_SOCKET_MODE` and `CLI_SOCKET_MODE` are octal permission modes applied after socket bind. Prefer tightening `FCGI_SOCKET_MODE` to `0660` when nginx/Apache can share a trusted group with the UCE worker.
- `SITE_DIRECTORY` is the public site tree to scan for `.uce` files. Use `/var/www/html` when the web root is outside the runtime tree; relative paths are resolved from the runtime working directory.
- `HTTP_DOCUMENT_ROOT` is the root used by the built-in HTTP/WebSocket listener when it resolves upgrade requests. Set it to the same web root as nginx/Apache.
- `BIN_DIRECTORY` stores generated C++, wasm artifacts, compile output, and runtime caches.
- `TMP_UPLOAD_PATH` and `SESSION_PATH` must be writable by the runtime.
- `SESSION_COOKIE_SECURE=1` adds the `Secure` attribute to UCE-managed session cookies and should be used for HTTPS-only deployments. Leave it `0` only for local/plain-HTTP development.
- `HTTP_PORT` is the built-in HTTP/WebSocket listener used for WebSocket upgrade traffic and direct local probes. Bind/firewall it for local access only; nginx/Apache should be the public entry point.
- `WS_BROKER_OUTBOUND_TIMEOUT_SECONDS` controls how long a forwarded WS message can remain queued in the broker before being dropped (default `30`). Set to `0` to disable the timeout.
- `WASM_COMPILE_SCRIPT` must point to `scripts/compile_wasm_unit` unless you provide an equivalent compiler. Relative paths are resolved from the runtime root/`COMPILER_SYS_PATH`. That script calls `scripts/check_unit_wasm.py` after linking each unit and uses the pinned WASI SDK on every deployment host.
@ -220,11 +227,12 @@ systemctl restart uce.service
For source-checkout deployments, install the provided service helper:
```bash
cd /opt/uce
repo_root=/path/to/uce-root
cd "$repo_root"
scripts/systemd/manage-uce-service.sh setup
```
That helper installs `scripts/systemd/uce.service`, creates runtime directories, enables the service, and starts it.
That helper installs `scripts/systemd/uce.service` from your checked-out tree, rewrites the checked-out repository path into the installed unit and first-time config, creates runtime directories, enables the service, and starts it. Use a custom unit only if you need a nonstandard runtime layout.
Useful commands:
@ -234,7 +242,7 @@ scripts/systemd/manage-uce-service.sh restart
scripts/systemd/manage-uce-service.sh logs 200
```
Equivalent manual systemd service for a source checkout:
Equivalent manual systemd service for a source checkout (`<UCE_REPO>` = checkout root):
```ini
[Unit]
@ -244,14 +252,14 @@ Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=/opt/uce
WorkingDirectory=<UCE_REPO>
RuntimeDirectory=uce
StateDirectory=uce
CacheDirectory=uce
ExecStartPre=/usr/bin/mkdir -p /var/cache/uce/work /var/lib/uce/uploads /var/lib/uce/sessions
ExecStartPre=/usr/bin/rm -f /run/uce/fastcgi.sock
ExecStartPre=/usr/bin/bash /opt/uce/scripts/build_linux.sh
ExecStart=/opt/uce/bin/uce_fastcgi.linux.bin
ExecStartPre=/usr/bin/bash <UCE_REPO>/scripts/build_linux.sh
ExecStart=<UCE_REPO>/bin/uce_fastcgi.linux.bin
ExecStopPost=/usr/bin/rm -f /run/uce/fastcgi.sock
Restart=always
RestartSec=2
@ -584,7 +592,8 @@ curl -i http://127.0.0.1/examples/uce-starter/ -H 'Host: example.com'
Check the local CLI path:
```bash
cd /opt/uce
repo_root=/path/to/uce-root
cd "$repo_root"
scripts/uce-cli /tests/cli.uce action=echo message=hello
scripts/run_cli_tests.sh
```
@ -615,7 +624,7 @@ PY
## Operational footguns
- Keep the FastCGI socket path consistent: `FCGI_SOCKET_PATH` and the web-server `fastcgi_pass` must match exactly. The reference config uses `/run/uce/fastcgi.sock`; if you choose `/run/uce.sock`, use it in both places.
- Keep the public web root separate from the runtime source tree. The examples use `/opt/uce` for runtime files and `/var/www/html` for public files.
- Keep the public web root separate from the runtime source tree. Replace `/opt/uce` with your actual checkout path in local examples (for example `/opt/uce` or `/Code/uce.openfu.com/uce`), and keep public files under `/var/www/html`.
- Set `HTTP_DOCUMENT_ROOT` when the web root is outside the runtime working directory. The built-in HTTP/WebSocket listener resolves upgrade paths from this setting.
- Do not expose `CLI_SOCKET_PATH` or `HTTP_PORT` as public entry points. The public path should be nginx/Apache.
- Do not trust `Script-Filename` request headers from direct HTTP clients. The built-in HTTP listener resolves from `HTTP_DOCUMENT_ROOT` and rejects `..` path segments.

View File

@ -26,10 +26,10 @@ gets invoked*.
```
┌────────────────────────────┐
nginx ──FastCGI──► worker pool (N processes) │ /run/uce.sock
nginx ──FastCGI──► worker pool (N processes) │ $FCGI_SOCKET_PATH (example `/run/uce/fastcgi.sock`)
(port 80 etc.) │ uniform unit renderers │ (FastCGI + CLI)
└─────────────▲──────────────┘
│ forward render (FastCGI, uce.sock)
│ forward render (FastCGI, FCGI_SOCKET_PATH)
browser ──raw HTTP / WS──► ┌────────┴─────────┐
(HTTP_PORT 8080) │ WS broker │ owns HTTP_PORT + every
@ -48,14 +48,14 @@ gets invoked*.
| Process | Owns | Renders units? | Source |
|---|---|---|---|
| **Parent** | nothing; supervises children | no | `main()`, `init_base_process()` |
| **Worker** (×`WORKER_COUNT`) | `FCGI_SOCKET_PATH` (`/run/uce.sock`) + `CLI_SOCKET_PATH` | **yes** — the only processes that run wasm | `listen_for_connections()` |
| **Worker** (×`WORKER_COUNT`) | `FCGI_SOCKET_PATH` (configured socket path; example `/run/uce/fastcgi.sock`) + `CLI_SOCKET_PATH` | **yes** — the only processes that run wasm | `listen_for_connections()` |
| **WS broker** (×1) | `HTTP_PORT` + every live WS connection + `WS_BROKER_SOCKET_PATH` | no — forwards to the pool | `run_ws_broker()` |
| **serve_http dispatcher** (×bind) | one custom-server bind address | no — forwards to the pool | `custom_server_http_dispatcher_loop()` |
| **Proactive compiler** | nothing; pre-compiles units | no | `run_proactive_compiler()` |
**only workers instantiate Wasmtime and run unit code.**
Every connection-owning process (broker, serve_http dispatcher) forwards the
request invocation back to a worker over `/run/uce.sock` using the minimal
request invocation back to a worker via `FCGI_SOCKET_PATH` using the minimal
FastCGI client in [`src/lib/fcgi_forward.h`](../src/lib/fcgi_forward.h). This is
forced by Wasmtime: an `Engine`/`Store` cannot be safely re-created across
`fork()`, and the brokers fork from the parent that already touched the
@ -137,7 +137,7 @@ Each request gets a fresh **workspace** — a per-request wasm instance tree wit
the membrane wired in. `wasm_worker_serve(worker, ctx, entry_unit, handler)`
(`src/wasm/worker.cpp`) is the single entry point for *every* mode:
1. Birth a workspace (CoW-snapshot-based where available).
1. Birth a request-scoped workspace with fresh per-request state. Current production workers instantiate a fresh workspace for each request; any future snapshot/CoW optimization must preserve that request-isolation contract.
2. Resolve `entry_unit` + `handler` to an export; components referenced at
runtime are resolved on demand via the `uce_host_component_resolve` hostcall
(`component_resolve()``__uce_<...>` slot), loading dependency modules
@ -158,8 +158,7 @@ Wasmtime's own trap signals are not escalated into a native fatal signal (see
captures the current `WasmWorkspace*`, but `src/lib/sys.cpp::task()` invokes the
captured callback only in the forked child, before the hostcall stack unwinds in
that child. The parent request may return and destroy its workspace; the child
still has its own copy-on-write stack and its own copy of the per-request wasm
workspace. This means a delayed task callback can run after the spawning request
is forked from the parent and keeps a private request-context copy of the per-request workspace. This means a delayed task callback can run after the spawning request
returns without dereferencing the parent's destroyed workspace. It is still a
callback into the inherited child workspace, not a fresh normal request
workspace; avoid adding host resources to `WasmWorkspace` that are invalid across
@ -182,7 +181,7 @@ otherwise (page) → serve_via_wasm(entry_unit, "render")
The `UCE_*` params are set by whichever broker forwarded the request:
- **Page render**: FastCGI nginx → `/run/uce.sock` directly; no `UCE_*` flags → `render`.
- **Page render**: FastCGI nginx → `FCGI_SOCKET_PATH` directly; no `UCE_*` flags → `render`.
- **CLI**: the CLI socket sets `is_cli`.
- **serve_http**: the custom-server dispatcher sets `UCE_SERVE_HTTP=1` plus
`UCE_SERVE_HTTP_FUNCTION` and rewrites `SCRIPT_FILENAME` to the configured
@ -218,7 +217,7 @@ broker loop:
2. The message rides as `UCE_WS_MESSAGE` (base64) with an **empty STDIN body**
a non-empty STDIN makes the FastCGI transport flush a premature response
before `on_complete` ever runs.
3. Connect to `/run/uce.sock` (non-blocking) and queue the encoded request in
3. Connect to `FCGI_SOCKET_PATH` (non-blocking) and queue the encoded request in
`ws_broker_outbound[fd]` with an enqueue timestamp.
`ws_broker_drain_outbound()` runs after every `process(50)` tick: it finishes
@ -310,8 +309,10 @@ header free-functions are `inline`. The wasm backend exposes only declarations
| Key | Default | Meaning |
|---|---|---|
| `WASM_BACKEND_VERBOSE` | `0` | Emit `X-UCE-Wasm-*` workspace timing headers (benchmark only). |
| `FCGI_SOCKET_PATH` | `/run/uce.sock` | Worker pool FastCGI socket (brokers forward here). |
| `CLI_SOCKET_PATH` | `/run/uce/cli.sock` | Worker CLI socket. |
| `FCGI_SOCKET_PATH` | runtime-configured (`/run/uce/fastcgi.sock` in this doc) | Worker pool FastCGI socket (brokers forward here). |
| `CLI_SOCKET_PATH` | `/run/uce/cli.sock` | Worker CLI/admin socket. Keep private; reference `CLI_SOCKET_MODE` is `0600`. |
| `FCGI_SOCKET_MODE` | `0666` | Permission mode applied to `FCGI_SOCKET_PATH` after bind; set tighter if nginx/Apache can use a trusted group. |
| `CLI_SOCKET_MODE` | `0600` | Permission mode applied to `CLI_SOCKET_PATH`; set `0660` only for a trusted admin group. |
| `HTTP_PORT` | `8080` | Raw HTTP + WebSocket port — owned by the WS broker. |
| `WS_BROKER_SOCKET_PATH` | `/run/uce/ws-broker.sock` | Broker command socket for `ws_*` flushes. |
| `WS_BROKER_OUTBOUND_TIMEOUT_SECONDS` | `30` | Max lifetime in seconds for queued WS broker forwards before drop. |

View File

@ -5,11 +5,15 @@ SESSION_PATH=/var/lib/uce/sessions
# LISTEN ON SOCKETS
FCGI_SOCKET_PATH=/run/uce/fastcgi.sock
# FastCGI is normally consumed by the local web server user/group.
FCGI_SOCKET_MODE=0666
FCGI_PORT=9993
# HTTP-over-Unix command socket for local CLI/control tooling.
# Example: curl --unix-socket /run/uce/cli.sock http://localhost/ping
CLI_SOCKET_PATH=/run/uce/cli.sock
# Keep the CLI/admin socket private by default; set 0660 only for a trusted group.
CLI_SOCKET_MODE=0600
# Built-in HTTP/WebSocket listener used for WebSocket Upgrade requests.
# Keep this behind nginx/Apache on localhost or firewall it from public access.
@ -34,7 +38,7 @@ WASM_COMPILE_SCRIPT=scripts/compile_wasm_unit
# WASM RUNTIME SETTINGS. Unit execution is always routed through wasm.
WASM_BACKEND_VERBOSE=0
WASM_CORE_PATH=/Code/uce.openfu.com/uce/bin/wasm/core.wasm
WASM_CORE_PATH=bin/wasm/core.wasm
WASM_MEMORY_LIMIT_BYTES=536870912
WASM_EPOCH_DEADLINE_TICKS=200
WASM_EPOCH_PERIOD_MS=50
@ -85,6 +89,8 @@ ARCHIVE_MAX_ZIP_ENTRIES=4096
# LIFETIME OF SESSION COOKIES IN SECONDS
SESSION_TIME=2592000
# Set to 1 behind HTTPS-only frontends to add Secure to session cookies.
SESSION_COOKIE_SECURE=0
# DEVELOPER-DEFINED ERROR PAGES (see /doc/index.uce?p=error_pages and
# /doc/index.uce?p=3_blocked_functions). The unit receives the error details in

View File

@ -19,10 +19,23 @@ CONFIG_DEST="/etc/uce/settings.cfg"
action="${1:-setup}"
render_runtime_file() {
local source="$1"
local destination="$2"
local mode="$3"
local tmp
tmp="$(mktemp)"
trap 'rm -f "$tmp"' RETURN
sed "s#/Code/uce.openfu.com/uce#$REPO_ROOT#g" "$source" > "$tmp"
install -D -m "$mode" "$tmp" "$destination"
rm -f "$tmp"
trap - RETURN
}
install_unit() {
install -D -m 0644 "$UNIT_SOURCE" "$UNIT_DEST"
render_runtime_file "$UNIT_SOURCE" "$UNIT_DEST" 0644
if [[ ! -f "$CONFIG_DEST" ]]; then
install -D -m 0644 "$CONFIG_SOURCE" "$CONFIG_DEST"
render_runtime_file "$CONFIG_SOURCE" "$CONFIG_DEST" 0644
fi
systemctl daemon-reload
}

View File

@ -8,9 +8,9 @@ session_name : the name of the session
>session
:content
Deletes the cookie specified by `session_name` and clears the data stored under the current session ID.
Deletes the cookie specified by `session_name`, removes the matching server-side session file, and clears the data stored under the current session ID.
This also empties `context.session_id` and `context.session`.
This also empties `context.session_id`, `context.session_name`, and `context.session`.
:example

View File

@ -11,7 +11,7 @@ return value : the current session ID
:content
Starts a session or reconnects to an existing one.
If the cookie named by `session_name` does not exist, UCE creates it and fills it with a new unique session ID. The function then loads the session data for that ID.
If the cookie named by `session_name` does not exist, UCE creates it and fills it with a new unique session ID. The function then loads the session data for that ID. Session cookies are emitted with `Path=/`, `HttpOnly`, `SameSite=Lax`, and `Secure` when `SESSION_COOKIE_SECURE=1` is set in the runtime config.
After `session_start()` completes, the following `context` fields are populated:

View File

@ -2,7 +2,7 @@
u64 socket_connect(String host, u16 port)
:params
host : host name
host : IPv4 address (for example `127.0.0.1`), as a dotted-quad string
port : port number
return value : the socket handle
@ -12,7 +12,7 @@ return value : the socket handle
:content
Opens a socket connection to the given `host` and `port`.
The returned socket handle is then used with `socket_read()`, `socket_write()`, and `socket_close()`.
The returned socket handle is then used with `socket_read()`, `socket_write()`, and `socket_close()`. Currently this helper accepts IPv4 dotted-quad addresses in socket calls and does not perform hostname resolution.
:example
u64 fd = socket_connect("127.0.0.1", 80);

View File

@ -17,8 +17,9 @@ RENDER(Request& context)
String action = first(context.get["action"], "touch");
String session_id = session_start("uce-site-tests");
String session_path = context.server->config["SESSION_PATH"] + "/" + session_id;
if(action == "destroy")
session_destroy();
session_destroy("uce-site-tests");
else
context.session["suite"] = "http";
@ -56,7 +57,7 @@ RENDER(Request& context)
check("session_id_create()", generated_session.length() >= 16, generated_session);
if(action == "destroy")
check("session_destroy()", session_dump.find("suite") == String::npos, session_dump);
check("session_destroy()", session_dump.find("suite") == String::npos && context.session_id == "" && context.session_name == "" && !file_exists(session_path), session_dump + " path=" + session_path);
else
check("context.session writes", context.session["suite"] == "http", session_dump);

View File

@ -2,7 +2,7 @@
// Minimal FastCGI client used by the connection brokers (the custom HTTP server
// dispatcher and the websocket exec child) to render a request through a normal
// worker on /run/uce.sock instead of rendering wasm in the broker's own forked
// worker on FCGI_SOCKET_PATH instead of rendering wasm in the broker's own forked
// process. Wasmtime cannot be safely re-created across fork, so the broker owns
// the connection but forwards the actual unit invocation to a clean-engine
// worker — the "broker holds connections, units respond like RENDER()" model.

View File

@ -876,7 +876,6 @@ String shell_exec(String cmd)
String shell_escape(String raw)
{
// FIXME
String result = "";
for(auto c : raw)
{
@ -971,8 +970,12 @@ bool path_is_within(String path, String root)
bool mkdir(String path)
{
shell_exec(String("mkdir -p ")+" "+shell_escape(path));
return(true);
if(path == "")
return(false);
std::error_code ec;
if(std::filesystem::exists(path, ec))
return(std::filesystem::is_directory(path, ec));
return(std::filesystem::create_directories(path, ec) || std::filesystem::is_directory(path, ec));
}
bool file_exists(String path)
@ -1605,7 +1608,18 @@ void on_child_exit(int sig)
StringList ls(String dir)
{
return(split(trim(shell_exec("ls -1 "+shell_escape(dir))), "\n"));
StringList entries;
std::error_code ec;
if(!std::filesystem::is_directory(dir, ec))
return(entries);
for(auto const& entry : std::filesystem::directory_iterator(dir, ec))
{
if(ec)
break;
entries.push_back(entry.path().filename().string());
}
std::sort(entries.begin(), entries.end());
return(entries);
}
StringMap make_server_settings()
@ -1622,8 +1636,10 @@ StringMap make_server_settings()
cfg["SETUP_TEMPLATE"] = "scripts/setup.h.template";
cfg["LIT_ESC"] = "3d5b5_1";
cfg["CONTENT_TYPE"] = "text/html; charset=utf-8";
cfg["FCGI_SOCKET_PATH"] = "/run/uce.sock";
cfg["FCGI_SOCKET_PATH"] = "/run/uce/fastcgi.sock";
cfg["FCGI_SOCKET_MODE"] = "0666";
cfg["CLI_SOCKET_PATH"] = "/run/uce/cli.sock";
cfg["CLI_SOCKET_MODE"] = "0600";
// Command socket the WS broker listens on; workers flush ws_* dispatch
// command batches here at workspace teardown.
cfg["WS_BROKER_SOCKET_PATH"] = "/run/uce/ws-broker.sock";
@ -1632,6 +1648,7 @@ StringMap make_server_settings()
cfg["UCE_HOSTCALL_BLOCKLIST"] = "";
cfg["TMP_UPLOAD_PATH"] = "/tmp/uce/uploads";
cfg["SESSION_PATH"] = "/tmp/uce/sessions";
cfg["SESSION_COOKIE_SECURE"] = "0";
cfg["COMPILER_SYS_PATH"] = ".";
cfg["PRECOMPILE_FILES_IN"] = "";
cfg["SITE_DIRECTORY"] = "site";

View File

@ -843,7 +843,8 @@ String session_start(String session_name)
if(session_id.length() == 0)
{
session_id = session_id_create();
set_cookie(session_name, session_id, time() + int_val(context->server->config["SESSION_TIME"]));
bool secure_cookie = context->server->config["SESSION_COOKIE_SECURE"] == "1";
set_cookie(session_name, session_id, time() + int_val(context->server->config["SESSION_TIME"]), "/", "", secure_cookie, true);
}
context->session_id = session_id;
context->session_name = session_name;
@ -856,12 +857,23 @@ String session_start(String session_name)
void session_destroy(String session_name)
{
if(context->cookies[session_name].length() > 0)
String cookie_session_id = context->cookies[session_name];
String active_session_id = context->session_name == session_name ? context->session_id : "";
String destroy_session_id = active_session_id != "" ? active_session_id : cookie_session_id;
if(cookie_session_id.length() > 0 || active_session_id.length() > 0)
{
set_cookie(session_name, "", time() - int_val(context->server->config["SESSION_TIME"]));
context->session.clear();
save_session_data(context->session_id, context->session);
context->session_id = "";
bool secure_cookie = context->server->config["SESSION_COOKIE_SECURE"] == "1";
set_cookie(session_name, "", time() - int_val(context->server->config["SESSION_TIME"]), "/", "", secure_cookie, true);
String session_path = session_file_path(destroy_session_id);
if(session_path != "")
file_unlink(session_path);
if(active_session_id != "")
{
context->session.clear();
context->session_loaded_hash = session_hash_serialized(session_serialize(context->session));
context->session_id = "";
context->session_name = "";
}
}
}

View File

@ -1353,6 +1353,27 @@ void listen_for_connections()
}
}
mode_t configured_socket_mode(String value, mode_t fallback)
{
value = trim(value);
if(value == "")
return(fallback);
char* end = 0;
long parsed = strtol(value.c_str(), &end, 8);
if(end == value.c_str() || *end != '\0' || parsed < 0 || parsed > 0777)
return(fallback);
return((mode_t)parsed);
}
void chmod_configured_socket(String path, String mode_value, mode_t fallback)
{
if(path == "")
return;
mode_t mode = configured_socket_mode(mode_value, fallback);
if(chmod(path.c_str(), mode) != 0)
fprintf(stderr, "(!) Could not chmod socket %s to %04o: %s\n", path.c_str(), (unsigned int)mode, strerror(errno));
}
void init_base_process()
{
printf("(P) Starting parent server PID:%i\n", getpid());
@ -1369,13 +1390,13 @@ void init_base_process()
if(server_state.config["FCGI_SOCKET_PATH"] != "")
{
server.listen(server_state.config["FCGI_SOCKET_PATH"]);
chmod(server_state.config["FCGI_SOCKET_PATH"].c_str(), S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
chmod_configured_socket(server_state.config["FCGI_SOCKET_PATH"], server_state.config["FCGI_SOCKET_MODE"], 0666);
}
if(server_state.config["CLI_SOCKET_PATH"] != "")
{
server.listen_cli(server_state.config["CLI_SOCKET_PATH"]);
chmod(server_state.config["CLI_SOCKET_PATH"].c_str(), S_IRWXU | S_IRGRP | S_IWGRP);
chmod_configured_socket(server_state.config["CLI_SOCKET_PATH"], server_state.config["CLI_SOCKET_MODE"], 0600);
}
// HTTP_PORT (WebSocket + raw HTTP) is owned by the dedicated WS broker, not