Skip to content

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 29 Oct 17:51
· 97 commits to main since this release

Install on the hub (Iran server) — نصب روی هاب (سرور ایران):

bash <(curl -fsSL https://github.com/localroot4/DeyRoute/releases/latest/download/install.sh)

This release exactly: bash <(curl -fsSL https://github.com/localroot4/DeyRoute/releases/download/v0.3.0/install.sh) --version 0.3.0

Already installed? Run deyroute update on the hub (the nodes follow).
قبلاً نصب کرده‌اید؟ روی هاب deyroute update بزنید (نودها خودشان دنبال می‌کنند).

What's new in 0.3.0

Security — tunnel logs

  • The hub and the node rotate the tunnel logs in /var/log/deyroute/tunnels
    as root. The rotation no longer follows symbolic links and never writes
    through a file name that the backends' user deyroute can create first:
    a *.log that is a link, not a regular file or has a second hard link is
    skipped, the copy is staged in the root-only /var/lib/deyroute/tmp
    (0700, created when needed), and anything else found at a name the
    rotation writes is removed. Each case is logged once per file and reason
    as the warning tunnel log rotation refused in hub.log or node.log
    (docs/en/security.md, lab S36).
  • /var/log/deyroute/tunnels is created as root:deyroute with mode 1770
    (sticky) by the installer, deyroute setup and the services, which
    created a missing one as root:root 0750 before. The backends' user can
    still add files there but can no longer remove or rename root's live and
    rotated logs (QUESTIONS.md C.48).
  • Migration: on a server installed before, deyroute update is enough.
    The hub and node services add the sticky bit to an existing
    /var/log/deyroute/tunnels that belongs to root when they start (they
    never remove a bit, and log tunnel log directory made sticky). Running
    the installer again (it repairs and keeps the configuration) or
    chown root:deyroute /var/log/deyroute/tunnels && chmod 1770 /var/log/deyroute/tunnels
    also works. Check it with stat -c '%a %U:%G' /var/log/deyroute/tunnels
    (1770 root:deyroute).
  • Before the hub or a node starts a transport, and for every tunnel when
    the service starts, <tunnel>.log is made root's regular file with a
    single link: systemd opens it as root for the transport's output.
    Anything else found at that name is removed, an empty log (0644) is
    created, and the warning tunnel log replaced is logged once per file
    and reason (symlink, not_regular, hardlink or owner) in
    hub.log or node.log. To free space, empty a live tunnel log
    (: > <tunnel>.log); do not delete it (docs/en/security.md, lab S36,
    QUESTIONS.md C.56 and C.57).
  • deyroute logs and the log lines shown with a failed start
    (DEY-B003) read a tunnel log only when it is a regular file. They no
    longer follow a link or wait on a FIFO at a log name: such a name shows
    no lines on the hub, and a node answers DEY-X022.
  • A directory found at a name the rotation or the preparation of a tunnel
    log writes is renamed <name>.removed-<16 hex digits> at once and
    deleted in the background, at most 10000 entries per directory and
    minute, so neither the start of a transport nor the start of a service
    waits for it.
  • The hub and the node prepare a tunnel log only in a safe
    /var/log/deyroute/tunnels: root's own directory with the sticky bit,
    or closed to its group and others. At a link, in a directory of another
    user or in a group-writable one without the sticky bit, the transport is
    not started and the hub or node answers DEY-X022, whose why names the
    reason; the services log cannot make the tunnel log directory sticky
    for such a directory. Fix:
    chown root:deyroute /var/log/deyroute/tunnels && chmod 1770 /var/log/deyroute/tunnels
    (the services make a root-owned directory sticky themselves when they
    start).
  • deyroute logs and the log lines shown with a failed start (DEY-B003)
    no longer read a file with a second hard link at a log name
    (docs/en/security.md, lab S36, QUESTIONS.md C.56).

Security — per-backend privilege allowlist

  • A node now checks every backend unit the hub sends against a list of
    privileges built into its binary. Only wireguard and awg run as root,
    with CAP_NET_ADMIN and AF_NETLINK. wireguard may relax
    ProtectKernelTunables, and awg may also write to /run/amneziawg.
    rathole may relax MemoryDenyWriteExecute. The node side of
    direct/haproxy may open AF_NETLINK. Every other backend runs as user
    deyroute with CAP_NET_BIND_SERVICE only. No backend may relax
    NoNewPrivileges or the capability bounding set, or get a writable path
    its entry does not list.
  • A unit that asks for more is refused: the node answers DEY-N050 and names
    the field. The hub checks its own units against the same list before it
    writes them (DEY-X034). In both cases only that rung is skipped, with the
    code; the other rungs keep working. Every unit DEYROUTE's own backends
    render passes the check, so a working install does not change. The list
    is in docs/en/security.md (Tunnel processes).

Security — nodes install only what a signed release proves

  • A node no longer installs whatever its hub serves. When it follows the
    hub's version, the hub passes on the signed files of its release
    (SHA256SUMS, SHA256SUMS.minisig, the archive) and the node checks
    them with the release key built into its own binary: the signature, that
    the release is exactly the announced version, the checksum of the
    archive, that the archive holds a single regular file deyroute (at most
    200 MB, nothing outside its directory), and that this binary checks
    signed updates itself (deyroute --json version says
    "signed_update": true). Anything else is refused with DEY-S001;
    nothing is installed and the node keeps its version
    (docs/en/update-uninstall.md, "How the nodes follow"; lab S19, S41).
  • A node installs a backend only in a version that a signed manifest pins:
    the version, download address and sha256 for its architecture must be
    those of the manifest built into it, or of a manifest the hub sends with
    a signature the node checks itself (the hub's override, or the manifest
    of the hub's own release when the node runs another build). Anything else
    is refused with DEY-N050 before a download. A version already installed
    exactly as pinned is kept without a manifest (QUESTIONS.md C.62;
    "Backends on the nodes"; lab S20, S21).
  • deyroute update manifest now keeps the signature as
    /etc/deyroute/backends.yaml.minisig next to the override; both are in
    backups. An override saved by an earlier build has no signature: the hub
    fetches a matching one at start and every 10 minutes, and until then its
    nodes install only the versions built into them (warning "the backend
    manifest override has no valid signature …"; fix:
    deyroute update manifest).
  • The hub keeps the signed release of its version for its nodes in
    /var/lib/deyroute/bin/<version>/: deyroute update and the installer
    (also with --local) leave it there, and a hub without it downloads it
    the first time a node needs it (through a node first). The installer
    keeps it on every server it installs, nodes included (QUESTIONS.md
    C.60). deyroute doctor lists it as "signed release kept for the
    nodes".
  • Migration: update the hub first. A node of this version refuses the
    update of a hub of an earlier build (DEY-S001, "update the hub first")
    and keeps running; nodes of earlier builds update from a new hub as
    before. After deyroute update --version or --rollback on the hub to
    an earlier build, the nodes keep their version. A hub on a development
    build has no signed release: its nodes refuse with DEY-S012.

Added — signed updates

  • DEY-S012 "Hub runs an unsigned development build".
  • node.allow_unsigned_updates in a node's config.yaml (default false,
    written only when true; for test labs only): that node takes the hub's
    binary without a signature, from a development build or from an earlier
    hub. It does not apply to backends. While it is set, the node refuses to
    install a release older than both itself and 0.3.0 (DEY-S011, every
    older 0.3.0-edge.N included) (QUESTIONS.md C.58).
  • deyroute --json version gains "test_key" (true only for a lab build
    that trusts a second, throwaway signing key; both release paths refuse to
    publish such a binary) and "signed_update": true (docs/cli-json.md).

Changed — the automatic update is off by default

  • The automatic update is now off by default (spec section 5: an update
    never runs without asking). Its switch moved from the state database,
    which no backup contains, to config.yaml as hub.auto_update: true
    (written only when on), so a backup keeps it and a restored hub has it
    again. deyroute update auto on|off sets it after an automatic backup,
    deyroute --json update auto adds "source": "config", and
    deyroute setup --auto-update on|off (default off) chooses it at
    setup. A hub that ran an earlier build records the event
    auto_update_default_off once and shows "automatic update is now off by
    default; turn it on with: deyroute update auto on" on the dashboard for 7
    days while it is off. The switch is also still written where earlier
    builds read it, so a rollback to one of them keeps it as it was.
  • Migration: a hub that relied on the old default no longer installs
    releases by itself; run deyroute update auto on once to keep that.
  • While hub.auto_update is set, deyroute update --rollback and
    deyroute update --version to a release before 0.3.0 (every
    0.3.0-edge.N included) are refused with DEY-S011; run
    deyroute update auto off first. The rollback guard now compares each
    newer config.yaml key with the first release that knows it (DEY-S011
    names both), never refuses --version to a release that is not older
    than the running one, and trusts the recorded version of
    deyroute.prev only while that file is unchanged (after the installer
    or a rollback without the service replaced it, every newer key counts).
  • A failed automatic update still goes back to the version it came from.
    Only when a setting added after the update is unknown to that version
    is the new version kept; the event update_rolled_back with the code
    DEY-S011 and a dashboard line for 7 days say so.

Fixed — the join command installs the hub's own release

  • A hub on a release now prints a join command with the installer of that
    release:
    bash <(curl -fsSL https://github.com/localroot4/DeyRoute/releases/download/v<ver>/install.sh) join '…' --version <ver>
    (with DEYROUTE_MIRROR: <mirror>/v<ver>/install.sh). It used the newest
    installer (…/releases/latest/download/install.sh) with an older
    --version. A development build pins no version and keeps the latest
    installer. The JSON field command of node join-command keeps its name;
    only its text changes. Hubs on older builds print the old form until they
    are updated.
  • Migration for mirror owners: keep the v<ver>/ directory, with its
    install.sh, of every release your hubs run. A mirror that keeps only
    latest/ answers the new join command with 404.

More in the full changelog.

Full changelog · راهنمای فارسی · English guides

Verify

SHA256SUMS is signed with minisign; the installer and deyroute update check it before anything is installed. Public key: RWQtE2Hu1KwstgupEuPAWy+J2tiIgFVTIcReWI+Qh+yGMY3s2xWpyMuO