Make work in macOS using docker instead of podman

This commit is contained in:
Micah Lee 2020-01-09 13:09:17 -08:00
parent 8a84c115a1
commit dd295271f5
No known key found for this signature in database
GPG key ID: 403C2657CD994F73
8 changed files with 275 additions and 66 deletions

52
BUILD.md Normal file
View file

@ -0,0 +1,52 @@
# Development environment
After cloning this git repo, make sure to checkout the git submodules.
```
git submodule init
git submodule update
```
## Debian/Ubuntu
You need [podman](https://podman.io/getting-started/installation) ([these instructions](https://kushaldas.in/posts/podman-on-debian-buster.html) are useful for installing in Debian or Ubuntu).
Install dependencies:
```sh
sudo apt install -y python3 python3-pyqt5 python3-appdirs python3-click python3-xdg
```
Run from source tree:
```sh
./dev_script/dangerzone
```
Create a .deb:
```sh
./install/linux/build_deb.py
```
## macOS
## macOS
Install Xcode from the Mac App Store. Once it's installed, run it for the first time to set it up. Also, run this to make sure command line tools are installed: `xcode-select --install`. And finally, open Xcode, go to Preferences > Locations, and make sure under Command Line Tools you select an installed version from the dropdown. (This is required for installing Qt5.)
Download and install Python 3.7.4 from https://www.python.org/downloads/release/python-374/. I downloaded `python-3.7.4-macosx10.9.pkg`.
Install Qt 5.14.0 for macOS from https://www.qt.io/offline-installers. I downloaded `qt-opensource-mac-x64-5.14.0.dmg`. In the installer, you can skip making an account, and all you need is `Qt` > `Qt 5.14.0` > `macOS`.
If you don't have it already, install pipenv (`pip3 install --user pipenv`). Then install dependencies:
```sh
pipenv install --dev --pre
```
Run from source tree:
```
pipenv run ./dev_scripts/dangerzone
```

View file

@ -7,10 +7,12 @@ name = "pypi"
PyQt5 = "*"
click = "*"
appdirs = "*"
pyxdg = "*"
[dev-packages]
black = "*"
[requires]
python_version = "3.7"
[pipenv]
allow_prereleases = true

167
Pipfile.lock generated Normal file
View file

@ -0,0 +1,167 @@
{
"_meta": {
"hash": {
"sha256": "0141117b8b77eba5269d570a53da1ab9d2029bc11b6a4862111e04b8b72b9ea7"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"appdirs": {
"hashes": [
"sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92",
"sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"
],
"index": "pypi",
"version": "==1.4.3"
},
"click": {
"hashes": [
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
],
"index": "pypi",
"version": "==7.0"
},
"pyqt5": {
"hashes": [
"sha256:2b79209aa6e4688f6ac46e6d2694236dcf91db5f3a87270150d0f82082e3d360",
"sha256:2f230f2dbd767099de7a0cb915abdf0cbc3256a0b5bb910eb09b99117db7a65b",
"sha256:3d6e315e6e2d6489a2e1e0148d00e784e277c6590c189227d6060f15b9be690a",
"sha256:812233bd155735377e2e9c7eea7a28815f357440334db51788d941e2a8b62f64",
"sha256:be10fa95e6bdc9cad616ebf368c51b3f5748138b2b3a600cf7c4f80b78cb9852"
],
"index": "pypi",
"version": "==5.14.1"
},
"pyqt5-sip": {
"hashes": [
"sha256:02d94786bada670ab17a2b62ce95b3cf8e3b40c99d36007593a6334d551840bb",
"sha256:06bc66b50556fb949f14875a4c224423dbf03f972497ccb883fb19b7b7c3b346",
"sha256:091fbbe10a7aebadc0e8897a9449cda08d3c3f663460d812eca3001ca1ed3526",
"sha256:0a067ade558befe4d46335b13d8b602b5044363bfd601419b556d4ec659bca18",
"sha256:1910c1cb5a388d4e59ebb2895d7015f360f3f6eeb1700e7e33e866c53137eb9e",
"sha256:1c7ad791ec86247f35243bbbdd29cd59989afbe0ab678e0a41211f4407f21dd8",
"sha256:3c330ff1f70b3eaa6f63dce9274df996dffea82ad9726aa8e3d6cbe38e986b2f",
"sha256:482a910fa73ee0e36c258d7646ef38f8061774bbc1765a7da68c65056b573341",
"sha256:7695dfafb4f5549ce1290ae643d6508dfc2646a9003c989218be3ce42a1aa422",
"sha256:8274ed50f4ffbe91d0f4cc5454394631edfecd75dc327aa01be8bc5818a57e88",
"sha256:9047d887d97663790d811ac4e0d2e895f1bf2ecac4041691487de40c30239480",
"sha256:9f6ab1417ecfa6c1ce6ce941e0cebc03e3ec9cd9925058043229a5f003ae5e40",
"sha256:b43ba2f18999d41c3df72f590348152e14cd4f6dcea2058c734d688dfb1ec61f",
"sha256:c3ab9ea1bc3f4ce8c57ebc66fb25cd044ef92ed1ca2afa3729854ecc59658905",
"sha256:da69ba17f6ece9a85617743cb19de689f2d63025bf8001e2facee2ec9bcff18f",
"sha256:ef3c7a0bf78674b0dda86ff5809d8495019903a096c128e1f160984b37848f73",
"sha256:fabff832046643cdb93920ddaa8f77344df90768930fbe6bb33d211c4dcd0b5e"
],
"version": "==12.7.0"
}
},
"develop": {
"appdirs": {
"hashes": [
"sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92",
"sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"
],
"index": "pypi",
"version": "==1.4.3"
},
"attrs": {
"hashes": [
"sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
"sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
],
"version": "==19.3.0"
},
"black": {
"hashes": [
"sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b",
"sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"
],
"index": "pypi",
"version": "==19.10b0"
},
"click": {
"hashes": [
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
],
"index": "pypi",
"version": "==7.0"
},
"pathspec": {
"hashes": [
"sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424",
"sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"
],
"version": "==0.7.0"
},
"regex": {
"hashes": [
"sha256:07b39bf943d3d2fe63d46281d8504f8df0ff3fe4c57e13d1656737950e53e525",
"sha256:0932941cdfb3afcbc26cc3bcf7c3f3d73d5a9b9c56955d432dbf8bbc147d4c5b",
"sha256:0e182d2f097ea8549a249040922fa2b92ae28be4be4895933e369a525ba36576",
"sha256:10671601ee06cf4dc1bc0b4805309040bb34c9af423c12c379c83d7895622bb5",
"sha256:23e2c2c0ff50f44877f64780b815b8fd2e003cda9ce817a7fd00dea5600c84a0",
"sha256:26ff99c980f53b3191d8931b199b29d6787c059f2e029b2b0c694343b1708c35",
"sha256:27429b8d74ba683484a06b260b7bb00f312e7c757792628ea251afdbf1434003",
"sha256:3e77409b678b21a056415da3a56abfd7c3ad03da71f3051bbcdb68cf44d3c34d",
"sha256:4e8f02d3d72ca94efc8396f8036c0d3bcc812aefc28ec70f35bb888c74a25161",
"sha256:4eae742636aec40cf7ab98171ab9400393360b97e8f9da67b1867a9ee0889b26",
"sha256:6a6ae17bf8f2d82d1e8858a47757ce389b880083c4ff2498dba17c56e6c103b9",
"sha256:6a6ba91b94427cd49cd27764679024b14a96874e0dc638ae6bdd4b1a3ce97be1",
"sha256:7bcd322935377abcc79bfe5b63c44abd0b29387f267791d566bbb566edfdd146",
"sha256:98b8ed7bb2155e2cbb8b76f627b2fd12cf4b22ab6e14873e8641f266e0fb6d8f",
"sha256:bd25bb7980917e4e70ccccd7e3b5740614f1c408a642c245019cff9d7d1b6149",
"sha256:d0f424328f9822b0323b3b6f2e4b9c90960b24743d220763c7f07071e0778351",
"sha256:d58e4606da2a41659c84baeb3cfa2e4c87a74cec89a1e7c56bee4b956f9d7461",
"sha256:e3cd21cc2840ca67de0bbe4071f79f031c81418deb544ceda93ad75ca1ee9f7b",
"sha256:e6c02171d62ed6972ca8631f6f34fa3281d51db8b326ee397b9c83093a6b7242",
"sha256:e7c7661f7276507bce416eaae22040fd91ca471b5b33c13f8ff21137ed6f248c",
"sha256:ecc6de77df3ef68fee966bb8cb4e067e84d4d1f397d0ef6fce46913663540d77"
],
"version": "==2020.1.8"
},
"toml": {
"hashes": [
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
"sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
],
"version": "==0.10.0"
},
"typed-ast": {
"hashes": [
"sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161",
"sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e",
"sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e",
"sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0",
"sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c",
"sha256:48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47",
"sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631",
"sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4",
"sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34",
"sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b",
"sha256:7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2",
"sha256:838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e",
"sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a",
"sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233",
"sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1",
"sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36",
"sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d",
"sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a",
"sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66",
"sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"
],
"version": "==1.4.0"
}
}
}

View file

@ -15,36 +15,6 @@ Some features:
- Dangerzone compresses the safe PDF to reduce file size
- After converting, dangerzone lets you open the safe PDF in the PDF viewer of your choice, which allows you to open PDFs and office docs in dangerzone by default so you never accidentally open a dangerous document
Dangerzone was inspired by [Qubes trusted PDF](https://blog.invisiblethings.org/2013/02/21/converting-untrusted-pdfs-into-trusted.html), but it works in non-Qubes operating systems and sandboxes the document conversion in [podman](https://podman.io/) containers instead of virtual machines. Podman is like docker but more secure -- it doesn't require a privileged daemon, and containers can be launched without root.
Dangerzone was inspired by [Qubes trusted PDF](https://blog.invisiblethings.org/2013/02/21/converting-untrusted-pdfs-into-trusted.html), but it works in non-Qubes operating systems and sandboxes the document conversion in containers instead of virtual machines (using [podman](https://podman.io/) for Linux, and Docker for macOS, for now). Podman is like docker but more secure -- it doesn't require a privileged daemon, and containers can be launched without root.
Right now, dangerzone only works in Linux, but the goal is to [get it working in macOS](https://github.com/firstlookmedia/dangerzone/issues/1) so it can be more useful to journalists (who tend to <3 using Macs).
## Development environment
After cloning this git repo, make sure to checkout the git submodules.
```
git submodule init
```
### Debian/Ubuntu
You need [podman](https://podman.io/getting-started/installation) ([these instructions](https://kushaldas.in/posts/podman-on-debian-buster.html) are useful for installing in Debian or Ubuntu).
Install dependencies:
```
sudo apt install -y python3 python3-pyqt5 python3-appdirs python3-click python3-xdg
```
Run locally:
```
./dev_script/dangerzone
```
Create a .deb:
```
./install/linux/build_deb.py
```
Set up a development environment by following [these instructions](/BUILD.md).

View file

@ -3,8 +3,11 @@ import os
import inspect
import tempfile
import appdirs
import platform
from PyQt5 import QtGui
from xdg.DesktopEntry import DesktopEntry
if platform.system() == "Linux":
from xdg.DesktopEntry import DesktopEntry
from .settings import Settings
@ -19,8 +22,9 @@ class Common(object):
self.app = app
# Temporary directory to store pixel data
self.pixel_dir = tempfile.TemporaryDirectory()
self.safe_dir = tempfile.TemporaryDirectory()
# Note in macOS, temp dirs must be in /tmp (or a few other paths) for Docker to mount them
self.pixel_dir = tempfile.TemporaryDirectory(prefix="/tmp/dangerzone-pixel-")
self.safe_dir = tempfile.TemporaryDirectory(prefix="/tmp/dangerzone-safe-")
print(
f"Temporary directories created, dangerous={self.pixel_dir.name}, safe={self.safe_dir.name}"
)
@ -37,6 +41,12 @@ class Common(object):
# App data folder
self.appdata_path = appdirs.user_config_dir("dangerzone")
# Container runtime
if platform.system() == "Darwin":
self.container_runtime = "docker"
else:
self.container_runtime = "podman"
# Preload list of PDF viewers on computer
self.pdf_viewers = self._find_pdf_viewers()
@ -231,26 +241,27 @@ class Common(object):
def _find_pdf_viewers(self):
pdf_viewers = {}
for search_path in [
"/usr/share/applications",
"/usr/local/share/applications",
os.path.expanduser("~/.local/share/applications"),
]:
try:
for filename in os.listdir(search_path):
full_filename = os.path.join(search_path, filename)
if os.path.splitext(filename)[1] == ".desktop":
if platform.system == "Linux":
for search_path in [
"/usr/share/applications",
"/usr/local/share/applications",
os.path.expanduser("~/.local/share/applications"),
]:
try:
for filename in os.listdir(search_path):
full_filename = os.path.join(search_path, filename)
if os.path.splitext(filename)[1] == ".desktop":
desktop_entry = DesktopEntry(full_filename)
if (
"application/pdf" in desktop_entry.getMimeTypes()
and desktop_entry.getName() != "dangerzone"
):
pdf_viewers[
desktop_entry.getName()
] = desktop_entry.getExec()
desktop_entry = DesktopEntry(full_filename)
if (
"application/pdf" in desktop_entry.getMimeTypes()
and desktop_entry.getName() != "dangerzone"
):
pdf_viewers[
desktop_entry.getName()
] = desktop_entry.getExec()
except FileNotFoundError:
pass
except FileNotFoundError:
pass
return pdf_viewers

View file

@ -8,12 +8,17 @@ class Settings:
self.common = common
self.settings_filename = os.path.join(self.common.appdata_path, "settings.json")
if len(self.common.pdf_viewers) == 0:
default_pdf_viewer = None
else:
default_pdf_viewer = list(self.common.pdf_viewers)[0]
self.default_settings = {
"save": True,
"ocr": True,
"ocr_language": "English",
"open": True,
"open_app": list(self.common.pdf_viewers)[0],
"open_app": default_pdf_viewer,
"update_container": True,
}

View file

@ -111,7 +111,9 @@ class SettingsWidget(QtWidgets.QWidget):
self.update_checkbox.setCheckState(QtCore.Qt.Unchecked)
# Is update containers required?
output = subprocess.check_output(["podman", "image", "ls", "dangerzone"])
output = subprocess.check_output(
[self.common.container_runtime, "image", "ls", "dangerzone"]
)
if b"localhost/dangerzone" not in output:
self.update_checkbox.setCheckState(QtCore.Qt.Checked)
self.update_checkbox.setEnabled(False)

View file

@ -3,6 +3,7 @@ import time
import tempfile
import os
import pipes
import platform
from PyQt5 import QtCore, QtWidgets, QtGui
@ -15,7 +16,8 @@ class TaskBase(QtCore.QThread):
def __init__(self):
super(TaskBase, self).__init__()
def execute_podman(self, args, watch="stdout"):
def exec_container(self, args, watch="stdout"):
args = [self.common.container_runtime] + args
args_str = " ".join(pipes.quote(s) for s in args)
print(f"Executing: {args_str}")
output = f"Executing: {args_str}\n\n"
@ -55,8 +57,8 @@ class PullImageTask(TaskBase):
def run(self):
self.update_label.emit("Pulling container image")
self.update_details.emit("")
args = ["podman", "pull", "ubuntu:18.04"]
self.execute_podman(args, watch="stderr")
args = ["pull", "ubuntu:18.04"]
self.exec_container(args, watch="stderr")
self.task_finished.emit()
@ -69,8 +71,8 @@ class BuildContainerTask(TaskBase):
container_path = self.common.get_resource_path("container")
self.update_label.emit("Building container")
self.update_details.emit("")
args = ["podman", "build", "-t", "dangerzone", container_path]
self.execute_podman(args)
args = ["build", "-t", "dangerzone", container_path]
self.exec_container(args)
self.task_finished.emit()
@ -86,7 +88,6 @@ class ConvertToPixels(TaskBase):
def run(self):
self.update_label.emit("Converting document to pixels")
args = [
"podman",
"run",
"--network",
"none",
@ -97,7 +98,7 @@ class ConvertToPixels(TaskBase):
"dangerzone",
"document-to-pixels",
]
output = self.execute_podman(args)
output = self.exec_container(args)
# Did we hit an error?
for line in output.split("\n"):
@ -185,7 +186,6 @@ class ConvertToPDF(TaskBase):
args = (
[
"podman",
"run",
"--network",
"none",
@ -197,5 +197,5 @@ class ConvertToPDF(TaskBase):
+ envs
+ ["dangerzone", "pixels-to-pdf",]
)
self.execute_podman(args)
self.exec_container(args)
self.task_finished.emit()