feat(pam tunnel): add --foreground, --background, --run flags and cross-process tunnel registry#1848
Open
msawczynk wants to merge 3 commits intoKeeper-Security:releasefrom
Open
Conversation
Contributor
|
needs to be rebased from release branch |
485ea3e to
8800be8
Compare
…urn, port safety - Add Windows hard-termination warning to --background and --run banners (was only on --foreground) - Use clean_stale=False in --background polling loop to avoid filesystem churn every 0.5s - Guard port comparison in register_tunnel against non-int-coercible values - Document normalize_bind_host limitation (0.0.0.0 vs 127.0.0.1 caught at OS level) - Note thread-safety characteristic of _registry_dir_initialized flag
Contributor
Author
|
@miroberts — Ready for final review. Here's a summary of all changes from the original PR and why. Structural
Behavioral fixes (vs original PR)
Not changed (intentional)
Known limitations (v1, documented)
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add
--foreground/-fg,--pid-file,--run/-R, and--timeoutflags topam tunnel startthat enable tunnels to be used in non-interactive contexts: shell scripts, systemd services, CI/CD pipelines, and headless automation -- without requiring an interactivekeeper shellsession.--foreground: Blocks the Commander process after the tunnel connects, keeping it alive untilSIGTERM/SIGINT/Ctrl+C. Now waits for the WebRTC connection to reach"connected"state before printing the status banner and writing the PID file.--pid-file: Writes the process PID to a file for external signal-based management.--run <COMMAND>: Starts the tunnel, waits for connection, executes the given shell command, stops the tunnel, and exits with the command's exit code. Ideal for single-script workflows.--timeout <SECONDS>: Controls how long to wait for the tunnel to connect (default: 30s). Used with--foreground,--background, and--run.--background/-bg: Launches a separate Commander process with--foreground, polls the file-based tunnel registry for readiness, then returns control to the caller. The tunnel continues running as an independent background process. Works on all platforms.--target-host/--target-portare missing in non-interactive mode, raisesCommandErrorinstead of callinginput()(which hangs in batch/script contexts).<tempdir>/keeper-tunnel-sessions/) enablespam tunnel listto discover tunnels from any Commander session.pam tunnel stop <RECORD_UID>sendsSIGTERMto the owning process. Stale entries (dead PIDs) are auto-cleaned.Motivation
Currently,
pam tunnel startworks only inside an interactivekeeper shellsession because tunnels run as in-process background threads (spawned bystart_rust_tunnel()viakeeper_pam_webrtc_rs). When Commander is invoked in single-command mode (keeper "pam tunnel start <UID>"),PAMTunnelStartCommand.execute()returns after starting the tunnel, Commander exits, and all tunnel threads die with the process.This is a common pain point for users automating infrastructure with Keeper PAM:
pg_dumpthrough a tunnel)Changes
File:
keepercommander/commands/tunnel_and_connections.py(1 file changed)New imports:
signal,subprocess,threading(all stdlib);unregister_tunnel_session,wait_for_tunnel_connection(fromtunnel_helpers)New arguments on
PAMTunnelStartCommand.pam_cmd_parser:--backgroundlogic (subprocess-based, beforestart_rust_tunnel()):keepercommand with--foregroundand all user-supplied flagssubprocess.Popen(start_new_session=True)as an independent process<tempdir>/keeper-tunnel-sessions/) for readiness--runlogic (new branch, checked before--foreground):wait_for_tunnel_connection(result, timeout=connect_timeout)subprocess.run(command, shell=True)close_tube()+unregister_tunnel_session()sys.exit(proc.returncode)KeyboardInterrupt: exits with code 130--foregroundconnection readiness: Before printing the status banner and writing the PID file, callswait_for_tunnel_connection(result, timeout=connect_timeout)to ensure the tunnel is actually usable.Interactive shell detection: When
--foregroundor--backgroundis used insidekeeper shell(interactive mode,batch_mode=False), the blocking logic is skipped and a message informs the user that tunnels already persist in the shell.File-based tunnel registry: New module-level functions (
_register_tunnel,_unregister_tunnel,_list_registered_tunnels,_tunnel_registry_dir,_is_pid_alive) manage JSON metadata files in<tempdir>/keeper-tunnel-sessions/. Each foreground/background/run tunnel registers on connect and unregisters on cleanup.Enhanced
PAMTunnelListCommand: Now reads both the in-processPyTubeRegistryand the file-based registry. Cross-process tunnels appear in the listing with their mode and PID. Stale entries (dead PIDs) are auto-cleaned.Enhanced
PAMTunnelStopCommand: When a tunnel is not found in the in-process registry, falls back to the file registry and sendsSIGTERMto the owning process.--allalso stops cross-process tunnels.Batch mode fix for
--target-host/--target-port: Whenparams.batch_modeisTrueand these values are missing, raisesCommandErrorinstead of callinginput(), which would hang in scripts.No other files are modified. No new external dependencies are introduced. Only Python stdlib modules (
json,signal,subprocess,threading,time) are used.Usage
Testing
--foregroundaccepted -- argument parser recognizes the flag-fgshorthand accepted -- short form worksFalse-- without the flag,foregroundisFalse--pid-fileaccepted -- argument parser recognizes the flag--pid-filedefaults toNone-- without the flag,pid_fileisNone--runaccepted -- argument parser recognizes the flag, stores string value--rundefaults toNone-- without the flag,run_commandisNone--runwith--port----runand--portwork together--timeoutaccepted -- argument parser recognizes the flag, stores int value--timeoutdefaults to 30 -- without the flag,connect_timeoutis30--timeoutcustom value ----timeout 120setsconnect_timeoutto120--backgroundaccepted -- argument parser recognizes the flag-bgshorthand accepted -- short form works--backgrounddefaults toFalse-- without the flag,backgroundisFalse--backgroundwith--pid-file-- both flags work together--host,--port,--no-trickle-ice,--target-host,--target-portall work alongside new flagsunregister_tunnel_session-- importable fromtunnel_and_connections_stop_tunnel_process-- importable fromtunnel_and_connections--foreground,--background,--runall parse individually but runtime rejects combinations--foregroundis set--runexecutes and exits -- tunnel starts, command runs, tunnel stops, exit code propagated<tempdir>/keeper-tunnel-sessions/tunnel listtunnel listshows cross-process tunnels -- tunnels visible from any Commander sessiontunnel stopstops cross-process tunnels -- sends SIGTERM to owning process--backgrounddaemonizes and returns -- tunnel starts, waits, daemonizes, returns prompt--foregroundwaits for connection -- status banner only prints after WebRTC connection is establishedkill -SIGTERMtriggersclose_tube()and process exits 0KeyboardInterrupttriggers the same clean shutdown--foregroundinsidekeeper shellprints info message, does not block--target-host/--target-portin batch mode raisesCommandErrorType=simplesystemd unit starts, runs, and stops cleanlyBackward Compatibility
--foreground,--pid-file,--run,--timeout,--background) are optional and default toFalse/None/None/30/Falseexecute()method follows the exact same code path as before (the replacedpasswas a no-op)--target-host/--target-portonly triggers whenparams.batch_modeisTrueand the resource requires host/port supply -- interactive shell users still get theinput()promptPAMTunnelListCommandandPAMTunnelStopCommandare enhanced to read the file-based registry but continue to work identically for in-process tunnelsPAMTunnelEditCommand,PAMTunnelDiagnoseCommand, or any other commandjson,signal,subprocess,threading,timefrom stdlibPAMTunnelCommandgroup command registration--foreground,--background, and--runare mutually exclusive (enforced at runtime)