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

Use PAM Config for tunneling #1186

Merged
merged 1 commit into from
Feb 12, 2024
Merged
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
88 changes: 74 additions & 14 deletions keepercommander/commands/discoveryrotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1741,14 +1741,18 @@ class PAMTunnelEnableCommand(Command):
pam_cmd_parser.add_argument('uid', type=str, action='store', help='The Record UID of the PAM '
'resource record with network information to use '
'for tunneling')
pam_cmd_parser.add_argument('--configuration', '-c', required=False, dest='config', action='store',
help='The PAM Configuration UID to use for tunneling. '
'Use command `pam config list` to view available PAM Configurations.')

def get_parser(self):
return PAMTunnelEnableCommand.pam_cmd_parser

def execute(self, params, **kwargs):
record_uid = kwargs.get('uid')
config_uid = kwargs.get('config')
if not record_uid:
raise CommandError('tunnel Enable', '"record" argument is required')
raise CommandError('tunnel Enable', '"record UID" argument is required')
dirty = False

record = vault.KeeperRecord.load(params, record_uid)
Expand All @@ -1757,15 +1761,41 @@ def execute(self, params, **kwargs):
print(f"{bcolors.FAIL}Record {record_uid} not found.{bcolors.ENDC}")
return

record_type = record.record_type
if record_type not in "pamMachine pamDatabase pamDirectory".split():
print(f"{bcolors.FAIL}This record's type is not supported for tunnels. "
f"Tunnels are only supported on Pam Machine, Pam Database, and Pam Directory records{bcolors.ENDC}")
return

if config_uid:
configuration = vault.KeeperRecord.load(params, config_uid)
if not isinstance(configuration, vault.TypedRecord):
print(f"{bcolors.FAIL}Configuration {config_uid} not found.{bcolors.ENDC}")
return
if configuration.record_type != 'pamNetworkConfiguration':
print(f"{bcolors.FAIL}The record {config_uid} is not a Pam Configuration.{bcolors.ENDC}")
return

pam_settings = record.get_typed_field('pamSettings')
if not pam_settings:
pam_settings = vault.TypedField.new_field('pamSettings',
{"portForward": {"enabled": True}}, "")
pre_settings = {"portForward": {"enabled": True}}
if config_uid:
pre_settings["configUid"] = config_uid
pam_settings = vault.TypedField.new_field('pamSettings', pre_settings, "")
record.custom.append(pam_settings)
dirty = True
else:
if config_uid:
if pam_settings.value[0].get('configUid') != config_uid:
pam_settings.value[0]['configUid'] = config_uid
dirty = True
if not pam_settings.value[0]['portForward']['enabled']:
pam_settings.value[0]['portForward']['enabled'] = True
dirty = True
if not pam_settings.value[0].get('configUid'):
print(f"{bcolors.WARNING}No PAM Configuration UID found. "
f"This must be set for tunneling to work. You can do this by running 'pam tunnel enable {record_uid} "
f"--config [ConfigUID]' The ConfigUID can be found by running 'pam config list'{bcolors.ENDC}")

client_private_key = record.get_typed_field('trafficEncryptionKey')
if not client_private_key:
Expand All @@ -1791,6 +1821,11 @@ def execute(self, params, **kwargs):
if dirty:
record_management.update_record(params, record)
api.sync_down(params)
if pam_settings.value[0].get('configUid'):
print(f"{bcolors.OKGREEN}Tunneling enabled for {record_uid} using configuration "
f"{pam_settings.value[0].get('configUid')} {bcolors.ENDC}")
else:
print(f"{bcolors.OKGREEN}Tunneling enabled for {record_uid}{bcolors.ENDC}")


class PAMTunnelDisableCommand(Command):
Expand Down Expand Up @@ -1820,12 +1855,11 @@ def execute(self, params, **kwargs):
pam_settings.value[0]['portForward']['enabled'] = False
record_management.update_record(params, record)
api.sync_down(params)
print(f"{bcolors.OKGREEN}Tunneling disabled for {record_uid}{bcolors.ENDC}")


class PAMTunnelStartCommand(Command):
pam_cmd_parser = argparse.ArgumentParser(prog='dr-port-forward-command')
pam_cmd_parser.add_argument('--gateway', '-g', required=True, dest='gateway', action='store',
help='Used to list all tunnels for the given Gateway UID')
pam_cmd_parser.add_argument('--record', '-r', required=True, dest='record_uid', action='store',
help='The Record UID of the PAM resource record with network information to use for '
'tunneling')
Expand Down Expand Up @@ -2031,7 +2065,6 @@ def execute(self, params, **kwargs):
record_uid = kwargs.get('record_uid')
convo_id = GatewayAction.generate_conversation_id()
params.tunnel_threads[convo_id] = {}
gateway_uid = kwargs.get('gateway')
host = kwargs.get('host')
port = kwargs.get('port')
if port is not None and port > 0:
Expand All @@ -2046,12 +2079,6 @@ def execute(self, params, **kwargs):
print(f"{bcolors.FAIL}Could not find open port to use for tunnel{bcolors.ENDC}")
return

gateway_public_key_bytes = retrieve_gateway_public_key(gateway_uid, params, api, utils)

if not gateway_public_key_bytes:
print(f"{bcolors.FAIL}Could not retrieve public key for gateway {gateway_uid}{bcolors.ENDC}")
return

api.sync_down(params)
record = vault.KeeperRecord.load(params, record_uid)
if not isinstance(record, vault.TypedRecord):
Expand All @@ -2064,8 +2091,8 @@ def execute(self, params, **kwargs):
return

try:
dag_info = pam_settings.value[0]
enabled_port_forward = dag_info.get("portForward", {}).get("enabled", False)
pam_info = pam_settings.value[0]
enabled_port_forward = pam_info.get("portForward", {}).get("enabled", False)
if not enabled_port_forward:
print(f"{bcolors.FAIL}PAM Settings not enabled for record {record_uid}.{bcolors.ENDC}")
return
Expand All @@ -2080,6 +2107,39 @@ def execute(self, params, **kwargs):

client_private_key_value = client_private_key.get_default_value(str)

configuration_uid = pam_info.get("configUid", None)
if not configuration_uid:
print(f"{bcolors.FAIL}Configuration UID not found for record {record_uid}.{bcolors.ENDC}")
return
configuration = vault.KeeperRecord.load(params, configuration_uid)
if not isinstance(configuration, vault.TypedRecord):
print(f"{bcolors.FAIL}Configuration {configuration_uid} not found.{bcolors.ENDC}")
return

pam_resources = configuration.get_typed_field('pamResources')
if not pam_resources:
print(f"{bcolors.FAIL}PAM Resources not found for configuration {configuration_uid}.{bcolors.ENDC}")
return
if len(pam_resources.value) == 0:
print(f"{bcolors.FAIL}PAM Resources not found for configuration {configuration_uid}.{bcolors.ENDC}")
return
gateway_uid = ''
try:
gateway_uid = pam_resources.value[0].get("controllerUid", '')
except Exception as e:
print(f"{bcolors.FAIL}Error parsing PAM Resources for configuration {configuration_uid}: {e}{bcolors.ENDC}")
CommandError('Tunnel Start', f"{e}")

if not gateway_uid:
print(f"{bcolors.FAIL}Gateway UID not found for configuration {configuration_uid}.{bcolors.ENDC}")
return

gateway_public_key_bytes = retrieve_gateway_public_key(gateway_uid, params, api, utils)

if not gateway_public_key_bytes:
print(f"{bcolors.FAIL}Could not retrieve public key for gateway {gateway_uid}{bcolors.ENDC}")
return

t = threading.Thread(target=self.pre_connect, args=(params, record_uid, convo_id, gateway_uid, host, port,
gateway_public_key_bytes, client_private_key_value)
)
Expand Down
Loading