mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-05-18 19:20:35 +02:00
Compare commits
10 commits
e388fe6090
...
88a6b37770
Author | SHA1 | Date | |
---|---|---|---|
![]() |
88a6b37770 | ||
![]() |
fb90243668 | ||
![]() |
9724a16d81 | ||
![]() |
cf43a7a0c4 | ||
![]() |
cae4187550 | ||
![]() |
cfa4478ace | ||
![]() |
2557be9bc0 | ||
![]() |
235d71354a | ||
![]() |
5d49f5abdb | ||
![]() |
0ce7773ca1 |
17 changed files with 796 additions and 66 deletions
27
.github/workflows/ci.yml
vendored
27
.github/workflows/ci.yml
vendored
|
@ -471,3 +471,30 @@ jobs:
|
|||
# file successfully.
|
||||
xvfb-run -s '-ac' ./dev_scripts/env.py --distro ${{ matrix.distro }} --version ${{ matrix.version }} run --dev \
|
||||
bash -c 'cd dangerzone; poetry run make test'
|
||||
|
||||
check-reproducibility:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install dev. dependencies
|
||||
run: |-
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y git python3-poetry --no-install-recommends
|
||||
poetry install --only package
|
||||
|
||||
- name: Verify that the Dockerfile matches the commited template and params
|
||||
run: |-
|
||||
cp Dockerfile Dockerfile.orig
|
||||
make Dockerfile
|
||||
diff Dockerfile.orig Dockerfile
|
||||
|
||||
- name: Build Dangerzone container image
|
||||
run: |
|
||||
python3 ./install/common/build-image.py --no-save
|
||||
|
||||
- name: Reproduce the same container image
|
||||
run: |
|
||||
./dev_scripts/reproduce-image.py
|
||||
|
|
10
.github/workflows/scan.yml
vendored
10
.github/workflows/scan.yml
vendored
|
@ -21,13 +21,17 @@ jobs:
|
|||
sudo apt install pipx
|
||||
pipx install poetry
|
||||
pipx inject poetry poetry-plugin-export
|
||||
poetry install --only package
|
||||
- name: Bump date of Debian snapshot archive
|
||||
run: |
|
||||
date=$(date "+%Y%m%d")
|
||||
sed -i "s/DEBIAN_ARCHIVE_DATE=[0-9]\+/DEBIAN_ARCHIVE_DATE=${date}/" Dockerfile.env
|
||||
make Dockerfile
|
||||
- name: Build container image
|
||||
run: python3 ./install/common/build-image.py --runtime docker --no-save
|
||||
- name: Get image tag
|
||||
id: tag
|
||||
run: |
|
||||
tag=$(docker images dangerzone.rocks/dangerzone --format '{{ .Tag }}')
|
||||
echo "tag=$tag" >> $GITHUB_OUTPUT
|
||||
run: echo "tag=$(cat share/image-id.txt)" >> $GITHUB_OUTPUT
|
||||
# NOTE: Scan first without failing, else we won't be able to read the scan
|
||||
# report.
|
||||
- name: Scan container image (no fail)
|
||||
|
|
38
.grype.yaml
38
.grype.yaml
|
@ -2,10 +2,38 @@
|
|||
# latest release of Dangerzone, and offer our analysis.
|
||||
|
||||
ignore:
|
||||
# CVE-2024-11053
|
||||
# CVE-2023-45853
|
||||
# ==============
|
||||
#
|
||||
# NVD Entry: https://nvd.nist.gov/vuln/detail/CVE-2024-11053
|
||||
# Verdict: Dangerzone is not affected because libcurl is an HTTP client, and
|
||||
# the Dangerzone container does not make any network calls.
|
||||
- vulnerability: CVE-2024-11053
|
||||
# Debian tracker: https://security-tracker.debian.org/tracker/CVE-2023-45853
|
||||
# Verdict: Dangerzone is not affected because the zlib library in Debian is
|
||||
# built in a way that is not vulnerable.
|
||||
- vulnerability: CVE-2023-45853
|
||||
# CVE-2024-38428
|
||||
# ==============
|
||||
#
|
||||
# Debian tracker: https://security-tracker.debian.org/tracker/CVE-2024-38428
|
||||
# Verdict: Dangerzone is not affected because it doesn't use wget in the
|
||||
# container image (which also has no network connectivity).
|
||||
- vulnerability: CVE-2024-38428
|
||||
# CVE-2024-57823
|
||||
# ==============
|
||||
#
|
||||
# Debian tracker: https://security-tracker.debian.org/tracker/CVE-2024-57823
|
||||
# Verdict: Dangerzone is not affected. First things first, LibreOffice is
|
||||
# using this library for parsing RDF metadata in a document [1], and has
|
||||
# issued a fix for the vendored raptor2 package they have for other distros
|
||||
# [2].
|
||||
#
|
||||
# On the other hand, the Debian security team has stated that this is a minor
|
||||
# issue [3], and there's no fix from the developers yet. It seems that the
|
||||
# Debian package is not affected somehow by this CVE, probably due to the way
|
||||
# it's packaged.
|
||||
#
|
||||
# [1] https://wiki.documentfoundation.org/Documentation/DevGuide/Office_Development#RDF_metadata
|
||||
# [2] https://cgit.freedesktop.org/libreoffice/core/commit/?id=2b50dc0e4482ac0ad27d69147b4175e05af4fba4
|
||||
# [2] From https://security-tracker.debian.org/tracker/CVE-2024-57823:
|
||||
#
|
||||
# [bookworm] - raptor2 <postponed> (Minor issue, revisit when fixed upstream)
|
||||
#
|
||||
- vulnerability: CVE-2024-57823
|
||||
|
|
6
BUILD.md
6
BUILD.md
|
@ -515,3 +515,9 @@ poetry run .\install\windows\build-app.bat
|
|||
```
|
||||
|
||||
When you're done you will have `dist\Dangerzone.msi`.
|
||||
|
||||
## Updating the container image
|
||||
|
||||
The Dangezone container image is reproducible. This means that every time we
|
||||
build it, the result will be bit-for-bit the same, with some minor exceptions.
|
||||
Read more on how you can update it in `docs/developer/reproducibility.md`.
|
||||
|
|
154
Dockerfile
154
Dockerfile
|
@ -4,10 +4,10 @@
|
|||
|
||||
ARG DEBIAN_IMAGE_DATE=20250113
|
||||
|
||||
FROM debian:bookworm-${DEBIAN_IMAGE_DATE}-slim
|
||||
FROM debian:bookworm-${DEBIAN_IMAGE_DATE}-slim as dangerzone-image
|
||||
|
||||
ARG GVISOR_ARCHIVE_DATE=20250113
|
||||
ARG DEBIAN_ARCHIVE_DATE=20250120
|
||||
ARG GVISOR_ARCHIVE_DATE=20250120
|
||||
ARG DEBIAN_ARCHIVE_DATE=20250127
|
||||
ARG H2ORESTART_CHECKSUM=7760dc2963332c50d15eee285933ec4b48d6a1de9e0c0f6082946f93090bd132
|
||||
ARG H2ORESTART_VERSION=v0.7.0
|
||||
|
||||
|
@ -44,7 +44,7 @@ RUN \
|
|||
|
||||
# Download H2ORestart from GitHub using a pinned version and hash. Note that
|
||||
# it's available in Debian repos, but not in Bookworm yet.
|
||||
RUN mkdir /libreoffice_ext && cd libreoffice_ext \
|
||||
RUN mkdir /opt/libreoffice_ext && cd /opt/libreoffice_ext \
|
||||
&& H2ORESTART_FILENAME=h2orestart.oxt \
|
||||
&& wget https://github.com/ebandal/H2Orestart/releases/download/$H2ORESTART_VERSION/$H2ORESTART_FILENAME \
|
||||
&& echo "$H2ORESTART_CHECKSUM $H2ORESTART_FILENAME" | sha256sum -c \
|
||||
|
@ -64,18 +64,148 @@ RUN touch /opt/dangerzone/dangerzone/__init__.py
|
|||
# Copy only the Python code, and not any produced .pyc files.
|
||||
COPY conversion/*.py /opt/dangerzone/dangerzone/conversion/
|
||||
|
||||
# Let the entrypoint script write the OCI config for the inner container under
|
||||
# /config.json.
|
||||
RUN touch /config.json
|
||||
RUN chown dangerzone:dangerzone /config.json
|
||||
|
||||
# Switch to the dangerzone user for the rest of the script.
|
||||
USER dangerzone
|
||||
|
||||
# Create a directory that will be used by gVisor as the place where it will
|
||||
# store the state of its containers.
|
||||
RUN mkdir /home/dangerzone/.containers
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# REUSING CONTAINER IMAGES:
|
||||
# Anatomy of a hack
|
||||
# ========================
|
||||
#
|
||||
# The rest of the Dockerfile aims to do one thing: allow the final container
|
||||
# image to actually contain two container images; one for the outer container
|
||||
# (spawned by Podman/Docker Desktop), and one for the inner container (spawned
|
||||
# by gVisor).
|
||||
#
|
||||
# This has already been done in the past, and we explain why and how in the
|
||||
# design document for gVisor integration (should be in
|
||||
# `docs/developer/gvisor.md`). In this iteration, we want to also
|
||||
# achieve the following:
|
||||
#
|
||||
# 1. Have a small final image, by sharing some system paths between the inner
|
||||
# and outer container image using symlinks.
|
||||
# 2. Allow our security scanning tool to see the contents of the inner
|
||||
# container image.
|
||||
# 3. Make the outer container image operational, in the sense that you can use
|
||||
# `apt` commands and perform a conversion with Dangerzone, outside the
|
||||
# gVisor sandbox. This is helpful for debugging purposes.
|
||||
#
|
||||
# Below we'll explain how our design choices are informed by the above
|
||||
# sub-goals.
|
||||
#
|
||||
# First, to achieve a small container image, we basically need to copy `/etc`,
|
||||
# `/usr` and `/opt` from the original Dangerzone image to the **inner**
|
||||
# container image (under `/home/dangerzone/dangerzone-image/rootfs/`)
|
||||
#
|
||||
# That's all we need. The rest of the files play no role, and we can actually
|
||||
# mask them in gVisor's OCI config.
|
||||
#
|
||||
# Second, in order to let our security scanner find the installed packages,
|
||||
# we need to copy the following dirs to the root of the **outer** container
|
||||
# image:
|
||||
# * `/etc`, so that the security scanner can detect the image type and its
|
||||
# sources
|
||||
# * `/var`, so that the security scanner can have access to the APT database.
|
||||
#
|
||||
# IMPORTANT: We don't symlink the `/etc` of the **outer** container image to
|
||||
# the **inner** one, in order to avoid leaking files like
|
||||
# `/etc/{hostname,hosts,resolv.conf}` that Podman/Docker mounts when running
|
||||
# the **outer** container image.
|
||||
#
|
||||
# Third, in order to have an operational Debian image, we are _mostly_ covered
|
||||
# by the dirs we have copied. There's a _rare_ case where during debugging, we
|
||||
# may want to install a system package that has components in `/etc` and
|
||||
# `/var`, which will not be available in the **inner** container image. In that
|
||||
# case, the developer can do the necessary symlinks in the live container.
|
||||
#
|
||||
# FILESYSTEM HIERARCHY
|
||||
# ====================
|
||||
#
|
||||
# The above plan leads to the following filesystem hierarchy:
|
||||
#
|
||||
# Outer container image:
|
||||
#
|
||||
# # ls -l /
|
||||
# lrwxrwxrwx 1 root root 7 Jan 27 10:46 bin -> usr/bin
|
||||
# -rwxr-xr-x 1 root root 7764 Jan 24 08:14 entrypoint.py
|
||||
# drwxr-xr-x 1 root root 4096 Jan 27 10:47 etc
|
||||
# drwxr-xr-x 1 root root 4096 Jan 27 10:46 home
|
||||
# lrwxrwxrwx 1 root root 7 Jan 27 10:46 lib -> usr/lib
|
||||
# lrwxrwxrwx 1 root root 9 Jan 27 10:46 lib64 -> usr/lib64
|
||||
# drwxr-xr-x 2 root root 4096 Jan 27 10:46 root
|
||||
# drwxr-xr-x 1 root root 4096 Jan 27 10:47 run
|
||||
# lrwxrwxrwx 1 root root 8 Jan 27 10:46 sbin -> usr/sbin
|
||||
# drwxrwxrwx 2 root root 4096 Jan 27 10:46 tmp
|
||||
# lrwxrwxrwx 1 root root 44 Jan 27 10:46 usr -> /home/dangerzone/dangerzone-image/rootfs/usr
|
||||
# drwxr-xr-x 11 root root 4096 Jan 27 10:47 var
|
||||
#
|
||||
# Inner container image:
|
||||
#
|
||||
# # ls -l /home/dangerzone/dangerzone-image/rootfs/
|
||||
# total 12
|
||||
# lrwxrwxrwx 1 root root 7 Jan 27 10:47 bin -> usr/bin
|
||||
# drwxr-xr-x 43 root root 4096 Jan 27 10:46 etc
|
||||
# lrwxrwxrwx 1 root root 7 Jan 27 10:47 lib -> usr/lib
|
||||
# lrwxrwxrwx 1 root root 9 Jan 27 10:47 lib64 -> usr/lib64
|
||||
# drwxr-xr-x 4 root root 4096 Jan 27 10:47 opt
|
||||
# drwxr-xr-x 12 root root 4096 Jan 27 10:47 usr
|
||||
#
|
||||
# SYMLINKING /USR
|
||||
# ===============
|
||||
#
|
||||
# It's surprisingly difficult (maybe even borderline impossible), to symlink
|
||||
# `/usr` to a different path during image build. The problem is that /usr
|
||||
# is very sensitive, and you can't manipulate it in a live system. That is, I
|
||||
# haven't found a way to do the following, or something equivalent:
|
||||
#
|
||||
# rm -r /usr && ln -s /home/dangerzone/dangerzone-image/rootfs/usr/ /usr
|
||||
#
|
||||
# The `ln` binary, even if you specify it by its full path, cannot run
|
||||
# (probably because `ld-linux.so` can't be found). For this reason, we have
|
||||
# to create the symlinks beforehand, in a previous build stage. Then, in an
|
||||
# empty contianer image (scratch images), we can copy these symlinks and the
|
||||
# /usr, and stich everything together.
|
||||
###############################################################################
|
||||
|
||||
# Create the filesystem hierarchy that will be used to symlink /usr.
|
||||
|
||||
RUN mkdir /new_root
|
||||
RUN mkdir /new_root/root /new_root/run /new_root/tmp
|
||||
RUN chmod 777 /new_root/tmp
|
||||
RUN ln -s /home/dangerzone/dangerzone-image/rootfs/usr /new_root/usr
|
||||
RUN ln -s usr/bin /new_root/bin
|
||||
RUN ln -s usr/lib /new_root/lib
|
||||
RUN ln -s usr/lib64 /new_root/lib64
|
||||
RUN ln -s usr/sbin /new_root/sbin
|
||||
|
||||
## Final image
|
||||
|
||||
FROM scratch
|
||||
|
||||
# Copy the filesystem hierarchy that we created in the previous stage, so that
|
||||
# /usr can be a symlink.
|
||||
COPY --from=dangerzone-image /new_root/ /
|
||||
|
||||
# Copy the bare minimum to run Dangerzone in the inner container image.
|
||||
COPY --from=dangerzone-image /etc/ /home/dangerzone/dangerzone-image/rootfs/etc/
|
||||
COPY --from=dangerzone-image /opt/ /home/dangerzone/dangerzone-image/rootfs/opt/
|
||||
COPY --from=dangerzone-image /usr/ /home/dangerzone/dangerzone-image/rootfs/usr/
|
||||
RUN ln -s usr/bin /home/dangerzone/dangerzone-image/rootfs/bin
|
||||
RUN ln -s usr/lib /home/dangerzone/dangerzone-image/rootfs/lib
|
||||
RUN ln -s usr/lib64 /home/dangerzone/dangerzone-image/rootfs/lib64
|
||||
|
||||
# Copy the bare minimum to let the security scanner find vulnerabilities.
|
||||
COPY --from=dangerzone-image /etc/ /etc/
|
||||
COPY --from=dangerzone-image /var/ /var/
|
||||
|
||||
# Allow our entrypoint script to make changes in the following folders.
|
||||
RUN chown dangerzone:dangerzone /home/dangerzone /home/dangerzone/dangerzone-image/
|
||||
|
||||
# Switch to the dangerzone user for the rest of the script.
|
||||
USER dangerzone
|
||||
|
||||
COPY container_helpers/entrypoint.py /
|
||||
|
||||
ENTRYPOINT ["/entrypoint.py"]
|
||||
|
|
9
Dockerfile.env
Normal file
9
Dockerfile.env
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Can be bumped to the latest date in https://hub.docker.com/_/debian/tags?name=bookworm-
|
||||
DEBIAN_IMAGE_DATE=20250113
|
||||
# Can be bumped to today's date
|
||||
DEBIAN_ARCHIVE_DATE=20250127
|
||||
# Can be bumped to the latest date in https://github.com/google/gvisor/tags
|
||||
GVISOR_ARCHIVE_DATE=20250120
|
||||
# Can be bumped to the latest version and checksum from https://github.com/ebandal/H2Orestart/releases
|
||||
H2ORESTART_CHECKSUM=7760dc2963332c50d15eee285933ec4b48d6a1de9e0c0f6082946f93090bd132
|
||||
H2ORESTART_VERSION=v0.7.0
|
211
Dockerfile.in
Normal file
211
Dockerfile.in
Normal file
|
@ -0,0 +1,211 @@
|
|||
# NOTE: Updating the packages to their latest versions requires bumping the
|
||||
# Dockerfile args below. For more info about this file, read
|
||||
# docs/developer/reproducibility.md.
|
||||
|
||||
ARG DEBIAN_IMAGE_DATE={{DEBIAN_IMAGE_DATE}}
|
||||
|
||||
FROM debian:bookworm-${DEBIAN_IMAGE_DATE}-slim as dangerzone-image
|
||||
|
||||
ARG GVISOR_ARCHIVE_DATE={{GVISOR_ARCHIVE_DATE}}
|
||||
ARG DEBIAN_ARCHIVE_DATE={{DEBIAN_ARCHIVE_DATE}}
|
||||
ARG H2ORESTART_CHECKSUM={{H2ORESTART_CHECKSUM}}
|
||||
ARG H2ORESTART_VERSION={{H2ORESTART_VERSION}}
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# The following way of installing packages is taken from
|
||||
# https://github.com/reproducible-containers/repro-sources-list.sh/blob/master/Dockerfile.debian-12,
|
||||
# and adapted to allow installing gVisor from each own repo as well.
|
||||
RUN \
|
||||
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
--mount=type=bind,source=./container_helpers/repro-sources-list.sh,target=/usr/local/bin/repro-sources-list.sh \
|
||||
--mount=type=bind,source=./container_helpers/gvisor.key,target=/tmp/gvisor.key \
|
||||
: "Hacky way to set a date for the Debian snapshot repos" && \
|
||||
touch -d ${DEBIAN_ARCHIVE_DATE} /etc/apt/sources.list.d/debian.sources && \
|
||||
touch -d ${DEBIAN_ARCHIVE_DATE} /etc/apt/sources.list && \
|
||||
repro-sources-list.sh && \
|
||||
: "Setup APT to install gVisor from its separate APT repo" && \
|
||||
apt-get update && \
|
||||
apt-get upgrade -y && \
|
||||
apt-get install -y --no-install-recommends apt-transport-https ca-certificates gnupg && \
|
||||
gpg -o /usr/share/keyrings/gvisor-archive-keyring.gpg --dearmor /tmp/gvisor.key && \
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases ${GVISOR_ARCHIVE_DATE} main" > /etc/apt/sources.list.d/gvisor.list && \
|
||||
: "Install the necessary gVisor and Dangerzone dependencies" && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
python3 python3-fitz libreoffice-nogui libreoffice-java-common \
|
||||
python3 python3-magic default-jre-headless fonts-noto-cjk fonts-dejavu \
|
||||
runsc unzip wget && \
|
||||
: "Clean up for improving reproducibility (optional)" && \
|
||||
rm -rf /var/cache/fontconfig/ && \
|
||||
rm -rf /etc/ssl/certs/java/cacerts && \
|
||||
rm -rf /var/log/* /var/cache/ldconfig/aux-cache
|
||||
|
||||
# Download H2ORestart from GitHub using a pinned version and hash. Note that
|
||||
# it's available in Debian repos, but not in Bookworm yet.
|
||||
RUN mkdir /opt/libreoffice_ext && cd /opt/libreoffice_ext \
|
||||
&& H2ORESTART_FILENAME=h2orestart.oxt \
|
||||
&& wget https://github.com/ebandal/H2Orestart/releases/download/$H2ORESTART_VERSION/$H2ORESTART_FILENAME \
|
||||
&& echo "$H2ORESTART_CHECKSUM $H2ORESTART_FILENAME" | sha256sum -c \
|
||||
&& install -dm777 "/usr/lib/libreoffice/share/extensions/" \
|
||||
&& rm /root/.wget-hsts
|
||||
|
||||
# Create an unprivileged user both for gVisor and for running Dangerzone.
|
||||
RUN addgroup --gid 1000 dangerzone
|
||||
RUN adduser --uid 1000 --ingroup dangerzone --shell /bin/true \
|
||||
--disabled-password --home /home/dangerzone dangerzone
|
||||
|
||||
# Copy Dangerzone's conversion logic under /opt/dangerzone, and allow Python to
|
||||
# import it.
|
||||
RUN mkdir -p /opt/dangerzone/dangerzone
|
||||
RUN touch /opt/dangerzone/dangerzone/__init__.py
|
||||
|
||||
# Copy only the Python code, and not any produced .pyc files.
|
||||
COPY conversion/*.py /opt/dangerzone/dangerzone/conversion/
|
||||
|
||||
# Create a directory that will be used by gVisor as the place where it will
|
||||
# store the state of its containers.
|
||||
RUN mkdir /home/dangerzone/.containers
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# REUSING CONTAINER IMAGES:
|
||||
# Anatomy of a hack
|
||||
# ========================
|
||||
#
|
||||
# The rest of the Dockerfile aims to do one thing: allow the final container
|
||||
# image to actually contain two container images; one for the outer container
|
||||
# (spawned by Podman/Docker Desktop), and one for the inner container (spawned
|
||||
# by gVisor).
|
||||
#
|
||||
# This has already been done in the past, and we explain why and how in the
|
||||
# design document for gVisor integration (should be in
|
||||
# `docs/developer/gvisor.md`). In this iteration, we want to also
|
||||
# achieve the following:
|
||||
#
|
||||
# 1. Have a small final image, by sharing some system paths between the inner
|
||||
# and outer container image using symlinks.
|
||||
# 2. Allow our security scanning tool to see the contents of the inner
|
||||
# container image.
|
||||
# 3. Make the outer container image operational, in the sense that you can use
|
||||
# `apt` commands and perform a conversion with Dangerzone, outside the
|
||||
# gVisor sandbox. This is helpful for debugging purposes.
|
||||
#
|
||||
# Below we'll explain how our design choices are informed by the above
|
||||
# sub-goals.
|
||||
#
|
||||
# First, to achieve a small container image, we basically need to copy `/etc`,
|
||||
# `/usr` and `/opt` from the original Dangerzone image to the **inner**
|
||||
# container image (under `/home/dangerzone/dangerzone-image/rootfs/`)
|
||||
#
|
||||
# That's all we need. The rest of the files play no role, and we can actually
|
||||
# mask them in gVisor's OCI config.
|
||||
#
|
||||
# Second, in order to let our security scanner find the installed packages,
|
||||
# we need to copy the following dirs to the root of the **outer** container
|
||||
# image:
|
||||
# * `/etc`, so that the security scanner can detect the image type and its
|
||||
# sources
|
||||
# * `/var`, so that the security scanner can have access to the APT database.
|
||||
#
|
||||
# IMPORTANT: We don't symlink the `/etc` of the **outer** container image to
|
||||
# the **inner** one, in order to avoid leaking files like
|
||||
# `/etc/{hostname,hosts,resolv.conf}` that Podman/Docker mounts when running
|
||||
# the **outer** container image.
|
||||
#
|
||||
# Third, in order to have an operational Debian image, we are _mostly_ covered
|
||||
# by the dirs we have copied. There's a _rare_ case where during debugging, we
|
||||
# may want to install a system package that has components in `/etc` and
|
||||
# `/var`, which will not be available in the **inner** container image. In that
|
||||
# case, the developer can do the necessary symlinks in the live container.
|
||||
#
|
||||
# FILESYSTEM HIERARCHY
|
||||
# ====================
|
||||
#
|
||||
# The above plan leads to the following filesystem hierarchy:
|
||||
#
|
||||
# Outer container image:
|
||||
#
|
||||
# # ls -l /
|
||||
# lrwxrwxrwx 1 root root 7 Jan 27 10:46 bin -> usr/bin
|
||||
# -rwxr-xr-x 1 root root 7764 Jan 24 08:14 entrypoint.py
|
||||
# drwxr-xr-x 1 root root 4096 Jan 27 10:47 etc
|
||||
# drwxr-xr-x 1 root root 4096 Jan 27 10:46 home
|
||||
# lrwxrwxrwx 1 root root 7 Jan 27 10:46 lib -> usr/lib
|
||||
# lrwxrwxrwx 1 root root 9 Jan 27 10:46 lib64 -> usr/lib64
|
||||
# drwxr-xr-x 2 root root 4096 Jan 27 10:46 root
|
||||
# drwxr-xr-x 1 root root 4096 Jan 27 10:47 run
|
||||
# lrwxrwxrwx 1 root root 8 Jan 27 10:46 sbin -> usr/sbin
|
||||
# drwxrwxrwx 2 root root 4096 Jan 27 10:46 tmp
|
||||
# lrwxrwxrwx 1 root root 44 Jan 27 10:46 usr -> /home/dangerzone/dangerzone-image/rootfs/usr
|
||||
# drwxr-xr-x 11 root root 4096 Jan 27 10:47 var
|
||||
#
|
||||
# Inner container image:
|
||||
#
|
||||
# # ls -l /home/dangerzone/dangerzone-image/rootfs/
|
||||
# total 12
|
||||
# lrwxrwxrwx 1 root root 7 Jan 27 10:47 bin -> usr/bin
|
||||
# drwxr-xr-x 43 root root 4096 Jan 27 10:46 etc
|
||||
# lrwxrwxrwx 1 root root 7 Jan 27 10:47 lib -> usr/lib
|
||||
# lrwxrwxrwx 1 root root 9 Jan 27 10:47 lib64 -> usr/lib64
|
||||
# drwxr-xr-x 4 root root 4096 Jan 27 10:47 opt
|
||||
# drwxr-xr-x 12 root root 4096 Jan 27 10:47 usr
|
||||
#
|
||||
# SYMLINKING /USR
|
||||
# ===============
|
||||
#
|
||||
# It's surprisingly difficult (maybe even borderline impossible), to symlink
|
||||
# `/usr` to a different path during image build. The problem is that /usr
|
||||
# is very sensitive, and you can't manipulate it in a live system. That is, I
|
||||
# haven't found a way to do the following, or something equivalent:
|
||||
#
|
||||
# rm -r /usr && ln -s /home/dangerzone/dangerzone-image/rootfs/usr/ /usr
|
||||
#
|
||||
# The `ln` binary, even if you specify it by its full path, cannot run
|
||||
# (probably because `ld-linux.so` can't be found). For this reason, we have
|
||||
# to create the symlinks beforehand, in a previous build stage. Then, in an
|
||||
# empty contianer image (scratch images), we can copy these symlinks and the
|
||||
# /usr, and stich everything together.
|
||||
###############################################################################
|
||||
|
||||
# Create the filesystem hierarchy that will be used to symlink /usr.
|
||||
|
||||
RUN mkdir /new_root
|
||||
RUN mkdir /new_root/root /new_root/run /new_root/tmp
|
||||
RUN chmod 777 /new_root/tmp
|
||||
RUN ln -s /home/dangerzone/dangerzone-image/rootfs/usr /new_root/usr
|
||||
RUN ln -s usr/bin /new_root/bin
|
||||
RUN ln -s usr/lib /new_root/lib
|
||||
RUN ln -s usr/lib64 /new_root/lib64
|
||||
RUN ln -s usr/sbin /new_root/sbin
|
||||
|
||||
## Final image
|
||||
|
||||
FROM scratch
|
||||
|
||||
# Copy the filesystem hierarchy that we created in the previous stage, so that
|
||||
# /usr can be a symlink.
|
||||
COPY --from=dangerzone-image /new_root/ /
|
||||
|
||||
# Copy the bare minimum to run Dangerzone in the inner container image.
|
||||
COPY --from=dangerzone-image /etc/ /home/dangerzone/dangerzone-image/rootfs/etc/
|
||||
COPY --from=dangerzone-image /opt/ /home/dangerzone/dangerzone-image/rootfs/opt/
|
||||
COPY --from=dangerzone-image /usr/ /home/dangerzone/dangerzone-image/rootfs/usr/
|
||||
RUN ln -s usr/bin /home/dangerzone/dangerzone-image/rootfs/bin
|
||||
RUN ln -s usr/lib /home/dangerzone/dangerzone-image/rootfs/lib
|
||||
RUN ln -s usr/lib64 /home/dangerzone/dangerzone-image/rootfs/lib64
|
||||
|
||||
# Copy the bare minimum to let the security scanner find vulnerabilities.
|
||||
COPY --from=dangerzone-image /etc/ /etc/
|
||||
COPY --from=dangerzone-image /var/ /var/
|
||||
|
||||
# Allow our entrypoint script to make changes in the following folders.
|
||||
RUN chown dangerzone:dangerzone /home/dangerzone /home/dangerzone/dangerzone-image/
|
||||
|
||||
# Switch to the dangerzone user for the rest of the script.
|
||||
USER dangerzone
|
||||
|
||||
COPY container_helpers/entrypoint.py /
|
||||
|
||||
ENTRYPOINT ["/entrypoint.py"]
|
3
Makefile
3
Makefile
|
@ -47,6 +47,9 @@ test-large: test-large-init ## Run large test set
|
|||
python -m pytest --tb=no tests/test_large_set.py::TestLargeSet -v $(JUNIT_FLAGS) --junitxml=$(TEST_LARGE_RESULTS)
|
||||
python $(TEST_LARGE_RESULTS)/report.py $(TEST_LARGE_RESULTS)
|
||||
|
||||
Dockerfile: Dockerfile.env Dockerfile.in
|
||||
poetry run jinja2 Dockerfile.in Dockerfile.env > Dockerfile
|
||||
|
||||
.PHONY: build-clean
|
||||
build-clean:
|
||||
doit clean
|
||||
|
|
|
@ -15,6 +15,7 @@ Here is a list of tasks that should be done before issuing the release:
|
|||
- [ ] Update the "Version" field in `install/linux/dangerzone.spec`
|
||||
- [ ] Bump the Debian version by adding a new changelog entry in `debian/changelog`
|
||||
- [ ] [Bump the minimum Docker Desktop versions](https://github.com/freedomofpress/dangerzone/blob/main/RELEASE.md#bump-the-minimum-docker-desktop-version) in `isolation_provider/container.py`
|
||||
- [ ] Bump the dates and versions in the `Dockerfile`
|
||||
- [ ] Update screenshot in `README.md`, if necessary
|
||||
- [ ] CHANGELOG.md should be updated to include a list of all major changes since the last release
|
||||
- [ ] A draft release should be created. Copy the release notes text from the template at [`docs/templates/release-notes`](https://github.com/freedomofpress/dangerzone/tree/main/docs/templates/)
|
||||
|
|
|
@ -56,13 +56,31 @@ oci_config: dict[str, typing.Any] = {
|
|||
{"type": "RLIMIT_NOFILE", "hard": 4096, "soft": 4096},
|
||||
],
|
||||
},
|
||||
"root": {"path": "/", "readonly": True},
|
||||
"root": {"path": "rootfs", "readonly": True},
|
||||
"hostname": "dangerzone",
|
||||
"mounts": [
|
||||
# Mask almost every system directory of the outer container, by mounting tmpfs
|
||||
# on top of them. This is done to avoid leaking any sensitive information,
|
||||
# either mounted by Podman/Docker, or when gVisor runs, since we reuse the same
|
||||
# rootfs. We basically mask everything except for `/usr`, `/bin`, `/lib`,
|
||||
# `/etc`, and `/opt`.
|
||||
#
|
||||
# Note that we set `--root /home/dangerzone/.containers` for the directory where
|
||||
# gVisor will create files at runtime, which means that in principle, we are
|
||||
# covered by the masking of `/home/dangerzone` that follows below.
|
||||
#
|
||||
# Finally, note that the following list has been taken from the dirs in our
|
||||
# container image, and double-checked against the top-level dirs listed in the
|
||||
# Filesystem Hierarchy Standard (FHS) [1]. It would be nice to have an allowlist
|
||||
# approach instead of a denylist, but FHS is such an old standard that we don't
|
||||
# expect any new top-level dirs to pop up any time soon.
|
||||
#
|
||||
# [1] https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard
|
||||
{
|
||||
"destination": "/proc",
|
||||
"type": "proc",
|
||||
"source": "proc",
|
||||
"destination": "/boot",
|
||||
"type": "tmpfs",
|
||||
"source": "tmpfs",
|
||||
"options": ["nosuid", "noexec", "nodev", "ro"],
|
||||
},
|
||||
{
|
||||
"destination": "/dev",
|
||||
|
@ -70,6 +88,53 @@ oci_config: dict[str, typing.Any] = {
|
|||
"source": "tmpfs",
|
||||
"options": ["nosuid", "noexec", "nodev"],
|
||||
},
|
||||
{
|
||||
"destination": "/home",
|
||||
"type": "tmpfs",
|
||||
"source": "tmpfs",
|
||||
"options": ["nosuid", "noexec", "nodev", "ro"],
|
||||
},
|
||||
{
|
||||
"destination": "/media",
|
||||
"type": "tmpfs",
|
||||
"source": "tmpfs",
|
||||
"options": ["nosuid", "noexec", "nodev", "ro"],
|
||||
},
|
||||
{
|
||||
"destination": "/mnt",
|
||||
"type": "tmpfs",
|
||||
"source": "tmpfs",
|
||||
"options": ["nosuid", "noexec", "nodev", "ro"],
|
||||
},
|
||||
{
|
||||
"destination": "/proc",
|
||||
"type": "proc",
|
||||
"source": "proc",
|
||||
},
|
||||
{
|
||||
"destination": "/root",
|
||||
"type": "tmpfs",
|
||||
"source": "tmpfs",
|
||||
"options": ["nosuid", "noexec", "nodev", "ro"],
|
||||
},
|
||||
{
|
||||
"destination": "/run",
|
||||
"type": "tmpfs",
|
||||
"source": "tmpfs",
|
||||
"options": ["nosuid", "noexec", "nodev"],
|
||||
},
|
||||
{
|
||||
"destination": "/sbin",
|
||||
"type": "tmpfs",
|
||||
"source": "tmpfs",
|
||||
"options": ["nosuid", "noexec", "nodev", "ro"],
|
||||
},
|
||||
{
|
||||
"destination": "/srv",
|
||||
"type": "tmpfs",
|
||||
"source": "tmpfs",
|
||||
"options": ["nosuid", "noexec", "nodev", "ro"],
|
||||
},
|
||||
{
|
||||
"destination": "/sys",
|
||||
"type": "tmpfs",
|
||||
|
@ -82,6 +147,12 @@ oci_config: dict[str, typing.Any] = {
|
|||
"source": "tmpfs",
|
||||
"options": ["nosuid", "noexec", "nodev"],
|
||||
},
|
||||
{
|
||||
"destination": "/var",
|
||||
"type": "tmpfs",
|
||||
"source": "tmpfs",
|
||||
"options": ["nosuid", "noexec", "nodev"],
|
||||
},
|
||||
# LibreOffice needs a writable home directory, so just mount a tmpfs
|
||||
# over it.
|
||||
{
|
||||
|
@ -133,7 +204,7 @@ if os.environ.get("RUNSC_DEBUG"):
|
|||
json.dump(oci_config, sys.stderr, indent=2, sort_keys=True)
|
||||
# json.dump doesn't print a trailing newline, so print one here:
|
||||
log("")
|
||||
with open("/config.json", "w") as oci_config_out:
|
||||
with open("/home/dangerzone/dangerzone-image/config.json", "w") as oci_config_out:
|
||||
json.dump(oci_config, oci_config_out, indent=2, sort_keys=True)
|
||||
|
||||
# Run gVisor.
|
||||
|
@ -150,7 +221,7 @@ if os.environ.get("RUNSC_DEBUG"):
|
|||
runsc_argv += ["--debug=true", "--alsologtostderr=true"]
|
||||
if os.environ.get("RUNSC_FLAGS"):
|
||||
runsc_argv += [x for x in shlex.split(os.environ.get("RUNSC_FLAGS", "")) if x]
|
||||
runsc_argv += ["run", "--bundle=/", "dangerzone"]
|
||||
runsc_argv += ["run", "--bundle=/home/dangerzone/dangerzone-image", "dangerzone"]
|
||||
log(
|
||||
"Running gVisor with command line: {}", " ".join(shlex.quote(s) for s in runsc_argv)
|
||||
)
|
||||
|
|
|
@ -253,7 +253,7 @@ class DocumentToPixels(DangerzoneConverter):
|
|||
"unzip",
|
||||
"-d",
|
||||
f"/usr/lib/libreoffice/share/extensions/{libreoffice_ext}/",
|
||||
f"/libreoffice_ext/{libreoffice_ext}",
|
||||
f"/opt/libreoffice_ext/{libreoffice_ext}",
|
||||
]
|
||||
await self.run_command(
|
||||
unzip_args,
|
||||
|
|
180
dev_scripts/reproduce-image.py
Executable file
180
dev_scripts/reproduce-image.py
Executable file
|
@ -0,0 +1,180 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import logging
|
||||
import pathlib
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
DIFFOCI_URL = "https://github.com/reproducible-containers/diffoci/releases/download/v0.1.5/diffoci-v0.1.5.linux-amd64"
|
||||
DIFFOCI_CHECKSUM = "01d25fe690196945a6bd510d30559338aa489c034d3a1b895a0d82a4b860698f"
|
||||
DIFFOCI_PATH = (
|
||||
pathlib.Path.home() / ".local" / "share" / "dangerzone-dev" / "helpers" / "diffoci"
|
||||
)
|
||||
IMAGE_NAME = "dangerzone.rocks/dangerzone"
|
||||
|
||||
|
||||
def run(*args):
|
||||
"""Simple function that runs a command, validates it, and returns the output"""
|
||||
logger.debug(f"Running command: {' '.join(args)}")
|
||||
return subprocess.run(
|
||||
args,
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
).stdout
|
||||
|
||||
|
||||
def git_commit_get():
|
||||
return run("git", "rev-parse", "--short", "HEAD").decode().strip()
|
||||
|
||||
|
||||
def git_determine_tag():
|
||||
return run("git", "describe", "--long", "--first-parent").decode().strip()[1:]
|
||||
|
||||
|
||||
def git_verify(commit, source):
|
||||
if not commit in source:
|
||||
raise RuntimeError(
|
||||
f"Image '{source}' does not seem to be built from commit '{commit}'"
|
||||
)
|
||||
|
||||
|
||||
def diffoci_hash_matches(diffoci):
|
||||
"""Check if the hash of the downloaded diffoci bin matches the expected one."""
|
||||
m = hashlib.sha256()
|
||||
m.update(diffoci)
|
||||
diffoci_checksum = m.hexdigest()
|
||||
return diffoci_checksum == DIFFOCI_CHECKSUM
|
||||
|
||||
|
||||
def diffoci_is_installed():
|
||||
"""Determine if diffoci has been installed.
|
||||
|
||||
Determine if diffoci has been installed, by checking if the binary exists, and if
|
||||
its hash is the expected one. If the binary exists but the hash is different, then
|
||||
this is a sign that we need to update the local diffoci binary.
|
||||
"""
|
||||
if not DIFFOCI_PATH.exists():
|
||||
return False
|
||||
return diffoci_hash_matches(DIFFOCI_PATH.open("rb").read())
|
||||
|
||||
|
||||
def diffoci_download():
|
||||
"""Download the diffoci tool, based on a URL and its checksum."""
|
||||
with urllib.request.urlopen(DIFFOCI_URL) as f:
|
||||
diffoci_bin = f.read()
|
||||
|
||||
if not diffoci_hash_matches(diffoci_bin):
|
||||
raise ValueError(
|
||||
"Unexpected checksum for downloaded diffoci binary:"
|
||||
f" {diffoci_checksum} !={DIFFOCI_CHECKSUM}"
|
||||
)
|
||||
|
||||
DIFFOCI_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
DIFFOCI_PATH.open("wb+").write(diffoci_bin)
|
||||
DIFFOCI_PATH.chmod(DIFFOCI_PATH.stat().st_mode | stat.S_IEXEC)
|
||||
|
||||
|
||||
def diffoci_diff(source, local_target):
|
||||
"""Diff the source image against the recently built target image using diffoci."""
|
||||
target = f"podman://{local_target}"
|
||||
try:
|
||||
return run(
|
||||
str(DIFFOCI_PATH),
|
||||
"diff",
|
||||
source,
|
||||
target,
|
||||
"--semantic",
|
||||
"--verbose",
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
error = e.stdout.decode()
|
||||
raise RuntimeError(
|
||||
f"Could not rebuild an identical image to {source}. Diffoci report:\n{error}"
|
||||
)
|
||||
|
||||
|
||||
def build_image(tag, use_cache=False):
|
||||
"""Build the Dangerzone container image with a special tag."""
|
||||
run(
|
||||
"python3",
|
||||
"./install/common/build-image.py",
|
||||
"--no-save",
|
||||
"--use-cache",
|
||||
str(use_cache),
|
||||
"--tag",
|
||||
tag,
|
||||
)
|
||||
|
||||
|
||||
def parse_args():
|
||||
image_tag = git_determine_tag()
|
||||
# TODO: Remove the local "podman://" prefix once we have started pushing images to a
|
||||
# remote.
|
||||
default_image_name = f"podman://{IMAGE_NAME}:{image_tag}"
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog=sys.argv[0],
|
||||
description="Dev script for verifying container image reproducibility",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source",
|
||||
default=default_image_name,
|
||||
help=(
|
||||
"The name of the image that you want to reproduce. If the image resides in"
|
||||
" the local Docker / Podman engine, you can prefix it with podman:// or"
|
||||
f" docker:// accordingly (default: {default_image_name})"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--use-cache",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Whether to reuse the build cache (off by default for better reproducibility)",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format="%(asctime)s - %(levelname)s - %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
args = parse_args()
|
||||
|
||||
logger.info(f"Ensuring that current Git commit matches image '{args.source}'")
|
||||
commit = git_commit_get()
|
||||
git_verify(commit, args.source)
|
||||
|
||||
if not diffoci_is_installed():
|
||||
logger.info(f"Downloading diffoci helper from {DIFFOCI_URL}")
|
||||
diffoci_download()
|
||||
|
||||
tag = f"reproduce-{commit}"
|
||||
target = f"{IMAGE_NAME}:{tag}"
|
||||
logger.info(f"Building container image and tagging it as '{target}'")
|
||||
build_image(tag, args.use_cache)
|
||||
|
||||
logger.info(
|
||||
f"Ensuring that source image '{args.source}' is semantically identical with"
|
||||
f" built image '{target}'"
|
||||
)
|
||||
try:
|
||||
diffoci_diff(args.source, target)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(
|
||||
f"Could not reproduce image {args.source} for commit {commit}"
|
||||
)
|
||||
breakpoint()
|
||||
|
||||
logger.info(f"Successfully reproduced image '{args.source}' from commit '{commit}'")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
67
docs/developer/reproducibility.md
Normal file
67
docs/developer/reproducibility.md
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Reproducible builds
|
||||
|
||||
We want to improve the transparency and auditability of our build artifacts, and
|
||||
a way to achieve this is via reproducible builds. For a broader understanding of
|
||||
what reproducible builds entail, check out https://reproducible-builds.org/.
|
||||
|
||||
Our build artifacts consist of:
|
||||
* Container images (`amd64` and `arm64` architectures)
|
||||
* macOS installers (for Intel and Apple Silicon CPUs)
|
||||
* Windows installer
|
||||
* Fedora packages (for regular Fedora distros and Qubes)
|
||||
* Debian packages (for Debian and Ubuntu)
|
||||
|
||||
As of writing this, only the following artifacts are reproducible:
|
||||
* Container images (see [#1047](https://github.com/freedomofpress/dangerzone/issues/1047))
|
||||
|
||||
In the following sections, we'll mention some specifics about enforcing
|
||||
reproducibility for each artifact type.
|
||||
|
||||
## Container image
|
||||
|
||||
### Updating the image
|
||||
|
||||
The fact that our image is reproducible also means that it's frozen in time.
|
||||
This means that rebuilding the image without updating our Dockerfile will
|
||||
**not** receive security updates.
|
||||
|
||||
Here are the necessary variables that make up our image in the `Dockerfile.env`
|
||||
file:
|
||||
* `DEBIAN_IMAGE_DATE`: The date that the Debian container image was released
|
||||
* `DEBIAN_ARCHIVE_DATE`: The Debian snapshot repo that we want to use
|
||||
* `GVISOR_ARCHIVE_DATE`: The gVisor APT repo that we want to use
|
||||
* `H2ORESTART_CHECKSUM`: The SHA-256 checksum of the H2ORestart plugin
|
||||
* `H2ORESTART_VERSION`: The version of the H2ORestart plugin
|
||||
|
||||
If you update these values in `Dockerfile.env`, you must also create a new
|
||||
Dockerfile with:
|
||||
|
||||
```
|
||||
make Dockerfile
|
||||
```
|
||||
|
||||
Updating `Dockerfile` without bumping `Dockerfile.in` is detected and should
|
||||
trigger a CI error.
|
||||
|
||||
### Reproducing the image
|
||||
|
||||
For a simple way to reproduce a Dangerzone container image, you can checkout the
|
||||
commit this image was built from (you can find it from the image tag in its
|
||||
`g<commit>` portion), and run the following command in a Linux environment:
|
||||
|
||||
```
|
||||
./dev_scripts/reproduce-image.py --source <image>
|
||||
```
|
||||
|
||||
This command will download the `diffoci` helper, build a container image from
|
||||
the current Git commit, and ensure that the built image matches the source one,
|
||||
with the exception of image names and file timestamps.
|
||||
|
||||
> [!TIP]
|
||||
> If the source image is not pushed to a registry, and is local instead, you
|
||||
> can prefix it with `docker://` or `podman://` accordingly, so that `diffoci`
|
||||
> can load it from the local Docker / Podman container engine. For example:
|
||||
>
|
||||
> ```
|
||||
> ./dev_scripts/reproduce.py --source podman://dangerzone.rocks/dangerzone:0.8.0-125-g725ce3b
|
||||
> ```
|
|
@ -27,6 +27,29 @@ def str2bool(v):
|
|||
raise argparse.ArgumentTypeError("Boolean value expected.")
|
||||
|
||||
|
||||
def determine_git_tag():
|
||||
# Designate a unique tag for this image, depending on the Git commit it was created
|
||||
# from:
|
||||
# 1. If created from a Git tag (e.g., 0.8.0), the image tag will be `0.8.0`.
|
||||
# 2. If created from a commit, it will be something like `0.8.0-31-g6bdaa7a`.
|
||||
# 3. If the contents of the Git repo are dirty, we will append a unique identifier
|
||||
# for this run, something like `0.8.0-31-g6bdaa7a-fdcb` or `0.8.0-fdcb`.
|
||||
dirty_ident = secrets.token_hex(2)
|
||||
return (
|
||||
subprocess.check_output(
|
||||
[
|
||||
"git",
|
||||
"describe",
|
||||
"--long",
|
||||
"--first-parent",
|
||||
f"--dirty=-{dirty_ident}",
|
||||
],
|
||||
)
|
||||
.decode()
|
||||
.strip()[1:] # remove the "v" prefix of the tag.
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
|
@ -55,6 +78,11 @@ def main():
|
|||
const=True,
|
||||
help="Use the builder's cache to speed up the builds",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--tag",
|
||||
default=None,
|
||||
help="Provide a custom tag for the image (for development only)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
tarball_path = Path("share") / "container.tar.gz"
|
||||
|
@ -62,20 +90,7 @@ def main():
|
|||
|
||||
print(f"Building for architecture '{ARCH}'")
|
||||
|
||||
# Designate a unique tag for this image, depending on the Git commit it was created
|
||||
# from:
|
||||
# 1. If created from a Git tag (e.g., 0.8.0), the image tag will be `0.8.0`.
|
||||
# 2. If created from a commit, it will be something like `0.8.0-31-g6bdaa7a`.
|
||||
# 3. If the contents of the Git repo are dirty, we will append a unique identifier
|
||||
# for this run, something like `0.8.0-31-g6bdaa7a-fdcb` or `0.8.0-fdcb`.
|
||||
dirty_ident = secrets.token_hex(2)
|
||||
tag = (
|
||||
subprocess.check_output(
|
||||
["git", "describe", "--long", "--first-parent", f"--dirty=-{dirty_ident}"],
|
||||
)
|
||||
.decode()
|
||||
.strip()[1:] # remove the "v" prefix of the tag.
|
||||
)
|
||||
tag = args.tag or determine_git_tag()
|
||||
image_name_tagged = IMAGE_NAME + ":" + tag
|
||||
|
||||
print(f"Will tag the container image as '{image_name_tagged}'")
|
||||
|
|
|
@ -216,16 +216,6 @@ convert the documents within a secure sandbox.
|
|||
%prep
|
||||
%autosetup -p1 -n dangerzone-%{version}
|
||||
|
||||
# XXX: Bump the Python requirement in pyproject.toml from <3.13 to <3.14. Fedora
|
||||
# 41 comes with Python 3.13 installed, but our pyproject.toml does not support
|
||||
# it because PySide6 in PyPI works with Python 3.12 or earlier.
|
||||
#
|
||||
# This hack sidesteps this issue, and we haven't noticed any paticular problem
|
||||
# with the package that is built from that.
|
||||
%if 0%{?fedora} == 41
|
||||
sed -i 's/<3.13/<3.14/' pyproject.toml
|
||||
%endif
|
||||
|
||||
# Bypass the version pin for Fedora as the 6.8.1.1 package is causing trouble
|
||||
# A 6.8.1.1 package was only released with a wheel for macOS, but was picked by
|
||||
# Fedora packagers. We cannot use "*" when PyPI is involved as it will fail to download the latest version.
|
||||
|
|
16
poetry.lock
generated
16
poetry.lock
generated
|
@ -874,9 +874,6 @@ optional = false
|
|||
python-versions = "<3.14,>=3.9"
|
||||
files = [
|
||||
{file = "PySide6-6.8.1.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:805728a7ed58352a02689b953ddbe29af1c8944f8c7f2c28312dc0b69f64b85e"},
|
||||
{file = "PySide6-6.8.1.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:70f8c4745d981ebb5bb93d7b825222532d553373b68b9db7a42cfcee25cafc9a"},
|
||||
{file = "PySide6-6.8.1.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:de80ac62087a716b2bada2e3ddd739c5d176dc4be819abef91274d53d75f4e58"},
|
||||
{file = "PySide6-6.8.1.1-cp39-abi3-win_amd64.whl", hash = "sha256:60a2551053fa69845b893fb821507e2cc89d3a8a8b43726d568acd1250ad44fb"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -909,9 +906,6 @@ optional = false
|
|||
python-versions = "<3.14,>=3.9"
|
||||
files = [
|
||||
{file = "PySide6_Addons-6.8.1.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:83d35d7a1a7dbd1a16b4040a26ad4d5cc030a2aed4d439241babee1225d6e58a"},
|
||||
{file = "PySide6_Addons-6.8.1.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5ef45aeadca37d658e44a41e11f2b2e43dfc34c780a6be1cd09d96a7696e6cc6"},
|
||||
{file = "PySide6_Addons-6.8.1.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:e1b4a20b0bcbc2e440faba62e0d164223b8fd6f041d749543bc3812979116c4c"},
|
||||
{file = "PySide6_Addons-6.8.1.1-cp39-abi3-win_amd64.whl", hash = "sha256:d8ae86944ac48cc9891666cf71565acebd403a953d0e050be4d41ac490788d0a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -942,9 +936,6 @@ optional = false
|
|||
python-versions = "<3.14,>=3.9"
|
||||
files = [
|
||||
{file = "PySide6_Essentials-6.8.1.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:25f3fdb281ac3b442f08250e3284d3b1944f7c64c62ed93b57678a62c199cf46"},
|
||||
{file = "PySide6_Essentials-6.8.1.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:62b64842a91114c224c41eeb6a8c8f255ba60268bc5ac19724f944d60e2277c6"},
|
||||
{file = "PySide6_Essentials-6.8.1.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:e0c1cc3cfb2ea5eea70748da7d22032a59ea641e24988f543d5b274c0adab065"},
|
||||
{file = "PySide6_Essentials-6.8.1.1-cp39-abi3-win_amd64.whl", hash = "sha256:085f12e16db31eb0e802b21c64eabf582f54db6c44463a1f5e1814d897b1f2c0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -1187,9 +1178,6 @@ optional = false
|
|||
python-versions = "<3.14,>=3.9"
|
||||
files = [
|
||||
{file = "shiboken6-6.8.1.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:42fbb173a772c4e059dbeafb302e96f6ea8e1c9bacf05fab71ea7eb0d8f97b01"},
|
||||
{file = "shiboken6-6.8.1.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:d672df0f29dc5f44de7205c1acae4d0471ba8371bb1d68fdacbf1686f4d22a96"},
|
||||
{file = "shiboken6-6.8.1.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:ff1b22a66476b042d3dc09870edca353fdac1c1f517a4cdc364b24e296213ecd"},
|
||||
{file = "shiboken6-6.8.1.1-cp39-abi3-win_amd64.whl", hash = "sha256:26f7041c77058a8ecfb9345caa187250b199de79cfb37e33936e5fbd468a7780"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1388,5 +1376,5 @@ type = ["pytest-mypy"]
|
|||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.9,<3.13"
|
||||
content-hash = "9c77a647be7cd12ecb7e893ef3102554eb78faf761e99bafdb1d2424d6123c50"
|
||||
python-versions = ">=3.9,<3.14"
|
||||
content-hash = "c6395d63523761d272dfc5fe6eef7822a081b3b0fb0b739a82efec7de5346d57"
|
||||
|
|
|
@ -13,7 +13,7 @@ include = [
|
|||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9,<3.13"
|
||||
python = ">=3.9,<3.14"
|
||||
click = "*"
|
||||
platformdirs = "*"
|
||||
PySide6 = "^6.7.1"
|
||||
|
|
Loading…
Reference in a new issue