Add CAP_NET_BIND_SERVICE everywhere the service is defined (#2549)

The Virtual Printer binds 990 and 322, below 1024, which a service running
as a normal user may not do without CAP_NET_BIND_SERVICE. Without it the
rest of Bambuddy works and only the VP is dead -- sockets never open, the
slicer never finds the printer, and the sole trace is one journal line.

332a7c6ac added the line to install/install.sh in March under the heading
"Fix install.sh missing AmbientCapabilities". Three other places define the
same unit and none of them got it: the manual template, the combined
Bambuddy + SpoolBuddy installer, and the unit the wiki tells you to paste.
The wiki additionally claimed the capability was always included.

Also diagnose it. The VP diagnostic reported only that nothing was listening
on 990, which reads identically to a port conflict. It now checks CapEff for
the capability and names it as the cause -- but stays quiet when the port is
answering (an iptables REDIRECT is the documented alternative and that host
works) and when the capability is held (the port is down for another reason
and blaming this would misdirect). Skips where there is no procfs rather
than putting a systemd instruction in front of a macOS user.
This commit is contained in:
maziggy 2026-08-04 08:33:30 +02:00
parent 4af782cc2f
commit 28a6ca6f4d
20 changed files with 262 additions and 16 deletions

View file

@ -5,6 +5,7 @@ All notable changes to Bambuddy will be documented in this file.
## [1.2.6b1] - Unreleased
### Fixed
- **A hand-written systemd service left the Virtual Printer unable to start, with nothing obvious to blame (#2549, reporter @Ru3ck3)** — The Virtual Printer binds ports 990 and 322, both below 1024, which a service running as a normal user may not do without the `CAP_NET_BIND_SERVICE` capability. Without it the rest of Bambuddy works perfectly and only the Virtual Printer is dead: its sockets never open, the slicer never finds the printer, and the sole trace is one line in the journal. The reporter lost days to this before someone on Discord spotted the missing line. The install script has carried it since March, but the three other places that define the same service did not — the manual-install template, the combined Bambuddy plus SpoolBuddy installer, and the unit the wiki tells you to paste. All three have it now, and the wiki no longer claims the capability is always included when its own instructions omitted it. Bambuddy also diagnoses this itself: **Diagnose** on the virtual printer card previously reported only that nothing was listening on port 990, which reads identically to an ordinary port conflict. It now checks whether the process actually holds the capability and, when that is what is wrong, says so and gives the line to add. The check stays quiet when the port is answering, since fronting it another way (an iptables redirect is the documented alternative) is a legitimate setup, and it stays quiet when the capability is held, so a port that failed for some other reason is not misattributed. Existing installs are unaffected until reinstalled; the diagnostic tells you whether yours needs the line. Translated in all locales; wiki updated. Covered by backend tests.
- **A refused AMS filament setting now says so in the log (#2756, reporter @Jostxxl)** — Configuring a slot publishes an `ams_filament_setting` command, and the printer answers it with a verdict. That answer was received and then thrown away at debug level, so a printer that refused the write left no trace at the log level support bundles are collected at. The reporter hit exactly that: six manual **Configure Slot** attempts on one X1C, every one returning success, every one read back by the #2582 verification as still holding the previous profile, and nothing anywhere to say what the printer had made of the command. A refusal is now logged with the printer's own `result` and `reason` alongside the AMS and tray it concerned. Only refusals are promoted — unlike the K-profile and drying commands this one is not rare, since every spool assignment and every K-profile re-apply sends one, and logging each acknowledgement would bury the line worth reading. The developer-mode probe is excluded as well: it sends this same command to the external slot specifically to watch it be refused on P1 firmware, so its failure is a measurement rather than a fault. Diagnostics only — nothing about which commands are sent or how they are built has changed. Covered by backend tests.
- **Live updates stopped arriving while the Bambuddy tab was in the background (#2754, reporter @mic4rd)** — The progress percentage in the tab title froze whenever you switched to another tab and jumped straight to the current value the moment you switched back, which defeats the point of putting it in the title. The cause was not in the tab-title feature: every printer status arriving over the WebSocket was written into the browser's cache from inside an animation-frame callback, and a browser gives a hidden tab no frames at all. Those callbacks are not slowed down, they are held — so the connection stayed up, the messages kept arriving, and every one of them parked in a queue that only ran when the tab was shown again. The same applied to the archive, inventory and spool refreshes, and to the queue that carries every non-status message, which stalled completely and accumulated messages until the tab came back. The animation frames were added alongside the real fix for a browser freeze on print completion — that fix was the batching, which is untouched; the frames only ever deferred each write by about a sixteenth of a second and are gone. One limit is worth knowing about and is the browser's rather than ours: browsers deliberately slow down timers in tabs you are not looking at, to roughly once a second, and to about once a minute once a tab has been hidden for five minutes. So the title keeps moving in the background, but on a tab left alone for a long time it steps rather than ticks. Covered by frontend tests that reproduce a hidden tab.
- **The bug-report button no longer covers the controls in the bottom-right corner (#2750, reporter @goodjaltman)** — On a phone the floating red button sits on top of whatever else is in that corner, which turns out to be most things: the scroll-to-top button on Profiles was ~83% underneath it and, since both sit at the same stacking level, which one you could actually tap came down to the order they happened to render in. The floating camera window parks there, as do the Group Edit save bar, the bulk-selection toolbars, and — because the button is pinned to the viewport rather than the page — the per-card action buttons on File Manager and Archives simply scroll underneath it. The reporter asked for a switch to hide the button, but it is the only way into the report form, and that form is not just a text box: it runs the printer connection diagnostic, scans your logs against the known-issue catalog, optionally captures five minutes of debug logging and attaches a support bundle. Hiding it doesn't produce smaller reports, it produces reports with nothing attached. So the button moves instead of disappearing. Once the window is narrow enough that the sidebar collapses into a menu button, the bug icon moves into that top bar and the corner is left alone; above that width nothing changes. That threshold is the one the layout already switches on, so there is no new breakpoint and no third state to reason about, and it covers tablets and half-width desktop windows rather than only phones. The report form itself is now a proper bottom sheet on phones, which also fixes it hanging 16 pixels off the left edge of the screen — it was sized to the full viewport width and then inset from the right, so a strip of the form was simply unreachable on anything under about 460 pixels wide. The scroll-to-top button on Profiles has been nudged clear of the corner as well, for the wide layouts where the floating button stays. Wiki updated. Covered by frontend tests.

View file

@ -15,6 +15,7 @@ id + status.
import asyncio
import logging
import os
from backend.app.models.virtual_printer import VirtualPrinter
from backend.app.schemas.printer import DiagnosticCheck
@ -30,6 +31,41 @@ PORT_BIND_PLAIN = 3000 # bind/detect (plain) — legacy / some slicer models
_PORT_PROBE_TIMEOUT = 2.0
# Linux capability number for CAP_NET_BIND_SERVICE (linux/capability.h).
_CAP_NET_BIND_SERVICE = 10
def can_bind_privileged_ports() -> bool | None:
"""Whether this process is allowed to bind ports below 1024.
Returns ``None`` when that cannot be determined no procfs to read and not
running as root, i.e. macOS or Windows, where this capability model does not
apply and the caller should skip the check rather than guess.
Reading the effective set covers both ways the permission is granted,
because both are visible at runtime: ``AmbientCapabilities`` in the systemd
unit (or ``cap_add: [NET_BIND_SERVICE]`` in Docker), and
``setcap cap_net_bind_service=+ep`` on the interpreter binary.
Note this answers "does the process hold the capability", not "can port 990
be bound" — a host with ``net.ipv4.ip_unprivileged_port_start`` lowered can
bind it without holding anything. Callers must treat a False here as a
*possible* explanation for a port that failed to open, never as proof on its
own; the caller in this module only reports it when a probe actually failed.
"""
geteuid = getattr(os, "geteuid", None)
if geteuid is not None and geteuid() == 0:
return True
try:
with open("/proc/self/status", encoding="utf-8") as fh:
for line in fh:
if line.startswith("CapEff:"):
caps = int(line.split(":", 1)[1].strip(), 16)
return bool((caps >> _CAP_NET_BIND_SERVICE) & 1)
except (OSError, ValueError):
return None
return None
async def _check_port(ip: str, port: int, timeout: float = _PORT_PROBE_TIMEOUT) -> bool:
"""Test TCP connectivity to ip:port. Returns True if something is listening."""
@ -108,6 +144,7 @@ async def run_vp_diagnostic(vp: VirtualPrinter, instance) -> VPDiagnosticResult:
# bound (port already in use, permission denied) because start errors are
# logged and swallowed. Probe the bind IP directly.
bind_ip = vp.bind_ip
ftp_ok: bool | None = None
if not running or not bind_ip:
for cid, port in (("port_ftps", PORT_FTPS), ("port_mqtt", PORT_MQTT), ("port_bind", PORT_BIND)):
checks.append(DiagnosticCheck(id=cid, status="skip", params={"port": port}))
@ -156,6 +193,37 @@ async def run_vp_diagnostic(vp: VirtualPrinter, instance) -> VPDiagnosticResult:
)
)
# --- Privileged port binding ---
# 990 (FTPS) and 322 (RTSP) are below 1024, so a service running as a normal
# user cannot bind them without CAP_NET_BIND_SERVICE. When it is missing the
# sockets never open, and every symptom above is a downstream effect: the
# slicer simply never sees the printer. The EACCES is logged by TCPProxy but
# that is one line in the journal, and the port checks alone report the same
# "nothing is listening" as an ordinary port conflict — which is what sent
# the reporter in #2549 to Discord for several days over one missing line in
# a unit file.
#
# Reported only when a privileged port actually failed to answer. The
# capability can legitimately be absent on a host that fronts these ports
# some other way (an iptables REDIRECT is the documented alternative), and
# flagging a working setup would be noise.
if not running or ftp_ok is None:
checks.append(DiagnosticCheck(id="privileged_ports", status="skip"))
else:
has_cap = can_bind_privileged_ports()
if has_cap is None:
# No procfs to read and not obviously root — typically macOS or
# Windows, where this whole capability model does not apply.
checks.append(DiagnosticCheck(id="privileged_ports", status="skip"))
else:
checks.append(
DiagnosticCheck(
id="privileged_ports",
status="pass" if (has_cap or ftp_ok) else "fail",
params={"port": PORT_FTPS},
)
)
# --- TLS certificate ---
# When running, the cert chain must exist on disk for the slicer's TLS
# handshake to succeed. This is a pass/fail on the file; the localized

View file

@ -3,12 +3,15 @@
import tempfile
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, mock_open, patch
import pytest
from backend.app.services.virtual_printer.certificate import CertificateService
from backend.app.services.virtual_printer.diagnostic import run_vp_diagnostic
from backend.app.services.virtual_printer.diagnostic import (
can_bind_privileged_ports,
run_vp_diagnostic,
)
_DIAG = "backend.app.services.virtual_printer.diagnostic._check_port"
_FIND_IFACE = "backend.app.services.network_utils.find_interface_for_ip"
@ -165,3 +168,110 @@ class TestCaCertificateInfo:
second = service.get_ca_certificate_info()
assert first["fingerprint_sha256"] == second["fingerprint_sha256"]
assert "PRIVATE KEY" not in first["pem"]
class TestPrivilegedPortsCheck:
"""#2549: the VP binds 990 (FTPS) and 322 (RTSP), both below 1024.
Without CAP_NET_BIND_SERVICE those sockets never open and the slicer never
sees the printer. The port probes alone report the same "nothing is
listening" as an ordinary port conflict, which is what sent the reporter to
Discord for days over one missing line in a systemd unit. This check names
the cause but only when a port actually failed, since the capability can
legitimately be absent on a host that fronts 990 some other way.
"""
_CAP = "backend.app.services.virtual_printer.diagnostic.can_bind_privileged_ports"
@pytest.mark.asyncio
async def test_missing_capability_explains_a_dead_port(self):
with (
patch(_DIAG, AsyncMock(return_value=False)),
patch(_FIND_IFACE, return_value={"name": "eth0", "ip": "192.168.1.50"}),
patch(self._CAP, return_value=False),
):
result = await run_vp_diagnostic(_vp(), _FakeInstance())
assert _checks(result)["privileged_ports"] == "fail"
@pytest.mark.asyncio
async def test_missing_capability_is_not_flagged_when_the_port_answers(self):
"""An iptables REDIRECT is a documented alternative to the capability.
Flagging a setup that demonstrably works would be noise."""
with (
patch(_DIAG, AsyncMock(return_value=True)),
patch(_FIND_IFACE, return_value={"name": "eth0", "ip": "192.168.1.50"}),
patch(self._CAP, return_value=False),
):
result = await run_vp_diagnostic(_vp(), _FakeInstance())
assert _checks(result)["privileged_ports"] == "pass"
assert result.overall == "ok"
@pytest.mark.asyncio
async def test_dead_port_with_the_capability_held_is_not_blamed_on_it(self):
"""The port is down for some other reason — a conflict, a crashed
service. Saying "missing capability" here would misdirect the user."""
with (
patch(_DIAG, AsyncMock(return_value=False)),
patch(_FIND_IFACE, return_value={"name": "eth0", "ip": "192.168.1.50"}),
patch(self._CAP, return_value=True),
):
result = await run_vp_diagnostic(_vp(), _FakeInstance())
c = _checks(result)
assert c["privileged_ports"] == "pass"
assert c["port_ftps"] == "fail"
@pytest.mark.asyncio
async def test_undeterminable_capability_skips(self):
"""macOS / Windows have no procfs and no such capability model."""
with (
patch(_DIAG, AsyncMock(return_value=False)),
patch(_FIND_IFACE, return_value={"name": "eth0", "ip": "192.168.1.50"}),
patch(self._CAP, return_value=None),
):
result = await run_vp_diagnostic(_vp(), _FakeInstance())
assert _checks(result)["privileged_ports"] == "skip"
@pytest.mark.asyncio
async def test_not_running_skips(self):
"""Nothing was probed, so there is no failure to explain."""
result = await run_vp_diagnostic(_vp(), _FakeInstance(running=False))
assert _checks(result)["privileged_ports"] == "skip"
class TestCanBindPrivilegedPorts:
def test_root_can(self):
with patch("os.geteuid", return_value=0):
assert can_bind_privileged_ports() is True
def test_effective_set_with_the_bit_set(self):
# CAP_NET_BIND_SERVICE is capability 10, so bit 10 => 0x400.
with (
patch("os.geteuid", return_value=1000),
patch("builtins.open", mock_open(read_data="Name:\tpython3\nCapEff:\t0000000000000400\n")),
):
assert can_bind_privileged_ports() is True
def test_effective_set_without_the_bit_set(self):
with (
patch("os.geteuid", return_value=1000),
patch("builtins.open", mock_open(read_data="Name:\tpython3\nCapEff:\t0000000000000000\n")),
):
assert can_bind_privileged_ports() is False
def test_neighbouring_bits_do_not_count(self):
"""0x200 is capability 9 (CAP_NET_BROADCAST) and 0x800 is 11
(CAP_NET_ADMIN) neither grants a privileged bind."""
with (
patch("os.geteuid", return_value=1000),
patch("builtins.open", mock_open(read_data="CapEff:\t0000000000000a00\n")),
):
assert can_bind_privileged_ports() is False
def test_no_procfs_is_undeterminable_not_false(self):
"""Returning False here would put a Linux-only fix instruction in front
of a macOS user whose port failed for an unrelated reason."""
with (
patch("os.geteuid", return_value=1000),
patch("builtins.open", side_effect=FileNotFoundError),
):
assert can_bind_privileged_ports() is None

View file

@ -62,6 +62,14 @@ StandardOutput=journal
StandardError=journal
SyslogIdentifier=bambuddy
# Allow binding to privileged ports (322 RTSP, 990 FTPS) for Virtual Printer
# mode. Without this the VP's sockets never open and the slicer simply never
# sees the printer — with no obvious error, since the bind failure is one line
# in the journal (#2549). Works alongside NoNewPrivileges=true below: systemd
# raises the ambient set at exec, which is not the privilege escalation that
# setting forbids.
AmbientCapabilities=CAP_NET_BIND_SERVICE
# Security hardening
NoNewPrivileges=true
PrivateTmp=true

View file

@ -6538,6 +6538,10 @@ export default {
title: 'Erkennungsdienst (Port {{port}})',
fail: 'Auf Port {{port}} der Bind-IP lauscht nichts, daher schlägt der Erkennungs-Handshake des Slicers fehl.',
},
privileged_ports: {
title: 'Bindung an privilegierte Ports',
fail: 'Port {{port}} liegt unter 1024 und dieser Dienst darf ihn nicht belegen — deshalb lauscht oben nichts. Fügen Sie AmbientCapabilities=CAP_NET_BIND_SERVICE in /etc/systemd/system/bambuddy.service ein und starten Sie neu, oder führen Sie "sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))" aus. Unter Docker ergänzen Sie cap_add: [NET_BIND_SERVICE].',
},
certificate: {
title: 'TLS-Zertifikat',
pass: 'Zertifikat bereit. Stellen Sie sicher, dass das Bambuddy-CA-Zertifikat (oben) in den Vertrauensspeicher Ihres Slicers importiert ist.',

View file

@ -6582,6 +6582,10 @@ export default {
title: 'Discovery service (port {{port}})',
fail: 'Nothing is listening on port {{port}} of the bind IP, so the slicer\'s discovery handshake fails.',
},
privileged_ports: {
title: 'Privileged port binding',
fail: 'Port {{port}} is below 1024, and this service is not permitted to bind it — which is why nothing is listening above. Add AmbientCapabilities=CAP_NET_BIND_SERVICE to /etc/systemd/system/bambuddy.service and restart, or run "sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))". On Docker, add cap_add: [NET_BIND_SERVICE].',
},
certificate: {
title: 'TLS certificate',
pass: 'Certificate ready. Make sure the Bambuddy CA certificate (above) is imported into your slicer\'s trust store.',

View file

@ -6547,6 +6547,10 @@ export default {
title: 'Servicio de detección (puerto {{port}})',
fail: 'No hay nada escuchando en el puerto {{port}} de la IP de enlace, por lo que falla el protocolo de detección del laminador.',
},
privileged_ports: {
title: 'Vinculación a puertos privilegiados',
fail: 'El puerto {{port}} está por debajo de 1024 y este servicio no tiene permiso para vincularlo, por eso no hay nada escuchando arriba. Añade AmbientCapabilities=CAP_NET_BIND_SERVICE a /etc/systemd/system/bambuddy.service y reinicia, o ejecuta "sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))". En Docker, añade cap_add: [NET_BIND_SERVICE].',
},
certificate: {
title: 'Certificado TLS',
pass: 'Certificado listo. Asegúrese de que el certificado de CA de Bambuddy (arriba) esté importado en el almacén de confianza de su laminador.',

View file

@ -6528,6 +6528,10 @@ export default {
title: 'Service de détection (port {{port}})',
fail: 'Rien n\'écoute sur le port {{port}} de l\'IP de liaison, la poignée de main de détection du slicer échoue donc.',
},
privileged_ports: {
title: 'Liaison aux ports privilégiés',
fail: 'Le port {{port}} est inférieur à 1024 et ce service n\'est pas autorisé à s\'y lier — c\'est pourquoi rien n\'écoute ci-dessus. Ajoutez AmbientCapabilities=CAP_NET_BIND_SERVICE dans /etc/systemd/system/bambuddy.service puis redémarrez, ou exécutez "sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))". Sous Docker, ajoutez cap_add: [NET_BIND_SERVICE].',
},
certificate: {
title: 'Certificat TLS',
pass: 'Certificat prêt. Assurez-vous que le certificat CA Bambuddy (ci-dessus) est importé dans le magasin de confiance de votre slicer.',

View file

@ -6527,6 +6527,10 @@ export default {
title: 'Servizio di rilevamento (porta {{port}})',
fail: 'Nulla è in ascolto sulla porta {{port}} dell\'IP di binding, quindi l\'handshake di rilevamento dello slicer fallisce.',
},
privileged_ports: {
title: 'Binding sulle porte privilegiate',
fail: 'La porta {{port}} è sotto 1024 e questo servizio non è autorizzato ad associarla: per questo sopra non risulta nulla in ascolto. Aggiungi AmbientCapabilities=CAP_NET_BIND_SERVICE in /etc/systemd/system/bambuddy.service e riavvia, oppure esegui "sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))". Con Docker aggiungi cap_add: [NET_BIND_SERVICE].',
},
certificate: {
title: 'Certificato TLS',
pass: 'Certificato pronto. Assicurati che il certificato CA di Bambuddy (sopra) sia importato nell\'archivio attendibile del tuo slicer.',

View file

@ -6539,6 +6539,10 @@ export default {
title: '検出サービス(ポート {{port}}',
fail: 'バインド IP のポート {{port}} で待ち受けているものがないため、スライサーの検出ハンドシェイクが失敗します。',
},
privileged_ports: {
title: '特権ポートへのバインド',
fail: 'ポート {{port}} は 1024 未満で、このサービスにはバインドする権限がありません。上で何も待ち受けていないのはこのためです。/etc/systemd/system/bambuddy.service に AmbientCapabilities=CAP_NET_BIND_SERVICE を追加して再起動するか、"sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))" を実行してください。Docker の場合は cap_add: [NET_BIND_SERVICE] を追加します。',
},
certificate: {
title: 'TLS 証明書',
pass: '証明書の準備ができています。Bambuddy CA 証明書(上記)がスライサーの信頼ストアにインポートされていることを確認してください。',

View file

@ -6610,6 +6610,10 @@ export default {
title: '검색 서비스 (포트 {{port}})',
fail: '바인드 IP의 포트 {{port}}에서 수신 중인 서비스가 없어 슬라이서의 검색 핸드셰이크가 실패합니다.'
},
privileged_ports: {
title: '특권 포트 바인딩',
fail: '포트 {{port}}은(는) 1024 미만이며 이 서비스에는 바인딩 권한이 없습니다. 위에서 아무것도 수신 대기하지 않는 이유입니다. /etc/systemd/system/bambuddy.service에 AmbientCapabilities=CAP_NET_BIND_SERVICE를 추가하고 재시작하거나 "sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))"를 실행하세요. Docker에서는 cap_add: [NET_BIND_SERVICE]를 추가합니다.',
},
certificate: {
title: 'TLS 인증서',
pass: '인증서가 준비됐습니다. Bambuddy CA 인증서(위)가 슬라이서의 신뢰 저장소에 가져와져 있는지 확인하세요.',

View file

@ -6527,6 +6527,10 @@ export default {
title: 'Serviço de descoberta (porta {{port}})',
fail: 'Nada está escutando na porta {{port}} do IP de vínculo, então a negociação de descoberta do slicer falha.',
},
privileged_ports: {
title: 'Vinculação a portas privilegiadas',
fail: 'A porta {{port}} está abaixo de 1024 e este serviço não tem permissão para vinculá-la, por isso nada está escutando acima. Adicione AmbientCapabilities=CAP_NET_BIND_SERVICE em /etc/systemd/system/bambuddy.service e reinicie, ou execute "sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))". No Docker, adicione cap_add: [NET_BIND_SERVICE].',
},
certificate: {
title: 'Certificado TLS',
pass: 'Certificado pronto. Verifique se o certificado CA do Bambuddy (acima) está importado no armazenamento de confiança do seu slicer.',

View file

@ -6167,6 +6167,10 @@ export default {
title: "Служба обнаружения (порт {{port}})",
fail: "На порту {{port}} выбранного IP-адреса никто не слушает, поэтому сетевое обнаружение слайсером не работает.",
},
privileged_ports: {
title: 'Привязка к привилегированным портам',
fail: 'Порт {{port}} ниже 1024, и этой службе не разрешено его занимать — поэтому выше ничего не слушает. Добавьте AmbientCapabilities=CAP_NET_BIND_SERVICE в /etc/systemd/system/bambuddy.service и перезапустите либо выполните "sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))". В Docker добавьте cap_add: [NET_BIND_SERVICE].',
},
certificate: {
title: "Сертификат TLS",
pass: "Сертификат готов. Убедитесь, что сертификат центра сертификации Bambuddy, указанный выше, импортирован в доверенное хранилище слайсера.",

View file

@ -6478,6 +6478,10 @@ export default {
title: 'Keşif servisi (port {{port}})',
fail: 'Bind IP\'sinin {{port}} portunda hiçbir şey dinlemiyor, bu nedenle dilimleyicinin keşif el sıkışması başarısız oluyor.',
},
privileged_ports: {
title: 'Ayrıcalıklı bağlantı noktası bağlama',
fail: '{{port}} numaralı bağlantı noktası 1024 altındadır ve bu hizmetin onu bağlama izni yoktur; yukarıda hiçbir şeyin dinlememesinin nedeni budur. /etc/systemd/system/bambuddy.service dosyasına AmbientCapabilities=CAP_NET_BIND_SERVICE ekleyip yeniden başlatın veya "sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))" komutunu çalıştırın. Docker kullanıyorsanız cap_add: [NET_BIND_SERVICE] ekleyin.',
},
certificate: {
title: 'TLS sertifikası',
pass: 'Sertifika hazır. Bambuddy CA sertifikasının (yukarıda) dilimleyicinizin güven deposuna içe aktarıldığından emin olun.',

View file

@ -6582,6 +6582,10 @@ export default {
title: "Служба виявлення (порт {{port}})",
fail: "На порту {{port}} IP-адреси прив’язки немає служби, що приймає з’єднання, тому слайсер не може завершити процедуру виявлення.",
},
privileged_ports: {
title: 'Прив’язка до привілейованих портів',
fail: 'Порт {{port}} нижче 1024, і цій службі не дозволено його займати — тому вище ніщо не слухає. Додайте AmbientCapabilities=CAP_NET_BIND_SERVICE до /etc/systemd/system/bambuddy.service і перезапустіть або виконайте "sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))". У Docker додайте cap_add: [NET_BIND_SERVICE].',
},
certificate: {
title: "Сертифікат TLS",
pass: "Сертифікат готовий. Переконайтеся, що наведений вище сертифікат центру сертифікації Bambuddy імпортовано до сховища довірених сертифікатів слайсера.",

View file

@ -6526,6 +6526,10 @@ export default {
title: '发现服务(端口 {{port}}',
fail: '绑定 IP 的端口 {{port}} 上没有任何监听,因此切片软件的发现握手会失败。',
},
privileged_ports: {
title: '特权端口绑定',
fail: '端口 {{port}} 低于 1024而此服务没有绑定它的权限这正是上面没有任何监听的原因。请在 /etc/systemd/system/bambuddy.service 中添加 AmbientCapabilities=CAP_NET_BIND_SERVICE 并重启,或运行 "sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))"。使用 Docker 时请添加 cap_add: [NET_BIND_SERVICE]。',
},
certificate: {
title: 'TLS 证书',
pass: '证书已就绪。请确保已将 Bambuddy CA 证书(上方)导入切片软件的信任库。',

View file

@ -6526,6 +6526,10 @@ export default {
title: '探索服務(連接埠 {{port}}',
fail: '繫結 IP 的連接埠 {{port}} 上沒有任何監聽,因此切片軟體的探索交握會失敗。',
},
privileged_ports: {
title: '特權連接埠繫結',
fail: '連接埠 {{port}} 低於 1024而此服務沒有繫結它的權限這正是上面沒有任何項目在接聽的原因。請在 /etc/systemd/system/bambuddy.service 中加入 AmbientCapabilities=CAP_NET_BIND_SERVICE 並重新啟動,或執行 "sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))"。使用 Docker 時請加入 cap_add: [NET_BIND_SERVICE]。',
},
certificate: {
title: 'TLS 憑證',
pass: '憑證已就緒。請確保已將 Bambuddy CA 憑證(上方)匯入切片軟體的信任庫。',

View file

@ -808,6 +808,13 @@ TimeoutStopSec=30
StandardOutput=journal
StandardError=journal
# Allow binding to privileged ports (322 RTSP, 990 FTPS) for Virtual Printer
# mode. The Bambuddy-only installer has had this since #757; this unit did not,
# so a full-mode install produced a virtual printer whose sockets never opened
# (#2549). Compatible with NoNewPrivileges below — systemd raises the ambient
# set at exec, which is not the escalation that setting forbids.
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict

File diff suppressed because one or more lines are too long

View file

@ -26,7 +26,7 @@
<!-- Splash screens for iOS -->
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
<script type="module" crossorigin src="/assets/index-BClzVk8U.js"></script>
<script type="module" crossorigin src="/assets/index-DLVq-f_h.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-GBTQ2eaA.css">
</head>
<body>