Compare commits

...

6 commits

Author SHA1 Message Date
e0985c53c9
Merge 7f418118e6 into 02602b072a 2025-01-16 11:51:28 +01:00
Alexis Métaireau
7f418118e6
CI: Drop Fedora 39 from the CI checks
Some checks failed
Scan latest app and container / security-scan-container (push) Has been cancelled
Tests / windows (push) Has been cancelled
Tests / macOS (arch64) (push) Has been cancelled
Tests / build-deb (ubuntu 22.04) (push) Has been cancelled
Tests / macOS (x86_64) (push) Has been cancelled
Tests / build-deb (debian bookworm) (push) Has been cancelled
Scan latest app and container / security-scan-app (push) Has been cancelled
Tests / install-deb (ubuntu 22.04) (push) Has been cancelled
Tests / install-deb (ubuntu 24.04) (push) Has been cancelled
Tests / install-deb (ubuntu 24.10) (push) Has been cancelled
Tests / build-deb (debian bullseye) (push) Has been cancelled
Tests / build-deb (debian trixie) (push) Has been cancelled
Tests / build-deb (ubuntu 20.04) (push) Has been cancelled
Tests / build-deb (ubuntu 24.04) (push) Has been cancelled
Tests / build-deb (ubuntu 24.10) (push) Has been cancelled
Tests / install-deb (debian bookworm) (push) Has been cancelled
Tests / install-deb (debian bullseye) (push) Has been cancelled
Tests / install-deb (debian trixie) (push) Has been cancelled
Tests / install-deb (ubuntu 20.04) (push) Has been cancelled
Tests / run tests (fedora 40) (push) Has been cancelled
Tests / run tests (fedora 41) (push) Has been cancelled
Tests / run tests (ubuntu 20.04) (push) Has been cancelled
Tests / run tests (ubuntu 22.04) (push) Has been cancelled
Tests / run tests (ubuntu 24.04) (push) Has been cancelled
Tests / run tests (ubuntu 24.10) (push) Has been cancelled
Tests / build-install-rpm (fedora 40) (push) Has been cancelled
Tests / build-install-rpm (fedora 41) (push) Has been cancelled
Tests / run tests (debian bookworm) (push) Has been cancelled
Tests / run tests (debian bullseye) (push) Has been cancelled
Tests / run tests (debian trixie) (push) Has been cancelled
2025-01-16 11:51:22 +01:00
Alexis Métaireau
02602b072a
Remove intermediate variables for conversion start/end logs
Some checks are pending
Tests / run tests (ubuntu 22.04) (push) Blocked by required conditions
Tests / run tests (ubuntu 24.04) (push) Blocked by required conditions
Tests / run tests (ubuntu 24.10) (push) Blocked by required conditions
Tests / run-lint (push) Waiting to run
Tests / build-container-image (push) Waiting to run
Tests / Download and cache Tesseract data (push) Waiting to run
Tests / windows (push) Blocked by required conditions
Tests / macOS (arch64) (push) Blocked by required conditions
Tests / macOS (x86_64) (push) Blocked by required conditions
Tests / build-deb (debian bookworm) (push) Blocked by required conditions
Tests / build-deb (debian bullseye) (push) Blocked by required conditions
Tests / build-deb (debian trixie) (push) Blocked by required conditions
Tests / build-deb (ubuntu 20.04) (push) Blocked by required conditions
Tests / build-deb (ubuntu 22.04) (push) Blocked by required conditions
Tests / build-deb (ubuntu 24.04) (push) Blocked by required conditions
Tests / build-deb (ubuntu 24.10) (push) Blocked by required conditions
Tests / install-deb (debian bookworm) (push) Blocked by required conditions
Tests / install-deb (debian bullseye) (push) Blocked by required conditions
Tests / install-deb (debian trixie) (push) Blocked by required conditions
Tests / install-deb (ubuntu 20.04) (push) Blocked by required conditions
Tests / install-deb (ubuntu 22.04) (push) Blocked by required conditions
Tests / install-deb (ubuntu 24.04) (push) Blocked by required conditions
Tests / install-deb (ubuntu 24.10) (push) Blocked by required conditions
Tests / build-install-rpm (fedora 40) (push) Blocked by required conditions
Tests / build-install-rpm (fedora 41) (push) Blocked by required conditions
Tests / run tests (debian bookworm) (push) Blocked by required conditions
Tests / run tests (debian bullseye) (push) Blocked by required conditions
Tests / run tests (debian trixie) (push) Blocked by required conditions
Scan latest app and container / security-scan-container (push) Waiting to run
Scan latest app and container / security-scan-app (push) Waiting to run
Also, state that the logs are incomplete in the header.
2025-01-16 11:35:07 +01:00
Alexis Métaireau
acf20ef700
Add a --debug flag to the CLI to help retrieve more logs
When the flag is set, the `RUNSC_DEBUG=1` environment variable is added
to the outer container, and stderr is captured in a separate thread, before printing its output.
2025-01-16 11:35:06 +01:00
Alexis Métaireau
3499010d8e
docs(install): store GPG keys in the base64 format 2025-01-15 19:48:00 +01:00
Alexis Métaireau
2423fc18c5
CI: Store the signature key using the base64 format
The GPG binary format used until now doesn't seem to please `sqv` which
is now used by default on debian trixie.

Fixes #1052
2025-01-15 19:39:02 +01:00
6 changed files with 72 additions and 24 deletions

View file

@ -53,8 +53,13 @@ jobs:
gpg --keyserver hkps://keys.openpgp.org \ gpg --keyserver hkps://keys.openpgp.org \
--no-default-keyring --keyring ./fpf-apt-tools-archive-keyring.gpg \ --no-default-keyring --keyring ./fpf-apt-tools-archive-keyring.gpg \
--recv-keys "DE28 AB24 1FA4 8260 FAC9 B8BA A7C9 B385 2260 4281" --recv-keys "DE28 AB24 1FA4 8260 FAC9 B8BA A7C9 B385 2260 4281"
# Export the GPG key in armor mode because sequoia needs it this way
# (sqv is used on debian trixie by default to check the keys)
mkdir -p /etc/apt/keyrings/ mkdir -p /etc/apt/keyrings/
mv fpf-apt-tools-archive-keyring.gpg /etc/apt/keyrings gpg --no-default-keyring --keyring ./fpf-apt-tools-archive-keyring.gpg \
--armor --export "DE28 AB24 1FA4 8260 FAC9 B8BA A7C9 B385 2260 4281" \
> /etc/apt/keyrings/fpf-apt-tools-archive-keyring.gpg
- name: Add packages.freedom.press to our APT sources - name: Add packages.freedom.press to our APT sources
run: | run: |
@ -75,8 +80,6 @@ jobs:
strategy: strategy:
matrix: matrix:
include: include:
- distro: fedora
version: 39
- distro: fedora - distro: fedora
version: 40 version: 40
- distro: fedora - distro: fedora

View file

@ -94,7 +94,9 @@ gpg --keyserver hkps://keys.openpgp.org \
--no-default-keyring --keyring ./fpf-apt-tools-archive-keyring.gpg \ --no-default-keyring --keyring ./fpf-apt-tools-archive-keyring.gpg \
--recv-keys "DE28 AB24 1FA4 8260 FAC9 B8BA A7C9 B385 2260 4281" --recv-keys "DE28 AB24 1FA4 8260 FAC9 B8BA A7C9 B385 2260 4281"
sudo mkdir -p /etc/apt/keyrings/ sudo mkdir -p /etc/apt/keyrings/
sudo mv fpf-apt-tools-archive-keyring.gpg /etc/apt/keyrings sudo gpg --no-default-keyring --keyring ./fpf-apt-tools-archive-keyring.gpg \
--armor --export "DE28 AB24 1FA4 8260 FAC9 B8BA A7C9 B385 2260 4281" \
> /etc/apt/keyrings/fpf-apt-tools-archive-keyring.gpg
``` ```
Add the URL of the repo in your APT sources: Add the URL of the repo in your APT sources:

View file

@ -42,6 +42,12 @@ def print_header(s: str) -> None:
type=click.UNPROCESSED, type=click.UNPROCESSED,
callback=args.validate_input_filenames, callback=args.validate_input_filenames,
) )
@click.option(
"--debug",
"debug",
flag_value=True,
help="Run Dangerzone in debug mode, to get logs from gVisor.",
)
@click.version_option(version=get_version(), message="%(version)s") @click.version_option(version=get_version(), message="%(version)s")
@errors.handle_document_errors @errors.handle_document_errors
def cli_main( def cli_main(
@ -50,6 +56,7 @@ def cli_main(
filenames: List[str], filenames: List[str],
archive: bool, archive: bool,
dummy_conversion: bool, dummy_conversion: bool,
debug: bool,
) -> None: ) -> None:
setup_logging() setup_logging()
@ -58,7 +65,7 @@ def cli_main(
elif is_qubes_native_conversion(): elif is_qubes_native_conversion():
dangerzone = DangerzoneCore(Qubes()) dangerzone = DangerzoneCore(Qubes())
else: else:
dangerzone = DangerzoneCore(Container()) dangerzone = DangerzoneCore(Container(debug=debug))
display_banner() display_banner()
if len(filenames) == 1 and output_filename: if len(filenames) == 1 and output_filename:

View file

@ -5,7 +5,9 @@ import platform
import signal import signal
import subprocess import subprocess
import sys import sys
import threading
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from io import BytesIO
from typing import IO, Callable, Iterator, Optional from typing import IO, Callable, Iterator, Optional
import fitz import fitz
@ -18,10 +20,6 @@ from ..util import get_tessdata_dir, replace_control_chars
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
MAX_CONVERSION_LOG_CHARS = 150 * 50 # up to ~150 lines of 50 characters
DOC_TO_PIXELS_LOG_START = "----- DOC TO PIXELS LOG START -----"
DOC_TO_PIXELS_LOG_END = "----- DOC TO PIXELS LOG END -----"
TIMEOUT_EXCEPTION = 15 TIMEOUT_EXCEPTION = 15
TIMEOUT_GRACE = 15 TIMEOUT_GRACE = 15
TIMEOUT_FORCE = 5 TIMEOUT_FORCE = 5
@ -75,9 +73,9 @@ def read_int(f: IO[bytes]) -> int:
return int.from_bytes(untrusted_int, "big", signed=False) return int.from_bytes(untrusted_int, "big", signed=False)
def read_debug_text(f: IO[bytes], size: int) -> str: def sanitize_debug_text(text: bytes) -> str:
"""Read arbitrarily long text (for debug purposes), and sanitize it.""" """Read all the buffer and return a sanitized version"""
untrusted_text = f.read(size).decode("ascii", errors="replace") untrusted_text = text.decode("ascii", errors="replace")
return replace_control_chars(untrusted_text, keep_newlines=True) return replace_control_chars(untrusted_text, keep_newlines=True)
@ -86,12 +84,16 @@ class IsolationProvider(ABC):
Abstracts an isolation provider Abstracts an isolation provider
""" """
def __init__(self) -> None: def __init__(self, debug: bool = False) -> None:
if getattr(sys, "dangerzone_dev", False) is True: self.debug = debug
if self.should_capture_stderr():
self.proc_stderr = subprocess.PIPE self.proc_stderr = subprocess.PIPE
else: else:
self.proc_stderr = subprocess.DEVNULL self.proc_stderr = subprocess.DEVNULL
def should_capture_stderr(self) -> bool:
return self.debug or getattr(sys, "dangerzone_dev", False)
@abstractmethod @abstractmethod
def install(self) -> bool: def install(self) -> bool:
pass pass
@ -327,7 +329,11 @@ class IsolationProvider(ABC):
timeout_force: int = TIMEOUT_FORCE, timeout_force: int = TIMEOUT_FORCE,
) -> Iterator[subprocess.Popen]: ) -> Iterator[subprocess.Popen]:
"""Start a conversion process, pass it to the caller, and then clean it up.""" """Start a conversion process, pass it to the caller, and then clean it up."""
# Store the proc stderr in memory
stderr = BytesIO()
p = self.start_doc_to_pixels_proc(document) p = self.start_doc_to_pixels_proc(document)
stderr_thread = self.start_stderr_thread(p, stderr)
if platform.system() != "Windows": if platform.system() != "Windows":
assert os.getpgid(p.pid) != os.getpgid( assert os.getpgid(p.pid) != os.getpgid(
os.getpid() os.getpid()
@ -343,15 +349,40 @@ class IsolationProvider(ABC):
document, p, timeout_grace=timeout_grace, timeout_force=timeout_force document, p, timeout_grace=timeout_grace, timeout_force=timeout_force
) )
# Read the stderr of the process only if: if stderr_thread:
# * Dev mode is enabled. # Wait for the thread to complete. If it's still alive, mention it in the debug log.
# * The process has exited (else we risk hanging). stderr_thread.join(timeout=1)
if getattr(sys, "dangerzone_dev", False) and p.poll() is not None:
assert p.stderr debug_bytes = stderr.getvalue()
debug_log = read_debug_text(p.stderr, MAX_CONVERSION_LOG_CHARS) debug_log = sanitize_debug_text(debug_bytes)
incomplete = "(incomplete) " if stderr_thread.is_alive() else ""
log.info( log.info(
"Conversion output (doc to pixels)\n" "Conversion output (doc to pixels)\n"
f"{DOC_TO_PIXELS_LOG_START}\n" f"----- DOC TO PIXELS LOG START {incomplete}-----\n"
f"{debug_log}" # no need for an extra newline here f"{debug_log}" # no need for an extra newline here
f"{DOC_TO_PIXELS_LOG_END}" "----- DOC TO PIXELS LOG END -----"
) )
def start_stderr_thread(
self, process: subprocess.Popen, stderr: IO[bytes]
) -> Optional[threading.Thread]:
"""Start a thread to read stderr from the process"""
def _stream_stderr(process_stderr: IO[bytes]) -> None:
try:
for line in process_stderr:
stderr.write(line)
except (ValueError, IOError) as e:
log.debug(f"Stderr stream closed: {e}")
if process.stderr:
stderr_thread = threading.Thread(
target=_stream_stderr,
args=(process.stderr,),
daemon=True,
)
stderr_thread.start()
return stderr_thread
return None

View file

@ -168,6 +168,10 @@ class Container(IsolationProvider):
) -> subprocess.Popen: ) -> subprocess.Popen:
container_runtime = container_utils.get_runtime() container_runtime = container_utils.get_runtime()
security_args = self.get_runtime_security_args() security_args = self.get_runtime_security_args()
debug_args = []
if self.debug:
debug_args += ["-e", "RUNSC_DEBUG=1"]
enable_stdin = ["-i"] enable_stdin = ["-i"]
set_name = ["--name", name] set_name = ["--name", name]
prevent_leakage_args = ["--rm"] prevent_leakage_args = ["--rm"]
@ -177,14 +181,14 @@ class Container(IsolationProvider):
args = ( args = (
["run"] ["run"]
+ security_args + security_args
+ debug_args
+ prevent_leakage_args + prevent_leakage_args
+ enable_stdin + enable_stdin
+ set_name + set_name
+ image_name + image_name
+ command + command
) )
args = [container_runtime] + args return self.exec([container_runtime] + args)
return self.exec(args)
def kill_container(self, name: str) -> None: def kill_container(self, name: str) -> None:
"""Terminate a spawned container. """Terminate a spawned container.

View file

@ -71,6 +71,7 @@ class DangerzoneCore(object):
ocr_lang, ocr_lang,
stdout_callback, stdout_callback,
) )
except Exception: except Exception:
log.exception( log.exception(
f"Unexpected error occurred while converting '{document}'" f"Unexpected error occurred while converting '{document}'"