Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ jobs:
working-directory: e2e
- name: Wait until the demo has logged every kind of error
run: timeout 90 sh -c 'until docker logs demo-worker-1 2>&1 | grep -q import-catalog; do sleep 2; done'
- run: npx playwright test
- run: npx playwright test --project ui --project read-only
working-directory: e2e
- uses: actions/upload-artifact@v7
if: failure()
Expand All @@ -117,3 +117,43 @@ jobs:
e2e/playwright-report
e2e/test-results
retention-days: 7

e2e-swarm:
needs: ui
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
name: ui-dist
path: ui/dist
- name: Start the swarm demo stack
working-directory: dev/demo
run: |
docker pull -q mirror.gcr.io/library/alpine:3.20 && docker tag mirror.gcr.io/library/alpine:3.20 alpine:3.20 || docker pull -q alpine:3.20
docker swarm init
docker stack deploy --resolve-image never -c stack.yaml shop
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo build -p stackline
- uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
cache-dependency-path: e2e/package-lock.json
- run: npm ci
working-directory: e2e
- run: npx playwright install --with-deps chromium
working-directory: e2e
- name: Wait until every replica runs
run: timeout 120 sh -c 'until docker service ls --filter name=shop_api --format "{{.Replicas}}" | grep -q "3/3"; do sleep 2; done'
- run: npx playwright test --project swarm
working-directory: e2e
- uses: actions/upload-artifact@v7
if: failure()
with:
name: e2e-swarm-report
path: |
e2e/playwright-report
e2e/test-results
retention-days: 7
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,24 @@
Release notes for each version. The section of a version becomes the text of
its GitHub release, and Stackline shows it in the update dialog.

## Unreleased

### Security
- **SSH connections no longer open a port on this computer.** Stackline reached a server's Docker through a local TCP port that any program on the computer could also use, and a web page could try to. Whoever reaches that port controls the server's Docker. On macOS and Linux the connection now goes through a socket file in a folder only you can open, removed when Stackline stops. Windows still uses a local port for now

### Changed
- **A connection that reaches another server no longer deletes its logs by itself.** When the server behind a connection changes (its address was edited, an SSH alias points elsewhere, the swarm was set up again), Stackline stops collecting and asks: keep the old logs, or delete them. Before, they were deleted without asking
- **At the size limit, a busy server no longer pushes out the logs of a quiet one.** Each connection gets an equal share of the space, and only connections above their share lose their oldest lines

### Fixed
- **"Clear stored logs" during a read could leave part of that read behind**, and those lines then kept the cleared range from filling again
- **A replica read for the first time held up all the others.** Reading hours of a busy replica can take minutes; other replicas and container events now go on being read meanwhile
- **Deploys could go missing from the Overview** when a service crashed thousands of times after them
- **`--host 0.0.0.0` answered nobody.** Other machines were turned away because they address Stackline by an IP address it did not know. Any IP address of the machine now works; names are still refused, which keeps DNS rebinding out. Stackline also warns that the link travels unencrypted
- **A settings file that could not be read kept Stackline from starting.** It is now set aside as `config.broken.json`, Stackline starts with the default settings and says so
- **A log line could run as a formula** when a CSV download was opened in a spreadsheet app. Cells that start with `=`, `+`, `-` or `@` now start with `'`
- **A Docker that accepted a request and then never answered stopped log collection for good.** Requests now give up after 30 seconds without an answer and are tried again on the next poll

## 0.5.1

Logs no longer end without warning: the stream pages through every stored line, and long lines are stored whole.
Expand Down
41 changes: 39 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ that opens the dashboard in your browser.

[Download](https://github.com/snaketech-tu/stackline/releases/latest) ·
[Get started](#get-started) · [Connect a host](#connect-a-host) ·
[Development](#development) · [Changelog](CHANGELOG.md)
[Security](#security) · [Development](#development) · [Changelog](CHANGELOG.md)

## Features

Expand Down Expand Up @@ -231,6 +231,38 @@ When an update is available, Stackline shows the release notes and lets you
choose when to install it. Every update is verified against the app's built-in
public key before installation.

## Security

Stackline can reach the Docker of every server you connect, and whoever
controls a server's Docker controls the server. This is how it keeps that
access to you:

- **Only reads by default.** Stackline sends Docker requests that read:
lists, logs and events. Log downloads are files written on your computer.
- **SSH connections stay private.** Stackline reaches a server's Docker
socket through your own `ssh`. On macOS and Linux the tunnel ends in a
socket file in a folder only your user can open, which is removed when
Stackline stops. On Windows it still ends in a port on `127.0.0.1` that
other programs on the computer could use, so do not run Stackline on a
Windows machine you share with people you do not trust. Your private keys
are never read or copied.
- **Only your browser tab can use it.** The standalone server listens on
`127.0.0.1`. Every API call needs the random sign-in token from the link it
prints; the link stores it in a cookie that only Stackline's own pages send
(`HttpOnly`, `SameSite=Strict`). Requests addressed to any other name than
`localhost` or `127.0.0.1` are refused, which keeps web pages from reaching
Stackline through DNS rebinding.
- **`--host 0.0.0.0` makes it reachable from the network**, for anyone who has
the link. The connection is plain HTTP, so the token can be read on the way.
Only do this on a network you trust, or put Stackline behind a reverse proxy
that adds TLS. Stackline prints a warning when it listens beyond loopback.
- **Updates are signed.** An update is installed only when its signature
matches the public key built into the app.

To report a security problem, open a private
[security advisory](https://github.com/snaketech-tu/stackline/security/advisories/new)
on GitHub instead of an issue.

## Development

You need **Rust stable** and **Node.js 24**. From a fresh clone:
Expand Down Expand Up @@ -315,13 +347,18 @@ start the demo project, build the UI and Stackline, then:
cd e2e
npm ci
npx playwright install chromium
npx playwright test
npx playwright test --project ui --project read-only
```

They start their own Stackline on port 7171 with an empty data folder
(`e2e/.run`), so your own connections and stored logs are not touched. Set
`STACKLINE_BIN` to test another build, such as `target/release/stackline`.

The [swarm tests](e2e/tests/swarm.spec.ts) need the demo Swarm stack instead
(`docker swarm init`, then `docker stack deploy -c stack.yaml shop` in
`dev/demo`) and run with `npx playwright test --project swarm`. CI runs them in
a job of their own.

## License

Licensed under [Apache-2.0](LICENSE).
Loading
Loading