Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6f13666
Return separate name and ID fields for enterprise-info --users -v (#2…
sshrushanth-ks Aug 4, 2026
19dfc11
rsync SFTP path traversal — arbitrary file write via absolute remote …
sk-keeper Aug 4, 2026
ffb7cc2
connect command: SSH proxy command injection
sk-keeper Aug 4, 2026
b35fc6d
AD password rotation plugin: Do not ignore TLS certificate errors by …
sk-keeper Aug 4, 2026
65b4cd4
Password rotation plugins: SQL query injection vulnerability
sk-keeper Aug 4, 2026
a9c15a8
KeePass import: stop using template file
sk-keeper Aug 4, 2026
866e833
First cut at support for new PAM USS config entries for GitHub.
mfordkeeper Aug 4, 2026
fb2856c
Fix audit report compliance detail fetch
aaunario-keeper Aug 5, 2026
878c439
KC-1382,1383,1384 : Improve SailPoint Service Mode offboard and share…
amangalampalli-ks Aug 5, 2026
c835b42
KC-1368: Add gchat-app-setup for Google Chat integration (#2252)
sshrushanth-ks Aug 6, 2026
906dc60
switch-to-mc inherits MSP forbid_rsa policy instead of MC's own. KC-1207
sk-keeper Aug 6, 2026
fd0e16c
Implement Ownership check for "Commander Service Mode Config" record …
amangalampalli-ks Aug 7, 2026
790d459
added 429 for throttling and retry support (#2272)
pvagare-ks Aug 7, 2026
c16b25a
Release 18.1.0
sk-keeper Aug 7, 2026
974b262
KC-1357 : Add Nested Share Folder (NSF) support for import (#2260) (#…
amangalampalli-ks Aug 10, 2026
21b27b4
KC-1377: Add --format json support for various Commands (#2265) (#2278)
sshrushanth-ks Aug 10, 2026
db63dd3
KC-1392: Block PAM workflow admin commands after workflow policy is r…
amangalampalli-ks Aug 10, 2026
a159b2a
Delete unused files
sk-keeper Aug 10, 2026
9324cfd
Fix Docker container hanging after one-shot commands
maksimu Aug 11, 2026
f0d1929
Change service command to accept service names. (PG-367)
jwalstra-keeper Jul 29, 2026
7795dbc
Require ownership before adopting service Docker setup folders and ap…
amangalampalli-ks Aug 12, 2026
2272285
epm scim: record user/group Id
sk-keeper Aug 13, 2026
21fedb8
Output full record contents for record-history -a view --format=json
craiglurey Aug 13, 2026
9f25159
KC-1393: Fix pam rbi edit so NSF RBI settings persist via update_pam_…
sshrushanth-ks Aug 13, 2026
11bd160
Add team name validation for key fetching (#2282)
adeshmukh-ks Aug 13, 2026
6faafd5
Increase timeout to be in sync with backend timeouts (#2288)
adeshmukh-ks Aug 13, 2026
86196d0
Updated with krouter endpoint changes for GitHub. Regenerated from p…
mfordkeeper Aug 13, 2026
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
257 changes: 185 additions & 72 deletions docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,39 @@ log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}

# Function to run keeper service with intelligent lifecycle management
# Function to run keeper service with intelligent lifecycle management.
# Usage: run_keeper_service <config_file_or_empty> <command> [args...]
# Arguments are passed through as an array so values containing spaces survive.
run_keeper_service() {
local config_arg="$1"
local command_args="$2"

local config_file="$1"
shift

local config_args=()
if [[ -n "${config_file}" ]]; then
config_args=("--config" "${config_file}")
fi

# Check if command contains service-create
if [[ "$command_args" =~ service-create ]]; then
if [[ "$*" =~ service-create ]]; then
log "Service command detected, checking service status..."

# Get service status
local service_status
service_status=$(python3 keeper.py ${config_arg} service-status 2>/dev/null || true)
service_status=$(python3 keeper.py "${config_args[@]}" service-status 2>/dev/null || true)

if echo "${service_status}" | grep -q "Stopped"; then
log "Service exists but is stopped, starting it..."
python3 keeper.py ${config_arg} service-start
python3 keeper.py "${config_args[@]}" service-start
elif echo "${service_status}" | grep -q "Running"; then
log "Service is already running, no action needed."
else
log "Service not found, creating new service..."
log "Running: python3 keeper.py ${config_arg} ${command_args}"
python3 keeper.py ${config_arg} ${command_args}
log "Running: python3 keeper.py ${config_args[*]} $*"
python3 keeper.py "${config_args[@]}" "$@"
fi
else
# Not a service command, run as normal
python3 keeper.py ${config_arg} ${command_args}
python3 keeper.py "${config_args[@]}" "$@"
fi
}

Expand Down Expand Up @@ -103,23 +110,28 @@ parse_credentials() {
done
}

# Filter out authentication arguments and return remaining command arguments
# Filter out authentication arguments, leaving the actual Commander command in
# the global COMMAND_ARGS array. An array rather than a string so that arguments
# containing spaces -- record titles, notes, search queries -- reach Commander
# as single arguments instead of being re-split by word splitting.
filter_args() {
local filtered_args=()
COMMAND_ARGS=()

while [[ $# -gt 0 ]]; do
case $1 in
--user|--password|--server|--ksm-config|--ksm-token|--record)
shift 2 # Skip argument and its value
# Skip the flag and its value, tolerating a missing value.
shift
if [[ $# -gt 0 ]]; then
shift
fi
;;
*)
filtered_args+=("$1")
COMMAND_ARGS+=("$1")
shift
;;
esac
done

echo "${filtered_args[@]}"
}


Expand Down Expand Up @@ -221,6 +233,36 @@ download_config_from_ksm() {
log "Config.json downloaded successfully from KSM record"
}

# Upload config.json back to the KSM record once, without starting a monitor.
# Used after a one-shot command so refreshed device/session state is persisted
# before the container exits. Best effort: a failed upload must not mask the
# status of the command the user actually asked for.
upload_config_to_ksm() {
local ksm_config_path="$1"
local ksm_token="$2"
local record_uid="$3"

if [[ ! -f "${CONFIG_FILE}" ]]; then
return 0
fi

local helper_args=("upload" "--record-uid" "${record_uid}" \
"--config-file" "${CONFIG_FILE}")

if [[ -n "${ksm_config_path}" ]]; then
helper_args+=("--ksm-config" "${ksm_config_path}")
elif [[ -n "${ksm_token}" ]]; then
helper_args+=("--ksm-token" "${ksm_token}")
else
return 0
fi

log "Syncing config.json back to KSM record: ${record_uid}"
if ! python3 docker_ksm_utility.py "${helper_args[@]}"; then
log "WARNING: Failed to sync config back to KSM record"
fi
}

# Start config.json monitoring and upload changes
start_config_monitor() {
local ksm_config_path="$1"
Expand Down Expand Up @@ -275,15 +317,103 @@ stop_config_monitor() {
# CLEANUP AND SIGNAL HANDLING
# =============================================================================

# PID of the idle process used by keep-alive mode, so signal handling can end it.
KEEP_ALIVE_PID=""

# Handle cleanup on exit
cleanup_on_exit() {
log "Performing cleanup on exit..."
stop_config_monitor
log "Cleanup completed"
}

# Set up exit trap for cleanup
trap cleanup_on_exit EXIT INT TERM
# Handle SIGTERM/SIGINT (e.g. `docker stop`) by ending the keep-alive wait and
# exiting, which runs the EXIT trap and therefore the cleanup above.
on_terminate() {
local signum="$1"

log "Received signal, shutting down..."
if [[ -n "${KEEP_ALIVE_PID}" ]]; then
kill "${KEEP_ALIVE_PID}" 2>/dev/null || true
KEEP_ALIVE_PID=""
fi

# Conventional shell status for death by signal.
exit $((128 + signum))
}

# Set up traps: cleanup always runs on exit, signals shut down deliberately.
trap cleanup_on_exit EXIT
trap 'on_terminate 15' TERM
trap 'on_terminate 2' INT


# =============================================================================
# CONTAINER LIFECYCLE
# =============================================================================

# Decide whether the container should stay resident once setup is done.
#
# Persistent modes have to stay up: service mode leaves Commander running as a
# background daemon, and a bare `docker run` with no command is a request for a
# live container to exec into. A one-shot command is the opposite -- it must
# exit with its own status so the container behaves like the CLI it wraps and
# can be used in scripts and pipelines. Set KEEPER_KEEP_ALIVE=true to force the
# container to stay up regardless.
should_keep_alive() {
local flag
flag=$(printf '%s' "${KEEPER_KEEP_ALIVE:-}" | tr '[:upper:]' '[:lower:]')
case "${flag}" in
1|true|yes)
return 0
;;
esac

# No command given -- nothing to finish, so keep the container available.
if [[ ${#COMMAND_ARGS[@]} -eq 0 ]]; then
return 0
fi

# Service mode runs Commander as a daemon inside the container.
if [[ "${COMMAND_ARGS[*]}" =~ (service-create|service-start) ]]; then
return 0
fi

return 1
}

# Run the requested command (if any) and return its exit status. Uses
# `|| status=$?` so that `set -e` does not abort before we can report it.
run_command_args() {
local config_file="$1"
local status=0

if [[ ${#COMMAND_ARGS[@]} -gt 0 ]]; then
run_keeper_service "${config_file}" "${COMMAND_ARGS[@]}" || status=$?
fi

return "${status}"
}

# Final disposition: hold the container open for persistent modes, otherwise
# exit with the wrapped command's status.
finish() {
local status="${1:-0}"

if should_keep_alive; then
log "Keeping container alive..."
# Idle in the background and `wait` rather than running `sleep infinity`
# in the foreground: bash defers trap handlers until a foreground child
# finishes, so a foreground sleep would make the container ignore
# SIGTERM entirely and force `docker stop` to fall back to SIGKILL
# without ever running cleanup.
sleep infinity &
KEEP_ALIVE_PID=$!
wait "${KEEP_ALIVE_PID}" || true
fi

exit "${status}"
}


# =============================================================================
Expand Down Expand Up @@ -331,40 +461,35 @@ if [[ (-n "${KSM_CONFIG}" || -n "${KSM_TOKEN}") && -n "${RECORD}" ]]; then

# Download config.json from KSM record
download_config_from_ksm "${KSM_CONFIG}" "${KSM_TOKEN}" "${RECORD}"

# Start monitoring for config.json changes to upload back to KSM
start_config_monitor "${KSM_CONFIG}" "${KSM_TOKEN}" "${RECORD}"


# Filter out KSM arguments from command args
COMMAND_ARGS=$(filter_args "$@")

# Execute commands or keep container alive
if [[ -z "${COMMAND_ARGS}" ]]; then
log "No command arguments provided, keeping container alive..."
sleep infinity
else
# Run the service command with downloaded config file
run_keeper_service "--config ${CONFIG_FILE}" "${COMMAND_ARGS}"
log "Keeping container alive..."
sleep infinity
filter_args "$@"

if should_keep_alive; then
# Persistent run: watch config.json and push changes back to the record
# for as long as the container lives.
start_config_monitor "${KSM_CONFIG}" "${KSM_TOKEN}" "${RECORD}"
status=0
run_command_args "${CONFIG_FILE}" || status=$?
finish "${status}"
fi

# One-shot run: no monitor needed. Sync the config back once so refreshed
# device/session state is preserved, then exit with the command's status.
status=0
run_command_args "${CONFIG_FILE}" || status=$?
upload_config_to_ksm "${KSM_CONFIG}" "${KSM_TOKEN}" "${RECORD}"
exit "${status}"
# Check if config.json is mounted or available
elif [[ -f "${CONFIG_FILE}" ]]; then
log "Config file found at ${CONFIG_FILE}, using config-based authentication"

# Filter out authentication arguments, keep the rest
COMMAND_ARGS=$(filter_args "$@")

# Execute commands or keep container alive
if [[ -z "${COMMAND_ARGS}" ]]; then
log "No command arguments provided, keeping container alive..."
sleep infinity
else
# Run the service command with config file
run_keeper_service "--config ${CONFIG_FILE}" "${COMMAND_ARGS}"
log "Keeping container alive..."
sleep infinity
fi
filter_args "$@"

status=0
run_command_args "${CONFIG_FILE}" || status=$?
finish "${status}"
# Check if user/password authentication is provided
elif [[ -n "${USER}" && -n "${PASSWORD}" ]]; then
log "No config file found, using user/password authentication"
Expand All @@ -381,33 +506,21 @@ elif [[ -n "${USER}" && -n "${PASSWORD}" ]]; then
setup_device "${USER}" "${PASSWORD}" "${SERVER}"

# Filter out authentication arguments, keep the rest
COMMAND_ARGS=$(filter_args "$@")
filter_args "$@"

# Execute commands or keep container alive
if [[ -z "${COMMAND_ARGS}" ]]; then
log "Keeping container alive..."
sleep infinity
else
# Run the service command without credentials (device is now registered)
run_keeper_service "" "${COMMAND_ARGS}"
log "Keeping container alive..."
sleep infinity
fi
# Run without credentials -- the device is registered at this point.
status=0
run_command_args "" || status=$?
finish "${status}"
# Fallback: no authentication provided
else
log "No config file found and no user/password provided"

# Filter out authentication arguments, keep the rest
COMMAND_ARGS=$(filter_args "$@")
filter_args "$@"

# Execute commands or keep container alive
if [[ -z "${COMMAND_ARGS}" ]]; then
log "Keeping container alive..."
sleep infinity
else
# Run the command directly without any authentication setup
run_keeper_service "" "${COMMAND_ARGS}"
log "Keeping container alive..."
sleep infinity
fi
# Run the command directly without any authentication setup
status=0
run_command_args "" || status=$?
finish "${status}"
fi
51 changes: 0 additions & 51 deletions keeper-win-file.spec

This file was deleted.

Loading