Commit Graph
25 Commits
Author SHA1 Message Date
Maksim ShulgaandMaxim Shulga 6f8af2cfa4 Reject UID or GID zero unless a documented use case requires it (#32)
* Reject UID or GID zero unless a documented use case requires it

setup_uid() would happily remap the seafile user to UID/GID 0, which
after the previous non-root fix would mean drop_privileges() drops
straight back into root. main() now rejects --uid/SEAFILE_UID and
--gid/SEAFILE_GID being 0 with exit code 2, before setup_uid() runs.

Verified against the real image via tests/smoke.sh.

* Clarify the GID=0 rejection message

Copilot review: 'group root' isn't a commonly used phrase. Name GID 0
as the root group explicitly instead.

---------

Co-authored-by: Maxim Shulga <maxim.shulga@alludo.com>
2026-08-03 17:59:38 +00:00
Maksim ShulgaandMaxim Shulga 6d32460b13 Run the long-lived process as the configured non-root user (#31)
* Run the long-lived process as the configured non-root user

start.py used to stay root for its whole life; only the individual
seaf-cli/seaf-daemon subprocess calls were unprivileged, via a per-call
runuser wrapper. The orchestrating Python process itself, including the
RPC socket to seaf-daemon and the watch_status() loop, ran as root the
entire time.

main() now drops privileges in-process (dsc.misc.drop_privileges, using
os.initgroups/setgid/setuid) right after setup_uid()/create_dir() -
which still need root to remap the seafile user's UID/GID and chown
/dsc - and before SeafileClient is constructed. Since the process is
already the target user by the time any seaf-cli call happens, runuser
is removed from user_cmd(); it also could not be invoked from an
already non-root process.

Verified against the real image, not just mocked unit tests: a running
container's PID 1 shows Uid: 1000 1000 1000 1000 in /proc/1/status, and
tests/smoke.sh gained a check exercising setup_uid/create_dir/
drop_privileges together against the image's real /etc/passwd.

* Isolate os.environ mutation in drop_privileges test

drop_privileges() writes HOME/USER/LOGNAME directly to os.environ, and
the test asserting that didn't restore them, leaking state into later
tests. monkeypatch.delenv registers the keys so its teardown restores
them regardless of who wrote to os.environ.

---------

Co-authored-by: Maxim Shulga <maxim.shulga@alludo.com>
2026-08-03 17:44:34 +00:00
Maksim ShulgaandMaxim Shulga b0dfc1ad74 Run the image vulnerability scan on a weekly schedule (#30)
* Run the image vulnerability scan on a weekly schedule

The Trivy scan added in #29 only ran against changed code (PRs and
pushes), so a CVE disclosed against an otherwise unchanged base image
would go unnoticed until the next actual change. Add a weekly cron
trigger, matching Dependabot's cadence in .github/dependabot.yml.

The push job's existing `if: github.event_name == 'push'` guard means
a schedule-triggered run builds and scans but never publishes.

* Add workflow_dispatch so the scan can be run on demand

Copilot review on PR #30 suggested this: waiting for the Monday cron
to validate a fix is unnecessary friction. workflow_dispatch has no
inputs, so it just re-runs the same build/test/scan on demand; the
push job's event_name guard still keeps it from publishing.

---------

Co-authored-by: Maxim Shulga <maxim.shulga@alludo.com>
2026-08-03 05:26:08 +00:00
Maksim ShulgaandMaxim Shulga c2a634c13f Scan the built image for vulnerabilities and fix what's fixable (#29)
* Scan the built image for vulnerabilities and fix what's fixable

Ran trivy against the built image locally: 67 HIGH/CRITICAL findings,
65 of them in debian:bookworm-slim packages with no upstream fix yet.
The remaining 2 were both setuptools 66.1.1 (CVE-2024-6345,
CVE-2025-47273) — bundled by ensurepip when the venv is created, not
pinned by requirements.txt, so nothing was upgrading it.

- Upgrade setuptools to >=78.1.1 in the Dockerfile, which fixes both
  CVEs. Verified with a local rebuild + rescan: 0 HIGH/CRITICAL
  fixable findings remain.
- Add a Trivy scan step to the CI `test` job, after the smoke test.
  Uses --ignore-unfixed so the build fails only on findings this repo
  can act on, not on upstream Debian CVEs with no available patch.
- Check off the corresponding P0 item in IMPROVEMENTS.md; note that
  scheduled (as opposed to PR/push-triggered) scanning is still open.

* Fix trivy-action tag (needs v prefix)

* Tighten scan-coverage wording per review

Copilot review on PR #29 flagged "every pull request and push" as
ambiguous: the test job's push trigger is scoped to master and tags,
not every branch. Spell that out.

---------

Co-authored-by: Maxim Shulga <maxim.shulga@alludo.com>
2026-08-03 05:16:42 +00:00
Maksim ShulgaandMaxim Shulga a2887fa6db Bound HTTP request timeouts and retry budget (#28)
* Bound HTTP request timeouts and retry budget

With no request timeout, Retry(total=30, backoff_factor=2,
backoff_max=60) let a single unreachable server hang the client for
around 26 minutes: backoff sleeps alone sum to that, and each of the
30 connection attempts could itself stall indefinitely without a
timeout. Measured a black-holed connect attempt taking 75s on this
machine with no timeout set, confirming the mechanism.

- Add HTTP_TIMEOUT (connect, read) and a tighter retry budget
  (HTTP_RETRY_TOTAL/BACKOFF_FACTOR/BACKOFF_MAX) in dsc/const.py,
  bounding the worst case to a few minutes.
- Pass timeout=HTTP_TIMEOUT on both SeafileClient HTTP calls (token
  fetch, remote library listing).
- Add NetworkError(DscError) so a network failure is caught by
  start.py's existing top-level DscError handling instead of
  producing a bare traceback.
- Add tests/test_http.py covering the retry policy values, timeout
  propagation, and error wrapping.
- Check off the corresponding IMPROVEMENTS.md items.

* Include URL and status code in NetworkError messages

Copilot review on PR #28 flagged that the non-200 path for remote
library listing raised NetworkError with only the raw response body,
losing which request failed and its status code. The token-fetch
error had the same gap. Both now include the URL and HTTP status.

---------

Co-authored-by: Maxim Shulga <maxim.shulga@alludo.com>
2026-08-03 04:58:14 +00:00
Maksim Shulga c5b4262a4c Merge pull request #27 from snegov/feature/container-smoke-test
Run a container smoke test against the real seafile daemon
2026-08-03 02:52:38 +00:00
Maksim Shulga 2f93e4cdbc Merge pull request #26 from snegov/docs/record-certificate-flag-fix
Record why DISABLE_VERIFY_CERTIFICATE never worked
2026-08-03 01:32:23 +00:00
Maksim Shulga 872585833b Merge pull request #25 from snegov/feature/typed-env-parsing
Read the environment with typed parsers and apply the documented defaults
2026-08-03 01:12:06 +00:00
Maksim Shulga 87b76b80f6 Merge pull request #24 from snegov/feature/parse-library-list-as-json
Read the local library list as JSON instead of splitting columns
2026-08-03 00:36:38 +00:00
Maksim Shulga 8043ac1409 Merge pull request #23 from snegov/feature/secrets-support
Authenticate from mounted secrets and stop passing the password to seaf-cli
2026-08-03 00:24:28 +00:00
Maksim Shulga 1e59da6193 Merge pull request #22 from snegov/feature/deterministic-startup-shutdown
Bound daemon startup and shutdown and exit with a useful code
2026-08-03 00:12:50 +00:00
Maksim Shulga d1c0be2c24 Merge pull request #17 from snegov/dependabot/pip/cached-property-2.0.1
pip: bump cached-property from 1.5.2 to 2.0.1
2026-08-03 00:02:50 +00:00
Maksim Shulga d2d6621add Merge pull request #15 from snegov/dependabot/github_actions/docker/login-action-4
ci: bump docker/login-action from 3 to 4
2026-08-03 00:00:37 +00:00
Maksim Shulga f0e5c1fc06 Merge pull request #20 from snegov/dependabot/github_actions/docker/metadata-action-6
ci: bump docker/metadata-action from 5 to 6
2026-08-03 00:00:31 +00:00
Maksim Shulga 5546fe5317 Merge pull request #18 from snegov/dependabot/pip/requests-2.34.2
pip: bump requests from 2.33.0 to 2.34.2
2026-08-02 23:59:58 +00:00
Maksim Shulga fad6baf28a Merge pull request #16 from snegov/dependabot/github_actions/actions/checkout-7
ci: bump actions/checkout from 4 to 7
2026-08-02 23:59:42 +00:00
Maksim Shulga 964ab2c46f Merge pull request #19 from snegov/dependabot/github_actions/docker/build-push-action-7
ci: bump docker/build-push-action from 6 to 7
2026-08-02 23:59:39 +00:00
Maksim Shulga 02e703ffc3 Merge pull request #21 from snegov/feature/no-shell-and-path-injection
Stop server-provided values from reaching a shell or escaping /dsc/seafile
2026-08-02 23:55:20 +00:00
Maksim Shulga 50ede0b2f9 Merge pull request #14 from snegov/feature/dependabot-updates
Track dependency updates with Dependabot
2026-08-02 23:33:39 +00:00
Maksim Shulga dc1a9b6f03 Merge pull request #13 from snegov/feature/dockerignore-and-unused-imports
Trim the build context and drop unused imports
2026-08-02 23:27:44 +00:00
Maksim Shulga ff22f05576 Merge pull request #12 from snegov/feature/ci-release-pipeline
Publish one release image digest from a modernised workflow
2026-08-02 23:20:15 +00:00
Maksim Shulga b71a29b6ef Merge pull request #10 from snegov/dependabot/pip/requests-2.33.0
Bump requests from 2.32.4 to 2.33.0
2026-08-02 23:05:58 +00:00
Maksim Shulga f8a8e12add Merge pull request #11 from snegov/feature/appimage-seafile-cli
Install Seafile CLI from AppImage instead of the APT repository
2026-08-02 23:00:29 +00:00
Maks Snegov 42a0f3f05c Merge pull request #9 from snegov/dependabot/pip/requests-2.32.4
Bump requests from 2.32.0 to 2.32.4
2026-02-02 21:10:45 -08:00
Maks Snegov 446eb8e1ef Merge pull request #7 from snegov/dependabot/pip/requests-2.32.0
Bump requests from 2.31.0 to 2.32.0
2026-02-02 21:07:11 -08:00