Skip to content
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
99 changes: 99 additions & 0 deletions .github/workflows/deploy-ecr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Build and publish Commander ECR image

on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: [master]
workflow_dispatch:
inputs:
version_label:
description: Immutable image label (blank uses branch and SHA)
required: false
python_image:
description: Approved Python runtime image
required: false
default: python:3.11-slim
ecr_registry:
description: ECR registry hostname
required: true
default: 629781671340.dkr.ecr.us-east-1.amazonaws.com

permissions:
contents: read
id-token: write

env:
ECR_REGISTRY: ${{ inputs.ecr_registry || vars.ECR_REGISTRY || '629781671340.dkr.ecr.us-east-1.amazonaws.com' }}
PYTHON_IMAGE: ${{ inputs.python_image || vars.COMMANDER_PYTHON_IMAGE || 'python:3.11-slim' }}

jobs:
test:
name: Test Commander
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install and test
run: |
python -m pip install --upgrade pip wheel setuptools
pip install '.[test]'
python -m keepercommander.__main__ --version
python -m keepercommander.__main__ --help >/dev/null
pytest unit-tests/

publish:
name: Build and publish immutable image
needs: test
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-24.04
environment: commander-ecr
steps:
- uses: actions/checkout@v4
- name: Derive immutable tag
id: tag
shell: bash
run: |
set -euo pipefail
label="${{ inputs.version_label }}"
if [[ -z "$label" ]]; then
label="${GITHUB_REF_NAME//\//-}-${GITHUB_SHA::12}"
fi
echo "tag=$label" >> "$GITHUB_OUTPUT"
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-1
- name: Login to ECR
run: |
aws ecr get-login-password --region us-east-1 \
| docker login --username AWS --password-stdin "$ECR_REGISTRY"
- name: Build image
run: |
docker build \
--build-arg PYTHON_IMAGE="$PYTHON_IMAGE" \
-t "$ECR_REGISTRY/commander:${{ steps.tag.outputs.tag }}" .
- name: Smoke test image
run: |
docker run --rm "$ECR_REGISTRY/commander:${{ steps.tag.outputs.tag }}" --version
docker run --rm "$ECR_REGISTRY/commander:${{ steps.tag.outputs.tag }}" --help >/dev/null
- name: Scan image
uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: ${{ env.ECR_REGISTRY }}/commander:${{ steps.tag.outputs.tag }}
format: table
severity: CRITICAL,HIGH
ignore-unfixed: true
exit-code: '1'
- name: Push image
run: docker push "$ECR_REGISTRY/commander:${{ steps.tag.outputs.tag }}"
- name: Write image metadata
run: |
docker inspect "$ECR_REGISTRY/commander:${{ steps.tag.outputs.tag }}" > commander-image-metadata.json
- uses: actions/upload-artifact@v4
with:
name: commander-image-metadata-${{ steps.tag.outputs.tag }}
path: commander-image-metadata.json
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
FROM python:3.11-slim
ARG PYTHON_IMAGE=python:3.11-slim
FROM ${PYTHON_IMAGE}

ENV PYTHONUNBUFFERED=1 \
PYTHONHASHSEED=random \
Expand Down
5 changes: 5 additions & 0 deletions config.kiab.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"server": "local.keepersecurity.com",
"certificate_check": true,
"config_storage": "file"
}
5 changes: 4 additions & 1 deletion docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ readonly KEEPER_DIR="/home/commander/.keeper"
readonly CONFIG_FILE="${KEEPER_DIR}/config.json"
readonly MONITOR_LOG="${KEEPER_DIR}/config_monitor.log"
readonly MONITOR_PID_FILE="${KEEPER_DIR}/config_monitor.pid"
readonly DEFAULT_SERVER="keepersecurity.com"
readonly DEFAULT_SERVER="${KEEPER_SERVER:-keepersecurity.com}"
if [[ -n "${KEEPER_SERVER:-}" ]]; then
export KEEPER_ALLOW_CUSTOM_SERVER="${KEEPER_ALLOW_CUSTOM_SERVER:-true}"
fi
readonly DEVICE_TIMEOUT="43200" # 30 days in minutes
readonly MONITOR_INTERVAL="30" # Config monitoring interval in seconds

Expand Down
11 changes: 11 additions & 0 deletions keepercommander/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,17 @@ def resolve_server(server_input):
if server_lower in KEEPER_SERVERS.values():
return server_lower

# Explicit custom hosts are opt-in. This is used by self-hosted/KiaB
# deployments; normal region validation remains strict by default.
if os.getenv('KEEPER_ALLOW_CUSTOM_SERVER', '').lower() in ('1', 'true', 'yes'):
candidate = server_input.strip()
if candidate and ' ' not in candidate:
if '://' not in candidate:
candidate = 'https://' + candidate
parsed = urlparse(candidate)
if parsed.scheme in ('http', 'https') and parsed.hostname:
return parsed.geturl().rstrip('/')

# Not a valid server
return None

Expand Down
Loading