Successfully set up reverse ssh forward system, allowing the host to run commands on the guest over ssh

This commit is contained in:
Micah Lee 2021-07-01 15:32:07 -07:00
parent 1c39895206
commit 29a148d211
No known key found for this signature in database
GPG key ID: 403C2657CD994F73
14 changed files with 306 additions and 189 deletions

View file

@ -10,72 +10,72 @@ RUN apk -U upgrade && \
poppler-utils \
py3-magic \
py3-pillow \
sudo
# tesseract-ocr \
# tesseract-ocr-data-afr \
# tesseract-ocr-data-ara \
# tesseract-ocr-data-aze \
# tesseract-ocr-data-bel \
# tesseract-ocr-data-ben \
# tesseract-ocr-data-bul \
# tesseract-ocr-data-cat \
# tesseract-ocr-data-ces \
# tesseract-ocr-data-chi_sim \
# tesseract-ocr-data-chi_tra \
# tesseract-ocr-data-chr \
# tesseract-ocr-data-dan \
# tesseract-ocr-data-deu \
# tesseract-ocr-data-ell \
# tesseract-ocr-data-enm \
# tesseract-ocr-data-epo \
# tesseract-ocr-data-equ \
# tesseract-ocr-data-est \
# tesseract-ocr-data-eus \
# tesseract-ocr-data-fin \
# tesseract-ocr-data-fra \
# tesseract-ocr-data-frk \
# tesseract-ocr-data-frm \
# tesseract-ocr-data-glg \
# tesseract-ocr-data-grc \
# tesseract-ocr-data-heb \
# tesseract-ocr-data-hin \
# tesseract-ocr-data-hrv \
# tesseract-ocr-data-hun \
# tesseract-ocr-data-ind \
# tesseract-ocr-data-isl \
# tesseract-ocr-data-ita \
# tesseract-ocr-data-ita_old \
# tesseract-ocr-data-jpn \
# tesseract-ocr-data-kan \
# tesseract-ocr-data-kat \
# tesseract-ocr-data-kor \
# tesseract-ocr-data-lav \
# tesseract-ocr-data-lit \
# tesseract-ocr-data-mal \
# tesseract-ocr-data-mkd \
# tesseract-ocr-data-mlt \
# tesseract-ocr-data-msa \
# tesseract-ocr-data-nld \
# tesseract-ocr-data-nor \
# tesseract-ocr-data-pol \
# tesseract-ocr-data-por \
# tesseract-ocr-data-ron \
# tesseract-ocr-data-rus \
# tesseract-ocr-data-slk \
# tesseract-ocr-data-slv \
# tesseract-ocr-data-spa \
# tesseract-ocr-data-spa_old \
# tesseract-ocr-data-sqi \
# tesseract-ocr-data-srp \
# tesseract-ocr-data-swa \
# tesseract-ocr-data-swe \
# tesseract-ocr-data-tam \
# tesseract-ocr-data-tel \
# tesseract-ocr-data-tgl \
# tesseract-ocr-data-tha \
# tesseract-ocr-data-tur \
# tesseract-ocr-data-ukr \
# tesseract-ocr-data-vie
sudo \
tesseract-ocr \
tesseract-ocr-data-afr \
tesseract-ocr-data-ara \
tesseract-ocr-data-aze \
tesseract-ocr-data-bel \
tesseract-ocr-data-ben \
tesseract-ocr-data-bul \
tesseract-ocr-data-cat \
tesseract-ocr-data-ces \
tesseract-ocr-data-chi_sim \
tesseract-ocr-data-chi_tra \
tesseract-ocr-data-chr \
tesseract-ocr-data-dan \
tesseract-ocr-data-deu \
tesseract-ocr-data-ell \
tesseract-ocr-data-enm \
tesseract-ocr-data-epo \
tesseract-ocr-data-equ \
tesseract-ocr-data-est \
tesseract-ocr-data-eus \
tesseract-ocr-data-fin \
tesseract-ocr-data-fra \
tesseract-ocr-data-frk \
tesseract-ocr-data-frm \
tesseract-ocr-data-glg \
tesseract-ocr-data-grc \
tesseract-ocr-data-heb \
tesseract-ocr-data-hin \
tesseract-ocr-data-hrv \
tesseract-ocr-data-hun \
tesseract-ocr-data-ind \
tesseract-ocr-data-isl \
tesseract-ocr-data-ita \
tesseract-ocr-data-ita_old \
tesseract-ocr-data-jpn \
tesseract-ocr-data-kan \
tesseract-ocr-data-kat \
tesseract-ocr-data-kor \
tesseract-ocr-data-lav \
tesseract-ocr-data-lit \
tesseract-ocr-data-mal \
tesseract-ocr-data-mkd \
tesseract-ocr-data-mlt \
tesseract-ocr-data-msa \
tesseract-ocr-data-nld \
tesseract-ocr-data-nor \
tesseract-ocr-data-pol \
tesseract-ocr-data-por \
tesseract-ocr-data-ron \
tesseract-ocr-data-rus \
tesseract-ocr-data-slk \
tesseract-ocr-data-slv \
tesseract-ocr-data-spa \
tesseract-ocr-data-spa_old \
tesseract-ocr-data-sqi \
tesseract-ocr-data-srp \
tesseract-ocr-data-swa \
tesseract-ocr-data-swe \
tesseract-ocr-data-tam \
tesseract-ocr-data-tel \
tesseract-ocr-data-tgl \
tesseract-ocr-data-tha \
tesseract-ocr-data-tur \
tesseract-ocr-data-ukr \
tesseract-ocr-data-vie
# Install pdftk
RUN \

View file

@ -170,4 +170,10 @@ def gui_main(custom_container, filename):
# If the application is activated and all windows are closed, open a new one
app_wrapper.application_activated.connect(application_activated)
sys.exit(app.exec_())
# Launch the GUI
ret = app.exec_()
if vm:
vm.stop()
sys.exit(ret)

View file

@ -8,6 +8,8 @@ import socket
import random
import getpass
import json
import psutil
import time
from PySide2 import QtCore
@ -15,6 +17,7 @@ class Vm(QtCore.QObject):
STATE_OFF = 0
STATE_STARTING = 1
STATE_ON = 2
STATE_FAIL = 3
vm_state_change = QtCore.Signal(int)
@ -25,9 +28,14 @@ class Vm(QtCore.QObject):
# VM starts off
self.state = self.STATE_OFF
# Ports for ssh services
self.sshd_port = None
self.sshd_tunnel_port = None
# Processes
self.vpnkit_p = None
self.hyperkit_p = None
self.sshd_pid = None
# Relevant paths
self.vpnkit_path = self.global_common.get_resource_path("bin/vpnkit")
@ -50,6 +58,8 @@ class Vm(QtCore.QObject):
self.ssh_client_pubkey_path = os.path.join(
self.state_dir.name, "client_ed25519.pub"
)
self.sshd_pid_path = os.path.join(self.state_dir.name, "sshd.pid")
self.sshd_log_path = os.path.join(self.state_dir.name, "sshd.log")
self.vm_disk_img_path = os.path.join(self.state_dir.name, "disk.img")
# UDID for VM
@ -58,6 +68,12 @@ class Vm(QtCore.QObject):
"earlyprintk=serial console=ttyS0 modules=loop,squashfs,sd-mod"
)
# Threads
self.wait_t = None
def __del__(self):
self.stop()
def start(self):
self.state = self.STATE_STARTING
self.vm_state_change.emit(self.state)
@ -105,18 +121,21 @@ class Vm(QtCore.QObject):
ssh_client_pubkey = f.read()
# Find an open port
sshd_port = self.find_open_port()
sshd_tunnel_port = self.find_open_port()
self.sshd_port = self.find_open_port()
self.sshd_tunnel_port = self.find_open_port()
# Start an sshd service on this port
subprocess.run(
[
args = [
"/usr/sbin/sshd",
"-4",
"-E",
self.sshd_log_path,
"-o",
f"PidFile={self.sshd_pid_path}",
"-o",
f"HostKey={self.ssh_host_key_path}",
"-o",
f"ListenAddress=127.0.0.1:{sshd_port}",
f"ListenAddress=127.0.0.1:{self.sshd_port}",
"-o",
f"AllowUsers={getpass.getuser()}",
"-o",
@ -132,8 +151,9 @@ class Vm(QtCore.QObject):
"-o",
f"AuthorizedKeysFile={self.ssh_client_pubkey_path}",
]
)
# TODO: keep track of the sshd process so we can kill it on close
args_str = " ".join(pipes.quote(s) for s in args)
print("> " + args_str)
subprocess.run(args)
# Create a JSON object to pass into the VM
# This is a 512kb file that starts with a JSON object, followed by null bytes
@ -142,8 +162,8 @@ class Vm(QtCore.QObject):
"id_ed25519.pub": ssh_client_pubkey,
"user": getpass.getuser(),
"ip": "192.168.65.2",
"port": sshd_port,
"tunnel_port": sshd_tunnel_port,
"port": self.sshd_port,
"tunnel_port": self.sshd_tunnel_port,
}
with open(self.vm_disk_img_path, "wb") as f:
vm_info_bytes = json.dumps(vm_info).encode()
@ -200,16 +220,39 @@ class Vm(QtCore.QObject):
print("> " + args_str)
self.hyperkit_p = subprocess.Popen(
args,
stdout=sys.stdout,
stderr=subprocess.STDOUT,
# stdout=sys.stdout,
# stderr=subprocess.STDOUT,
)
# Get the sshd PID
with open(self.sshd_pid_path) as f:
self.sshd_pid = int(f.read())
print(f"sshd PID: {self.sshd_pid}")
print(f"vpnkit PID: {self.vpnkit_p.pid}")
print(f"hyperkit PID: {self.hyperkit_p.pid}")
# Wait for SSH thread
self.wait_t = WaitForSsh(self.sshd_tunnel_port)
self.wait_t.connected.connect(self.vm_connected)
self.wait_t.timeout.connect(self.vm_timeout)
self.wait_t.start()
def vm_connected(self):
self.state = self.STATE_ON
self.vm_state_change.emit()
def vm_timeout(self):
self.state = self.STATE_FAIL
self.vm_state_change.emit()
def restart(self):
self.stop()
self.start()
def stop(self):
# Kill existing processes
self.kill_sshd()
if self.vpnkit_p is not None:
self.vpnkit_p.terminate()
self.vpnkit_p = None
@ -228,3 +271,34 @@ class Vm(QtCore.QObject):
_, port = tmpsock.getsockname()
return port
def kill_sshd(self):
if self.sshd_pid and psutil.pid_exists(self.sshd_pid):
try:
proc = psutil.Process(self.sshd_pid)
proc.kill()
except Exception:
pass
self.sshd_pid = None
class WaitForSsh(QtCore.QThread):
connected = QtCore.Signal()
timeout = QtCore.Signal()
def __init__(self, ssh_port):
super(WaitForSsh, self).__init__()
self.ssh_port = ssh_port
def run(self):
# Wait for the SSH port to be open
timeout_seconds = 45
sock = socket.socket()
sock.settimeout(timeout_seconds)
try:
sock.connect(("127.0.0.1", int(self.ssh_port)))
self.connected.emit()
sock.close()
except socket.timeout:
self.timeout.emit()

View file

@ -17,32 +17,15 @@ class WaitingWidget(QtWidgets.QWidget):
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setStyleSheet("QLabel { font-size: 20px; }")
self.details = QtWidgets.QLabel()
self.details.setStyleSheet(
"QLabel { background-color: #ffffff; font-size: 12px; padding: 10px; }"
)
self.details.setFont(self.gui_common.fixed_font)
self.details.setAlignment(QtCore.Qt.AlignTop)
self.details_scrollarea = QtWidgets.QScrollArea()
self.details_scrollarea.setMinimumHeight(200)
self.details_scrollarea.setWidgetResizable(True)
self.details_scrollarea.setWidget(self.details)
self.details_scrollarea.verticalScrollBar().rangeChanged.connect(
self.scroll_to_bottom
)
# Layout
layout = QtWidgets.QVBoxLayout()
layout.addStretch()
layout.addWidget(self.label)
layout.addWidget(self.details_scrollarea)
layout.addStretch()
self.setLayout(layout)
def vm_state_change(self, state):
if state == self.vm.STATE_ON:
self.vm_started.emit()
def scroll_to_bottom(self, minimum, maximum):
self.scrollarea.verticalScrollBar().setValue(maximum)
elif state == self.vm.STATE_FAIL:
self.label.setText("Dangerzone virtual machine failed to start :(")

View file

@ -14,54 +14,10 @@ This takes awhile to run. It:
- Builds a new `dangerzone-converter` docker image
- Builds an ISO, which includes a copy of this image
- Outputs the ISO, as well as vmlinuz and initramfs files, in the `vm` folder
- Outputs files in the `vm` folder
## Run the VM
```sh
./run-vm.sh
```
You can ssh in as the unprivileged user like this (you need to `brew install socat`):
```sh
ssh -i ./ssh-key/id_ed25519 \
-o LogLevel=FATAL \
-o Compression=yes \
-o IdentitiesOnly=yes \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o "ProxyCommand nc -U /Users/user/code/dangerzone/rip_docker/vm/connect" \
-v \
user@localhost
```
(doesn't work yet)
Notes on sshd:
```
# Generate keys
rm -r state
mkdir state
ssh-keygen -t ed25519 -C dangerzone-vm-key -N "" -f state/host_ed25519
ssh-keygen -t ed25519 -C dangerzone-vm-key -N "" -f state/client_ed25519
# start sshd service
/usr/sbin/sshd -4 \
-o HostKey=$(pwd)/state/host_ed25519 \
-o ListenAddress=127.0.0.1:4444 \
-o AllowUsers=$(whoami) \
-o PasswordAuthentication=no \
-o PubkeyAuthentication=yes \
-o Compression=yes \
-o ForceCommand=/usr/bin/whoami \
-o UseDNS=no \
-o AuthorizedKeysFile=$(pwd)/state/client_ed25519.pub
# in the vm, making an ssh tunnel
ssh -o StrictHostKeyChecking=no -N -R 52038:127.0.0.1:22 -p 52039 user@192.168.65.2
```

View file

@ -1,3 +1,6 @@
alpine-base
openssh
podman
dropbear
sshfs
python3
sudo

View file

@ -1,6 +1,6 @@
#!/sbin/openrc-run
name="Dangerzone init script"
start_pre() {
start() {
# Setup Alpine
/sbin/setup-alpine -f /etc/answers.txt -e -q
rm /etc/answers.txt

View file

@ -1,8 +1,8 @@
#!/usr/bin/env python3
import os
import json
import stat
import subprocess
import shutil
def main():
@ -17,25 +17,36 @@ def main():
info = json.loads(s[0 : s.find(b"\0")])
# Create SSH files
os.makedirs("/home/user/.ssh", mode=0o700, exist_ok=True)
perms = stat.S_IRUSR | stat.S_IWUSR
os.makedirs("/home/user/.ssh", exist_ok=True)
with open("/home/user/.ssh/id_ed25519", "w") as f:
f.write(info["id_ed25519"])
f.write("\n")
with open("/home/user/.ssh/id_ed25519.pub", "w") as f:
f.write(info["id_ed25519.pub"])
f.write("\n")
with open("/home/user/.ssh/authorized_keys", "w") as f:
f.write(info["id_ed25519.pub"])
f.write("\n")
os.chmod("/home/user/.ssh/id_ed25519", perms)
os.chmod("/home/user/.ssh/id_ed25519.pub", perms)
os.chmod("/home/user/.ssh/authorized_keys", perms)
os.chmod("/home/user/.ssh", 0o700)
os.chmod("/home/user/.ssh/id_ed25519", 0o600)
os.chmod("/home/user/.ssh/id_ed25519.pub", 0o644)
os.chmod("/home/user/.ssh/authorized_keys", 0o600)
shutil.chown("/home/user/.ssh", "user", "user")
shutil.chown("/home/user/.ssh/id_ed25519", "user", "user")
shutil.chown("/home/user/.ssh/id_ed25519.pub", "user", "user")
shutil.chown("/home/user/.ssh/authorized_keys", "user", "user")
# Start SSH reverse port forward
subprocess.run(
[
"/usr/bin/sudo",
"-u",
"user",
"/usr/bin/ssh",
"-o",
"StrictHostKeyChecking=no",
@ -45,7 +56,7 @@ def main():
"-R",
f"{info['tunnel_port']}:127.0.0.1:22",
"-p",
info["port"],
str(info["port"]),
f"{info['user']}@{info['ip']}",
]
)

View file

@ -1,7 +0,0 @@
AuthorizedKeysFile .ssh/authorized_keys
AllowTcpForwarding no
GatewayPorts no
X11Forwarding no
Subsystem sftp /usr/lib/ssh/sftp-server
UseDNS no
PasswordAuthentication no

View file

@ -31,8 +31,8 @@ cp -r /home/user/.local/share/containers "$tmp"/etc/container-data
# Start cgroups, required by podman
rc_add cgroups boot
# Start sshd
rc_add sshd boot
# Start dropbear (ssh server)
rc_add dropbear boot
# Run setup-alpine
rc_add dangerzone boot

View file

@ -9,5 +9,5 @@ profile_dangerzone() {
kernel_cmdline="console=tty0 console=ttyS0,115200"
syslinux_serial="0 115200"
apkovl="genapkovl-dangerzone.sh"
apks="$apks podman openssh sshfs python3"
apks="$apks podman dropbear sshfs python3 sudo"
}

View file

@ -4,20 +4,69 @@ ROOT=$(pwd)/vm
HYPERKIT=/Applications/Docker.app/Contents/Resources/bin/com.docker.hyperkit
VPNKIT=/Applications/Docker.app/Contents/Resources/bin/com.docker.vpnkit
SSHD_PORT=4445
SSHD_TUNNEL_PORT=4446
tmp="$(mktemp -d)"
trap rm -rf "$tmp" EXIT
# make ssh keys
/usr/bin/ssh-keygen \
-t ed25519 \
-C dangerzone-host \
-N "" \
-f "$tmp/host_ed25519"
/usr/bin/ssh-keygen \
-t ed25519 \
-C dangerzone-client \
-N "" \
-f "$tmp/client_ed25519"
# run sshd
SSHD_PIDFILE=$ROOT/sshd.pid
/usr/sbin/sshd \
-4 \
-E $ROOT/sshd.log \
-o PidFile=$ROOT/sshd.pid \
-o HostKey=$tmp/host_ed25519 \
-o ListenAddress=127.0.0.1:$SSHD_PORT \
-o AllowUsers=$(whoami) \
-o PasswordAuthentication=no \
-o PubkeyAuthentication=yes \
-o Compression=yes \
-o ForceCommand=/usr/bin/whoami \
-o UseDNS=no \
-o AuthorizedKeysFile=$tmp/client_ed25519.pub &
echo $! > $SSHD_PIDFILE
trap 'test -f $SSHD_PIDFILE && kill `cat $SSHD_PIDFILE` && rm $SSHD_PIDFILE' EXIT
# create disk image
cd $ROOT
cat > info.json << EOF
{
"id_ed25519": "$(cat $tmp/client_ed25519 | awk '{printf "%s\\n", $0}')",
"id_ed25519.pub": "$(cat $tmp/client_ed25519.pub)",
"user": "$(whoami)",
"ip": "192.168.65.2",
"port": $SSHD_PORT,
"tunnel_port": $SSHD_TUNNEL_PORT
}
EOF
python3 -c 's=open("info.json").read(); open("disk.img", "wb").write(s.encode()+b"\x00"*(512*1024-len(s)))'
# run vpnkit
VPNKIT_SOCK=$ROOT/vpnkit.eth.sock
PIDFILE=$ROOT/vpnkit.pid
VPNKIT_PIDFILE=$ROOT/vpnkit.pid
$VPNKIT \
--ethernet=$VPNKIT_SOCK \
--gateway-ip 192.168.65.1 \
--host-ip 192.168.65.2 \
--lowest-ip 192.168.65.3 \
--highest-ip 192.168.65.254 &
echo $! > $PIDFILE
trap 'test -f $PIDFILE && kill `cat $PIDFILE` && rm $PIDFILE' EXIT
cd $ROOT
python3 -c 's="this is a test"; open("disk.img", "wb").write(s.encode()+b"\x00"*(512*1024-len(s)))'
echo $! > $VPNKIT_PIDFILE
trap 'test -f $VPNKIT_PIDFILE && kill `cat $VPNKIT_PIDFILE` && rm $VPNKIT_PIDFILE' EXIT
# run hyperkit
$HYPERKIT \
-F $ROOT/hyperkit.pid \
-A -u \

43
poetry.lock generated
View file

@ -144,6 +144,17 @@ python-versions = ">=3.6.0"
[package.dependencies]
future = "*"
[[package]]
name = "psutil"
version = "5.8.0"
description = "Cross-platform lib for process and system monitoring in Python."
category = "main"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.extras]
test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"]
[[package]]
name = "pyinstaller"
version = "4.3"
@ -382,7 +393,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt
[metadata]
lock-version = "1.1"
python-versions = ">=3.7,<3.10"
content-hash = "6db74c36125b2d3ad4d04c864c24ecbcf376d5ac75ee7402d950010cade5de96"
content-hash = "234d09a8607647ed536d6737a98bdacc9d6296d6f71e1382b9d97342de1911e5"
[metadata.files]
altgraph = [
@ -439,6 +450,36 @@ pathspec = [
pefile = [
{file = "pefile-2021.5.24.tar.gz", hash = "sha256:ed79b2353daa58421459abf4d685953bde0adf9f6e188944f97ba9795f100246"},
]
psutil = [
{file = "psutil-5.8.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64"},
{file = "psutil-5.8.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c"},
{file = "psutil-5.8.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df"},
{file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131"},
{file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60"},
{file = "psutil-5.8.0-cp27-none-win32.whl", hash = "sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876"},
{file = "psutil-5.8.0-cp27-none-win_amd64.whl", hash = "sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65"},
{file = "psutil-5.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8"},
{file = "psutil-5.8.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6"},
{file = "psutil-5.8.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac"},
{file = "psutil-5.8.0-cp36-cp36m-win32.whl", hash = "sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2"},
{file = "psutil-5.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d"},
{file = "psutil-5.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935"},
{file = "psutil-5.8.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d"},
{file = "psutil-5.8.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023"},
{file = "psutil-5.8.0-cp37-cp37m-win32.whl", hash = "sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394"},
{file = "psutil-5.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563"},
{file = "psutil-5.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef"},
{file = "psutil-5.8.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28"},
{file = "psutil-5.8.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b"},
{file = "psutil-5.8.0-cp38-cp38-win32.whl", hash = "sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d"},
{file = "psutil-5.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d"},
{file = "psutil-5.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7"},
{file = "psutil-5.8.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4"},
{file = "psutil-5.8.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b"},
{file = "psutil-5.8.0-cp39-cp39-win32.whl", hash = "sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0"},
{file = "psutil-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3"},
{file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"},
]
pyinstaller = [
{file = "pyinstaller-4.3.tar.gz", hash = "sha256:5ecf8bbc230d7298a796e52bb745b95eee12878d141f1645612c99246ecd23f2"},
]

View file

@ -20,6 +20,7 @@ pyobjc-core = {version = "*", platform = "darwin"}
pyobjc-framework-launchservices = {version = "*", platform = "darwin"}
strip-ansi = {version = "*", platform = "darwin"}
colorama = "^0.4.4"
psutil = "^5.8.0"
[tool.poetry.dev-dependencies]
pyinstaller = {version = "*", platform = "darwin"}