mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-04-28 18:02:38 +02:00

Some checks are pending
Build dev environments / Build dev-env (debian-bookworm) (push) Waiting to run
Build dev environments / Build dev-env (debian-bullseye) (push) Waiting to run
Build dev environments / Build dev-env (debian-trixie) (push) Waiting to run
Build dev environments / Build dev-env (fedora-39) (push) Waiting to run
Build dev environments / Build dev-env (fedora-40) (push) Waiting to run
Build dev environments / Build dev-env (fedora-41) (push) Waiting to run
Build dev environments / Build dev-env (ubuntu-20.04) (push) Waiting to run
Build dev environments / Build dev-env (ubuntu-22.04) (push) Waiting to run
Build dev environments / Build dev-env (ubuntu-23.10) (push) Waiting to run
Build dev environments / Build dev-env (ubuntu-24.04) (push) Waiting to run
Build dev environments / Build dev-env (ubuntu-24.10) (push) Waiting to run
Build dev environments / build-container-image (push) Waiting to run
Check branch conformity / prevent-fixup-commits (push) Waiting to run
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 23.10) (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 23.10) (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 39) (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
Tests / run tests (fedora 39) (push) Blocked by required conditions
Tests / run tests (fedora 40) (push) Blocked by required conditions
Tests / run tests (fedora 41) (push) Blocked by required conditions
Tests / run tests (ubuntu 20.04) (push) Blocked by required conditions
Tests / run tests (ubuntu 22.04) (push) Blocked by required conditions
Tests / run tests (ubuntu 23.10) (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
Scan latest app and container / security-scan-container (push) Waiting to run
Scan latest app and container / security-scan-app (push) Waiting to run
Do not close stderr as part of the Qubes termination logic, since we need to read the debug logs. This shouldn't affect typical termination scenarios, since we expect our disposable qube to be either busy reading from stdin, or writing to stdout. If this is not the case, then forcefully killing the `qrexec-client-vm` process should unblock the qube.
128 lines
4.9 KiB
Python
128 lines
4.9 KiB
Python
import io
|
|
import logging
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import zipfile
|
|
from pathlib import Path
|
|
from typing import IO
|
|
|
|
from ..conversion.common import running_on_qubes
|
|
from ..document import Document
|
|
from ..util import get_resource_path
|
|
from .base import IsolationProvider
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class Qubes(IsolationProvider):
|
|
"""Uses a disposable qube for performing the conversion"""
|
|
|
|
def install(self) -> bool:
|
|
return True
|
|
|
|
def get_max_parallel_conversions(self) -> int:
|
|
return 1
|
|
|
|
def start_doc_to_pixels_proc(self, document: Document) -> subprocess.Popen:
|
|
dev_mode = getattr(sys, "dangerzone_dev", False) is True
|
|
if dev_mode:
|
|
# Use dz.ConvertDev RPC call instead, if we are in development mode.
|
|
# Basically, the change is that we also transfer the necessary Python
|
|
# code as a zipfile, before sending the doc that the user requested.
|
|
qrexec_policy = "dz.ConvertDev"
|
|
stderr = subprocess.PIPE
|
|
else:
|
|
qrexec_policy = "dz.Convert"
|
|
stderr = subprocess.DEVNULL
|
|
|
|
p = subprocess.Popen(
|
|
["/usr/bin/qrexec-client-vm", "@dispvm:dz-dvm", qrexec_policy],
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
stderr=stderr,
|
|
# Start the conversion process in a new session, so that we can later on
|
|
# kill the process group, without killing the controlling script.
|
|
start_new_session=True,
|
|
)
|
|
|
|
if dev_mode:
|
|
assert p.stdin is not None
|
|
# Send the dangerzone module first.
|
|
self.teleport_dz_module(p.stdin)
|
|
|
|
return p
|
|
|
|
def terminate_doc_to_pixels_proc(
|
|
self, document: Document, p: subprocess.Popen
|
|
) -> None:
|
|
"""Terminate a spawned disposable qube.
|
|
|
|
Qubes does not offer a way out of the box to terminate disposable Qubes from
|
|
domU [1]. Our best bet is to close the standard streams of the process, and hope
|
|
that the disposable qube will attempt to read/write to them, and thus receive an
|
|
EOF.
|
|
|
|
There are two ways we can do the above; close the standard streams explicitly,
|
|
or terminate the process. The problem with the latter is that terminating
|
|
`qrexec-client-vm` happens immediately, and we no longer have a way to learn if
|
|
the disposable qube actually terminated. That's why we prefer closing the
|
|
standard streams explicitly, so that we can afterwards use `Popen.wait()` to
|
|
learn if the qube terminated.
|
|
|
|
Note that we don't close the stderr stream because we want to read debug logs
|
|
from it. In the rare case where a qube cannot terminate because it's stuck
|
|
writing at stderr (this is not the expected behavior), we expect that the
|
|
process will still be forcefully killed after the soft termination timeout
|
|
expires.
|
|
|
|
[1]: https://github.com/freedomofpress/dangerzone/issues/563#issuecomment-2034803232
|
|
"""
|
|
if p.stdin:
|
|
p.stdin.close()
|
|
if p.stdout:
|
|
p.stdout.close()
|
|
|
|
def teleport_dz_module(self, wpipe: IO[bytes]) -> None:
|
|
"""Send the dangerzone module to another qube, as a zipfile."""
|
|
# Grab the absolute file path of the dangerzone module.
|
|
import dangerzone as _dz
|
|
|
|
_conv_path = Path(_dz.conversion.__file__).parent
|
|
_src_root = Path(_dz.__file__).parent.parent
|
|
temp_file = io.BytesIO()
|
|
|
|
with zipfile.ZipFile(temp_file, "w") as z:
|
|
z.mkdir("dangerzone/")
|
|
z.writestr("dangerzone/__init__.py", "")
|
|
for root, _, files in os.walk(_conv_path):
|
|
for file in files:
|
|
if file.endswith(".py"):
|
|
file_path = os.path.join(root, file)
|
|
relative_path = os.path.relpath(file_path, _src_root)
|
|
z.write(file_path, relative_path)
|
|
|
|
# Send the following data:
|
|
# 1. The size of the Python zipfile, so that the server can know when to
|
|
# stop.
|
|
# 2. The Python zipfile itself.
|
|
bufsize_bytes = len(temp_file.getvalue()).to_bytes(4, "big")
|
|
wpipe.write(bufsize_bytes)
|
|
wpipe.write(temp_file.getvalue())
|
|
|
|
|
|
def is_qubes_native_conversion() -> bool:
|
|
"""Returns True if the conversion should be run using Qubes OS's diposable
|
|
VMs and False if not."""
|
|
if running_on_qubes():
|
|
if getattr(sys, "dangerzone_dev", False):
|
|
return os.environ.get("QUBES_CONVERSION", "0") == "1"
|
|
|
|
# XXX If Dangerzone is installed check if container image was shipped
|
|
# This disambiguates if it is running a Qubes targetted build or not
|
|
# (Qubes-specific builds don't ship the container image)
|
|
|
|
compressed_container_path = get_resource_path("container.tar.gz")
|
|
return not os.path.exists(compressed_container_path)
|
|
else:
|
|
return False
|