Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Large Architecture overhaul. #26

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/ubuntu-packages.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ gnupg
iptables
login
lvm2
python3-virtualenv
python3
python3-dev
systemd
tar
ubuntu-release-upgrader-core
update-notifier-common
virtualenv
wireless-tools
24 changes: 18 additions & 6 deletions penguindome/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
top_dir,
)

gpg_command = partial(main_gpg_command, with_user_id=True,
gpg_user_id = 'penguindome-client'
gpg_command = partial(main_gpg_command, '-u', gpg_user_id,
minimum_version=client_gpg_version)
session = None

Expand Down Expand Up @@ -79,7 +80,7 @@ def server_request(cmd, data=None, data_path=None,
exit_on_connection_error=False, logger=None,
# Clients should never need to use these. They are for
# internal use on the server.
local_port=None, signed=True):
local_port=None, signed=True, useServerKeychain=False):
global session
if session is None:
session = requests.Session()
Expand All @@ -98,10 +99,21 @@ def server_request(cmd, data=None, data_path=None,
data_path = temp_data_file.name
post_data = {'data': data}
if signed:
gpg_command('--armor', '--detach-sign', '-o', signature_file.name,
data_path, log=logger)
signature_file.seek(0)
post_data['signature'] = signature_file.read()
if useServerKeychain:
main_gpg_command(
'-u', 'penguindome-server',
'--armor', '--detach-sign', '-o', signature_file.name,
data_path, log=logger, minimum_version=client_gpg_version
)
signature_file.seek(0)
post_data['signature'] = signature_file.read()
else:
gpg_command(
'--armor', '--detach-sign', '-o', signature_file.name,
data_path, log=logger
)
signature_file.seek(0)
post_data['signature'] = signature_file.read()

kwargs = {
'data': post_data,
Expand Down
50 changes: 33 additions & 17 deletions penguindome/penguindome.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
from itertools import chain
import logbook
import os
import pwd
import sys
import pickle
import re
import socket
Expand Down Expand Up @@ -52,10 +54,6 @@
client_gpg_version = '2.1.11'
server_pipe_log_filter_re = re.compile(
r'POST.*/server_pipe/.*/(?:send|receive).* 200 ')
gpg_user_ids = {
'client': 'penguindome-client',
'server': 'penguindome-server',
}

SelectorVariants = namedtuple(
'SelectorVariants', ['plain_mongo', 'plain_mem', 'enc_mongo', 'enc_mem'])
Expand Down Expand Up @@ -105,14 +103,21 @@ def set_gpg(mode):
mode))

os.environ['GNUPGHOME'] = home
os.chmod(home, 0o0700)
try:
os.chmod(home, 0o0700)
except PermissionError:
print("It looks like you are trying to run the debug server as " +
"your user, please run with `sudo -u " +
pwd.getpwuid(os.stat(top_dir).st_uid).pw_name + "`"
)
sys.exit(1)
# random seed gets corrupted sometimes because we're copying keyring from
# server to client
list(map(os.unlink, glob.glob(os.path.join(home, "random_seed*"))))
gpg_mode = mode


def gpg_command(*cmd, with_user_id=False, with_trustdb=False, quiet=True,
def gpg_command(*cmd, with_trustdb=False, quiet=True,
minimum_version='2.1.15', log=None):
global gpg_exe, gpg_exe

Expand All @@ -124,12 +129,28 @@ def gpg_command(*cmd, with_user_id=False, with_trustdb=False, quiet=True,
('gpg2', '--version'),
stderr=subprocess.STDOUT).decode('utf8')
except Exception:
output = subprocess.check_output(
('gpg', '--version'),
stderr=subprocess.STDOUT).decode('utf8')
gpg_exe = 'gpg'
try:
output = subprocess.check_output(
('gpg', '--version'),
stderr=subprocess.STDOUT).decode('utf8')
except Exception:
try:
# We know we installed gpg during the server install
# so chances are the user running this just doent have
# PATH configured right
output = subprocess.check_output(
('/usr/bin/gpg', '--version'),
stderr=subprocess.STDOUT).decode('utf8')
except Exception:
raise Exception("No gpg application found. " +
"verify gpg or gpg2 is installed!")
else:
gpg_exe = '/usr/bin/gpg'
else:
gpg_exe = "gpg"
else:
gpg_exe = 'gpg2'
gpg_exe = "gpg2"

match = re.search(r'^gpg \(GnuPG\) (\d+(?:\.\d+(?:\.\d+)?)?)', output,
re.MULTILINE)
if not match:
Expand All @@ -145,18 +166,13 @@ def gpg_command(*cmd, with_user_id=False, with_trustdb=False, quiet=True,
else:
trustdb_args = ('--trust-model', 'always')

if with_user_id:
user_id_args = ('-u', gpg_user_ids[gpg_mode])
else:
user_id_args = ()

if quiet:
quiet_args = ('--quiet',)
else:
quiet_args = ()

cmd = tuple(chain((gpg_exe, '--batch', '--yes'), quiet_args, trustdb_args,
user_id_args, cmd))
cmd))
try:
return subprocess.check_output(cmd, stderr=subprocess.STDOUT).\
decode('utf8')
Expand Down
7 changes: 6 additions & 1 deletion penguindome/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
top_dir,
)

gpg_command = partial(main_gpg_command, with_user_id=True)
gpg_user_id = 'penguindome-server'
gpg_command = partial(main_gpg_command, '-u', gpg_user_id)

valid_client_parameters = (
# This is a list of other client which should be treated as the same as the
Expand Down Expand Up @@ -121,6 +122,10 @@ def get_db(force_db=None):
replicaset = get_setting('database:replicaset')
if replicaset:
kwargs['replicaset'] = replicaset
db_ssl_ca = get_setting('database:ssl_ca')
if db_ssl_ca:
kwargs['ssl'] = True
kwargs['ssl_ca_certs'] = db_ssl_ca
connection = MongoClient(host, **kwargs)

newdb = connection[database_name]
Expand Down
31 changes: 25 additions & 6 deletions penguindome/shell/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,10 @@ def close(self):

class PenguinDomeServerPeer(InteractionPeer):
def __init__(self, peer_type, pipe_id=None, local_port=None, logger=None,
client_hostname=None):
client_hostname=None, useServerKeychain=False):
if peer_type not in ('client', 'server'):
raise Exception('Invalid peer type "{}"'.format(peer_type))
self.useServerKeychain = useServerKeychain
self.type = peer_type
self.pipe_id = pipe_id
self.pending_data = b''
Expand All @@ -173,8 +174,16 @@ def __init__(self, peer_type, pipe_id=None, local_port=None, logger=None,
else:
response = self._request('open', data=data)
self.encryptors = {
'send': Encryptor(data['encryption_key'], data['encryption_iv']),
'receive': Encryptor(data['encryption_key'], data['encryption_iv'])
'send':
{
'k': data['encryption_key'],
'i': data['encryption_iv']
},
'receive':
{
'k': data['encryption_key'],
'i': data['encryption_iv']
}
}

def _request(self, request, data=None):
Expand All @@ -187,7 +196,8 @@ def _request(self, request, data=None):
response = server_request(
'/penguindome/v1/server_pipe/{}/{}'.format(self.type, request),
data=data, local_port=self.local_port, logger=self.logger,
signed=request not in ('send', 'receive'))
signed=request not in ('send', 'receive'),
useServerKeychain=self.useServerKeychain)
if response.status_code == 404:
raise FileNotFoundError('Pipe ID {} not found'.format(
self.pipe_id))
Expand All @@ -203,7 +213,11 @@ def receive(self, timeout=None):
def send(self, data):
if self.done:
raise EOFError()
encrypted_data = self.encryptors['send'].encrypt(data)
encrypted_data = Encryptor(
self.encryptors['send']['k'],
self.encryptors['send']['i']
).encrypt(data)

encoded_data = b64encode(encrypted_data).decode('utf8')
data = self._request('send', {'data': encoded_data})
if 'eof' in data and data['eof']:
Expand All @@ -229,7 +243,12 @@ def poll(self, timeout=None):
if 'data' in data and data['data']:
encoded_data = data['data']
encrypted_data = b64decode(encoded_data)
decrypted_data = self.encryptors['receive'].decrypt(encrypted_data)

decrypted_data = Encryptor(
self.encryptors['receive']['k'],
self.encryptors['receive']['i']
).decrypt(encrypted_data)

self.pending_data += decrypted_data
if 'eof' in data and data['eof']:
self.done = True
Expand Down
4 changes: 2 additions & 2 deletions server/client_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ def main():
log.info('Requesting remote shell from {}', args.hostname)
with PenguinDomeServerPeer(
'server', local_port=get_setting('local_port'),
logger=log, client_hostname=args.hostname) as remote, \
TerminalPeer() as terminal:
logger=log, client_hostname=args.hostname,
useServerKeychain=True) as remote, TerminalPeer() as terminal:
host = args.hostname
script = '#!/bin/bash\npython client/endpoints/shell.py {}\n'.format(
remote.pipe_id)
Expand Down
Loading