v0.24.0 cli long and short options, http sink viewer and new logo
This commit is contained in:
1 parent
bf410e1225
commit
9e86d7999d
42 files changed
+786
-404
No files matched your search
+1
-1
@@ -97,7 +97,7 @@ Append a line and watch it arrive:
|
||||
> curl -N http://127.0.0.1:$PORT_HTTP/stream
|
||||
> echo hello >>\$APP/app.log
|
||||
The secured sink (bearer keeps the token off argv):
|
||||
> token=\$(\$LW auth token -url https://127.0.0.1:$PORT_S_HTTP -user viewer-01 -password-file \$PW -ca-file \$CA)
|
||||
> token=\$(\$LW auth token --url https://127.0.0.1:$PORT_S_HTTP --user viewer-01 --password-file \$PW --ca-file \$CA)
|
||||
> curl -N --cacert \$CA -H @<(bearer) https://127.0.0.1:$PORT_S_HTTP/stream
|
||||
Logs: $(short "$LOG")/
|
||||
EOF
|
||||
|
||||
+4
-4
@@ -168,9 +168,9 @@ Ports (relay on ::1, TLS with SAN IP ::1, scram):
|
||||
Shell setup (LW, CA, PW: viewer-01's password file):
|
||||
> . $(short "$RUN")/env
|
||||
Read the tcp sink as viewer-01:
|
||||
> \$LW auth stream -addr [::1]:$PORT_TCP_SINK -user viewer-01 -password-file \$PW -ca-file \$CA
|
||||
> \$LW auth stream --addr [::1]:$PORT_TCP_SINK --user viewer-01 --password-file \$PW --ca-file \$CA
|
||||
Read the http sink (bearer keeps the token off argv; -g leaves the brackets to the URL):
|
||||
> token=\$(\$LW auth token -url https://[::1]:$PORT_HTTP_SINK -user viewer-01 -password-file \$PW -ca-file \$CA)
|
||||
> token=\$(\$LW auth token --url https://[::1]:$PORT_HTTP_SINK --user viewer-01 --password-file \$PW --ca-file \$CA)
|
||||
> curl -gN --cacert \$CA -H @<(bearer) https://[::1]:$PORT_HTTP_SINK/stream
|
||||
Which listener answers on $PORT_FAMILY:
|
||||
> curl -s http://127.0.0.1:$PORT_FAMILY/status; curl -gs http://[::1]:$PORT_FAMILY/status
|
||||
@@ -203,7 +203,7 @@ check "relay logged its listener as [::1]:$PORT_TCP_CHAIN ($n)" $((n >= 1))
|
||||
# 2. HTTP sink: lw auth token at an IPv6 URL, the token carried by curl -g
|
||||
token="$("$BIN" auth token -url "https://[::1]:$PORT_HTTP_SINK" -user viewer-01 \
|
||||
-password-file "$AUTH/viewer-01.pass" -ca-file "$PKI/ca.crt" 2>>"$LOG/auth-cli.out")"
|
||||
check "http sink: lw auth token -url https://[::1]:$PORT_HTTP_SINK issued a token" $(is_set "$token")
|
||||
check "http sink: lw auth token --url https://[::1]:$PORT_HTTP_SINK issued a token" $(is_set "$token")
|
||||
|
||||
code="$(curl "${curl_args[@]}" -o /dev/null -w '%{http_code}' --cacert "$PKI/ca.crt" \
|
||||
-H @<(printf 'Authorization: Bearer %s\n' "$token") "https://[::1]:$PORT_HTTP_SINK/status" 2>/dev/null || true)"
|
||||
@@ -220,7 +220,7 @@ timeout 4 "$BIN" auth stream -addr "[::1]:$PORT_TCP_SINK" -user viewer-01 \
|
||||
-password-file "$AUTH/viewer-01.pass" -ca-file "$PKI/ca.crt" \
|
||||
>"$RUN/stream.out" 2>"$LOG/stream.err"
|
||||
n=$(grep -c 'edge-01/' "$RUN/stream.out")
|
||||
check "tcp sink: lw auth stream -addr [::1]:$PORT_TCP_SINK streamed entries ($n lines)" $((n >= 1))
|
||||
check "tcp sink: lw auth stream --addr [::1]:$PORT_TCP_SINK streamed entries ($n lines)" $((n >= 1))
|
||||
|
||||
section "Scenario 2: each listener keeps to its address family"
|
||||
|
||||
|
||||
+12
-2
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
# logwisp preset test: colors, presets, --dump, lw tls, listeners that make
|
||||
# their certificate (self-signed and pinned, or issued), and the http and tcp
|
||||
# sinks writing a finite input whole. One-shot: both modes run the checks.
|
||||
# their certificate (self-signed and pinned, or issued), the serve preset's
|
||||
# browser viewer, and the http and tcp sinks writing a finite input whole.
|
||||
# One-shot: both modes run the checks.
|
||||
# Ports: 15890-15896 on 127.0.0.1.
|
||||
# Requires: bash 5+, coreutils, curl, openssl. Linux dev host only.
|
||||
|
||||
@@ -85,6 +86,15 @@ check "curl --pinnedpubkey reads a self-signed stream's status" \
|
||||
"$(curl -sk --pinnedpubkey "$SERVE_PIN" https://127.0.0.1:15895/status | grep -q '"tls":true' && echo 1 || echo 0)"
|
||||
curl -sk --pinnedpubkey "$PIN" -o /dev/null https://127.0.0.1:15895/status
|
||||
check "curl with another listener's pin is refused" "$(( $? != 0 ))"
|
||||
check "a browser at the self-signed root lands on the viewer" \
|
||||
"$(curl -sLk --pinnedpubkey "$SERVE_PIN" https://127.0.0.1:15895/ | grep -q 'name="logwisp-login" content="none"' && echo 1 || echo 0)"
|
||||
|
||||
section "Viewer"
|
||||
spawn serve-plain "$BIN" --preset "serve,path=$RUN/edge.log,listen=127.0.0.1:15896"
|
||||
wait_port 15896 || abort "the plain serve preset did not listen" serve-plain
|
||||
root=$(curl -s -o /dev/null -w '%{http_code} %{redirect_url}' http://127.0.0.1:15896/)
|
||||
check "GET / sends a browser to the viewer, which needs no login over plain http ($root)" \
|
||||
"$( [[ $root == '303 http://127.0.0.1:15896/auth/view' ]] && curl -sL http://127.0.0.1:15896/ | grep -q 'name="logwisp-login" content="none"' && echo 1 || echo 0)"
|
||||
|
||||
section "Flush at exit"
|
||||
# feed SECONDS PORT: the lines, once a client had time to connect
|
||||
|
||||
@@ -235,12 +235,12 @@ edge_unknown and edge_noauth are refused on $PORT_TCP_CHAIN. On $PORT_BOUND edge
|
||||
Shell setup (LW, CA, PW: viewer-01's password file, USERS, RELAY: its pid):
|
||||
> . $(short "$RUN")/env
|
||||
Read the tcp sink as viewer-01:
|
||||
> \$LW auth stream -addr 127.0.0.1:$PORT_TCP_SINK -user viewer-01 -password-file \$PW -ca-file \$CA
|
||||
> \$LW auth stream --addr 127.0.0.1:$PORT_TCP_SINK --user viewer-01 --password-file \$PW --ca-file \$CA
|
||||
Read the http sink (bearer keeps the token off argv):
|
||||
> token=\$(\$LW auth token -url https://127.0.0.1:$PORT_HTTP_SINK -user viewer-01 -password-file \$PW -ca-file \$CA)
|
||||
> token=\$(\$LW auth token --url https://127.0.0.1:$PORT_HTTP_SINK --user viewer-01 --password-file \$PW --ca-file \$CA)
|
||||
> curl -N --cacert \$CA -H @<(bearer) https://127.0.0.1:$PORT_HTTP_SINK/stream
|
||||
Revoke viewer-01:
|
||||
> \$LW auth remove-user -credentials \$USERS -user viewer-01 && kill -HUP \$RELAY
|
||||
> \$LW auth remove-user --credentials \$USERS --user viewer-01 && kill -HUP \$RELAY
|
||||
Ingested entries (node label forced to the username): $(short "$OUT")/
|
||||
Passwords: $(short "$AUTH")/ Logs: $(short "$LOG")/
|
||||
EOF
|
||||
|
||||
+44
-11
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
# logwisp browser login test: an http sink in proxy mode behind a TLS-terminating
|
||||
# reverse proxy that mounts it under /logs/; headless Chromium signs in through
|
||||
# the shipped pages, with cookies and without (token mode); the CLI unbound.
|
||||
# the shipped pages, with cookies and without (token mode); the CLI unbound; an
|
||||
# open sink's viewer streams with no login on a page the browser holds insecure.
|
||||
# Requires: bash 5+, go, openssl, curl; node with playwright and both builds
|
||||
# `playwright install chromium` fetches, or the browser checks skip.
|
||||
|
||||
@@ -15,13 +16,14 @@ PROXY_SRC=$RUN/proxy
|
||||
USERS=$AUTH/users.toml
|
||||
PORT_PROXY=15831 # the site: TLS ends here, logwisp is under /logs/
|
||||
PORT_SINK=15832 # logwisp http sink, plaintext, only for 127.0.0.2
|
||||
PORT_OPEN=15833 # logwisp http sink without auth
|
||||
PROXY_ADDR=127.0.0.2
|
||||
SITE="https://127.0.0.1:$PORT_PROXY"
|
||||
e2e_init "$@"
|
||||
|
||||
section "Setup"
|
||||
need go openssl curl
|
||||
ports_free $PORT_PROXY $PORT_SINK
|
||||
ports_free $PORT_PROXY $PORT_SINK $PORT_OPEN
|
||||
# The launch each scenario makes: the module resolves without its browsers, and
|
||||
# chromium.executablePath() names full Chromium, not the default headless shell
|
||||
launches() { timeout 60 node -e 'require("playwright").chromium.launch(JSON.parse(process.argv[1])).then((b) => b.close())' "$1" >/dev/null 2>&1; }
|
||||
@@ -118,6 +120,13 @@ viewer_page = true
|
||||
type = "scram"
|
||||
credentials_file = "$USERS"
|
||||
trusted_proxies = ["$PROXY_ADDR"]
|
||||
|
||||
[[pipelines.plugin_sinks]]
|
||||
id = "open"
|
||||
type = "http"
|
||||
[pipelines.plugin_sinks.config]
|
||||
host = "127.0.0.1"
|
||||
port = $PORT_OPEN
|
||||
EOF
|
||||
|
||||
cat >"$RUN/browser.cjs" <<'EOF'
|
||||
@@ -147,7 +156,7 @@ const inlineLogin = (page) => page.waitForFunction(() =>
|
||||
|
||||
const scenarios = {
|
||||
async cookie(context, page) {
|
||||
await page.goto(base + "auth/view");
|
||||
await page.goto(base);
|
||||
await page.waitForURL(/\/auth\/login\?next=view%23signed-in$/, { timeout: 10000 });
|
||||
out.redirected_to_login = 1;
|
||||
await signIn(page, "not-the-password");
|
||||
@@ -235,6 +244,16 @@ const scenarios = {
|
||||
await inlineLogin(page);
|
||||
out.expired_asks_again = 1;
|
||||
},
|
||||
|
||||
// base names 127.0.0.1 as viewer.test, which the browser holds insecure
|
||||
async open(context, page) {
|
||||
await page.goto(base);
|
||||
await page.waitForURL(/\/auth\/view$/, { timeout: 10000 });
|
||||
await linesAtLeast(page, 3);
|
||||
out.events = await lines(page);
|
||||
out.secure = await page.evaluate(() => isSecureContext);
|
||||
out.sign_in_ui = await page.evaluate(() => !document.getElementById("logout").hidden || !document.getElementById("login").hidden);
|
||||
},
|
||||
};
|
||||
|
||||
(async () => {
|
||||
@@ -244,7 +263,8 @@ const scenarios = {
|
||||
// Full Chromium: the headless shell ignores a profile's Preferences
|
||||
context = await chromium.launchPersistentContext(profile, { ignoreHTTPSErrors: true, channel: "chromium" });
|
||||
} else {
|
||||
context = await (await chromium.launch()).newContext({ ignoreHTTPSErrors: true });
|
||||
const args = mode === "open" ? ["--host-resolver-rules=MAP viewer.test 127.0.0.1"] : [];
|
||||
context = await (await chromium.launch({ args })).newContext({ ignoreHTTPSErrors: true });
|
||||
}
|
||||
const page = await context.newPage();
|
||||
page.on("console", (m) => { if (/Content Security Policy/i.test(m.text())) out.csp_violations++; });
|
||||
@@ -266,6 +286,7 @@ spawn proxy "$PROXY_SRC/proxy" "127.0.0.1:$PORT_PROXY" "http://127.0.0.1:$PORT_S
|
||||
"$PKI/site.crt" "$PKI/site.key"
|
||||
wait_port $PORT_SINK || abort "logwisp port $PORT_SINK is not listening" logwisp
|
||||
wait_port $PORT_PROXY || abort "proxy port $PORT_PROXY is not listening" proxy
|
||||
wait_port $PORT_OPEN || abort "logwisp port $PORT_OPEN is not listening" logwisp
|
||||
daemons_up
|
||||
write_env LW="$BIN" CA="$PKI/ca.crt" PW="$AUTH/viewer-01.pass" SITE="$SITE"
|
||||
|
||||
@@ -273,13 +294,14 @@ guide "logwisp browser login test" <<EOF
|
||||
Ports:
|
||||
$PORT_PROXY the site: TLS-terminating reverse proxy, logwisp under /logs/
|
||||
$PORT_SINK logwisp http sink, plaintext, trusted_proxies = ["$PROXY_ADDR"]
|
||||
$PORT_OPEN logwisp http sink without auth: its viewer at the root
|
||||
In a browser trusting $(short "$PKI/ca.crt"), sign in as viewer-01 with the
|
||||
password in $(short "$AUTH/viewer-01.pass"):
|
||||
> $SITE/logs/auth/view
|
||||
Shell setup (LW, CA, PW: viewer-01's password file, SITE):
|
||||
> . $(short "$RUN")/env
|
||||
The CLI, unbound through the proxy (bearer keeps the token off argv):
|
||||
> token=\$(\$LW auth token -unbound -url \$SITE/logs -user viewer-01 -password-file \$PW -ca-file \$CA)
|
||||
> token=\$(\$LW auth token --unbound --url \$SITE/logs --user viewer-01 --password-file \$PW --ca-file \$CA)
|
||||
> curl -N --cacert \$CA -H @<(bearer) \$SITE/logs/stream
|
||||
Logs: $(short "$LOG")/
|
||||
EOF
|
||||
@@ -294,9 +316,9 @@ code_of() { # url [token] -> http_code; the header goes through a pipe, never ar
|
||||
fi
|
||||
}
|
||||
|
||||
browser() { # mode: runs browser.cjs, whose key=value lines val reads
|
||||
browser() { # mode [base]: runs browser.cjs, whose key=value lines val reads
|
||||
info "browser: $1 (output in $(short "$LOG")/browser-$1.out)"
|
||||
timeout 120 node "$RUN/browser.cjs" "$1" "$SITE/logs/" viewer-01 "$AUTH/viewer-01.pass" \
|
||||
timeout 120 node "$RUN/browser.cjs" "$1" "${2:-$SITE/logs/}" viewer-01 "$AUTH/viewer-01.pass" \
|
||||
"$RUN/profile" "${PIDS[0]}" >"$LOG/browser-$1.out" 2>"$LOG/browser-$1.err"
|
||||
browser_out="$(cat "$LOG/browser-$1.out")"
|
||||
[[ -z $(val error) ]] || info "${C_RED}browser error ($1): $(val error)$C_OFF"
|
||||
@@ -308,7 +330,7 @@ csp_clean() { echo $(($(is "$(val error)" "") && $(is "$(val csp_violations)" 0)
|
||||
section "Scenario 1: a person in a browser"
|
||||
if ((BROWSER)); then
|
||||
browser cookie
|
||||
check "browser: an unauthenticated viewer is sent to the login page" $(is "$(val redirected_to_login)" 1)
|
||||
check "browser: the mount sends an unauthenticated visitor to the viewer, then the login page" $(is "$(val redirected_to_login)" 1)
|
||||
check "browser: a wrong password is refused on the page" $(is "$(val wrong_password_refused)" 1)
|
||||
check "browser: login (Argon2 in the page, $(val login_ms) ms) opened the viewer" $(is_set "$(val login_ms)")
|
||||
n=$(val events)
|
||||
@@ -360,7 +382,7 @@ fi
|
||||
section "Scenario 3: the CLI through the proxy"
|
||||
token="$("$BIN" auth token -unbound -url "$SITE/logs" -user viewer-01 -password-file "$AUTH/viewer-01.pass" \
|
||||
-ca-file "$PKI/ca.crt" 2>>"$LOG/auth-cli.out")"
|
||||
check "cli: lw auth token -unbound logged in through the proxy" $(is_set "$token")
|
||||
check "cli: lw auth token --unbound logged in through the proxy" $(is_set "$token")
|
||||
code="$(code_of "$SITE/logs/status" "$token")"
|
||||
check "cli: /logs/status served with the token (HTTP $code)" $(is "$code" 200)
|
||||
sse="$(timeout 3 curl -sN --noproxy '*' --cacert "$PKI/ca.crt" \
|
||||
@@ -372,9 +394,20 @@ check "cli: /logs/status refused without a token (HTTP $code)" $(is "$code" 401)
|
||||
"$BIN" auth token -url "$SITE/logs" -user viewer-01 -password-file "$AUTH/viewer-01.pass" \
|
||||
-ca-file "$PKI/ca.crt" >/dev/null 2>>"$LOG/auth-cli.out"
|
||||
rc=$?
|
||||
check "cli: a path without -unbound is a usage error (exit $rc)" $((rc == 2))
|
||||
check "cli: a path without --unbound is a usage error (exit $rc)" $((rc == 2))
|
||||
|
||||
section "Scenario 4: around the proxy"
|
||||
section "Scenario 4: an open sink"
|
||||
if ((BROWSER)); then
|
||||
browser open "http://viewer.test:$PORT_OPEN/"
|
||||
n=$(val events)
|
||||
check "open: the root led to the viewer, which streamed with no login on an insecure page (${n:-0} lines, secure $(val secure))" \
|
||||
$((${n:-0} >= 3 && $(is "$(val secure)" false) && $(is "$(val sign_in_ui)" false)))
|
||||
check "open: no CSP violations ($(val csp_violations))" $(csp_clean)
|
||||
else
|
||||
skip "browser checks: node, playwright or its Chromium headless shell missing"
|
||||
fi
|
||||
|
||||
section "Scenario 5: around the proxy"
|
||||
code="$(code_of "http://127.0.0.1:$PORT_SINK/auth/login")"
|
||||
check "direct: the login page refuses a peer that is not the proxy (HTTP $code)" $(is "$code" 403)
|
||||
code="$(code_of "http://127.0.0.1:$PORT_SINK/status" "$token")"
|
||||
|
||||
Reference in new issue
Block a user