No description
  • Python 78.5%
  • Shell 21.1%
  • Just 0.4%
Find a file
2026-06-26 14:51:16 +00:00
docs access-gate: sync sudo with ssh window 2026-05-22 07:35:11 +02:00
scripts access-gate: include reason in SSH requests 2026-06-26 16:49:37 +02:00
templates access-gate: include reason in SSH requests 2026-06-26 16:49:37 +02:00
.env.example access-gate: improve group commands and deploys 2026-05-30 16:18:26 +02:00
.gitignore Initial public release 2026-05-21 14:55:31 +02:00
AGENTS.md Initial public release 2026-05-21 14:55:31 +02:00
deploy-env.sh access-gate: improve group commands and deploys 2026-05-30 16:18:26 +02:00
deploy.env.example access-gate: improve group commands and deploys 2026-05-30 16:18:26 +02:00
install-host.sh access-gate: sync sudo with ssh window 2026-05-22 07:35:11 +02:00
Justfile access-gate: improve group commands and deploys 2026-05-30 16:18:26 +02:00
README.md access-gate: improve group commands and deploys 2026-05-30 16:18:26 +02:00
update-host.sh bot: refine live access controls 2026-05-21 17:39:57 +02:00

telegram-access-gate

Temporary SSH and Telegram-approved sudo access for servers where an automation agent or operator should not keep a permanently privileged login open.

Design

  • Each server stands alone.
  • Prefer one Telegram bot per server.
  • A pinned Telegram group message can list server commands such as /example_server, /server_2, and /server_3.
  • The selected server bot shows the current SSH/SUDO status directly when a session is active.
  • /status without a server argument is intentionally broadcast-like: every bot that sees it answers with its own current state.
  • /<server> uses the configured server command for that server only; other server bots ignore foreign slash commands.
  • Request-only SSH uses a second locked-down user that can only send an access request.
  • The real working user defaults to access-gated.
  • The working user receives the managed SSH key only for the approved time window.
  • Privileged commands go through hakalya-sudo, which asks the allowed Telegram operator for approval.

No global controller is required. A controller or pinned command list is only UI convenience.

Security model

  • Telegram actions are accepted only from TELEGRAM_ALLOWED_USER_ID.
  • Approval questions can be posted to a shared group via TELEGRAM_APPROVAL_CHAT_ID, while callbacks still require TELEGRAM_ALLOWED_USER_ID.
  • Every callback includes a server name and is ignored on mismatches.
  • Bot tokens are root-readable only.
  • The request-only user has a forced command and no general shell path.
  • Sudo is not passwordless except for narrow root-owned wrappers.
  • Approval buttons are single-use in effect: first decision wins.
  • Revocation is fail-closed through a systemd timer.

Files

  • scripts/hakalya-access-bot.py - Telegram bot for SSH allow/status/adjust/revoke callbacks and sudo decisions.
  • scripts/hakalya-approved-sudo.py - root sudo approval wrapper.
  • scripts/hakalya-request-access-root.py - root helper for request-only SSH.
  • scripts/hakalya-gated-allow - root script that opens the gated SSH window.
  • scripts/hakalya-gated-revoke - root script that removes expired managed keys.
  • scripts/hakalya-sudo - user-facing sudo wrapper.
  • scripts/sudo-shadow - /usr/local/bin/sudo shim that routes the gated user through hakalya-sudo while preserving /usr/bin/sudo for other users.
  • templates/local-ssh-wrapper.sh - local wrapper that requests access and retries SSH.
  • install-host.sh - host installer for one server.
  • update-host.sh - updater for an already installed server.
  • .env.example - example host configuration with semantic placeholders.

Minimal install shape

Generate two local keypairs per server:

ssh-keygen -t ed25519 -N '' -f ~/.ssh/<server>_approval_ed25519 -C "access-gate-<server>-approval-ttl"
ssh-keygen -t ed25519 -N '' -f ~/.ssh/<server>_request_ed25519 -C "access-gate-<server>-request-only"

Then install on the server as root:

sudo ./install-host.sh \
  --server example-server \
  --allowed-telegram-id 123456789 \
  --approval-public-key ~/.ssh/example-server_approval_ed25519.pub \
  --request-public-key ~/.ssh/example-server_request_ed25519.pub

After install, place the Telegram bot token on the server:

sudoedit /etc/hakalya-access-gate/bot.env

Use .env.example as the non-secret shape. Required:

TELEGRAM_ALLOWED_USER_ID=<telegram-user-id>
TELEGRAM_BOT_TOKEN=<token>
ACCESS_GATE_SERVER_NAME=<server>
ACCESS_GATE_COMMAND=<telegram-slash-command-without-leading-slash>
ACCESS_GATE_WORKING_USER=access-gated
ACCESS_GATE_REQUEST_USER=access-request

Optional:

TELEGRAM_APPROVAL_CHAT_ID=<telegram-chat-id>
ACCESS_GATE_AGENT_NAME=Example Access Bot
ACCESS_GATE_OPERATOR_NAME=Example Operator
ACCESS_GATE_ICON=🖥️
ACCESS_GATE_DISPLAY_TZ=Europe/Berlin
ACCESS_GATE_MAX_MINUTES=240
ACCESS_GATE_SUDO_SESSION_MINUTES=30
ACCESS_GATE_BUTTON_TTL_SECONDS=900

TELEGRAM_APPROVAL_CHAT_ID is the group or DM where SSH/sudo approval questions are posted. Leave it unset to send approval questions to the TELEGRAM_ALLOWED_USER_ID DM.

When an SSH window is open, "SUDO session" approval follows that SSH window. If the operator adjusts the SSH expiry, an active sudo session is synced to the adjusted expiry.

ACCESS_GATE_COMMAND defaults to the lower-case server name with - replaced by _, because Telegram slash commands are friendlier with underscores.

The token file must stay root-only:

sudo chown root:root /etc/hakalya-access-gate/bot.env
sudo chmod 0600 /etc/hakalya-access-gate/bot.env
sudo systemctl enable --now hakalya-access-bot.service

The installer creates this baseline:

/etc/hakalya-access-gate/                 root:root 0755
/etc/hakalya-access-gate/bot.env          root:root 0600
/opt/hakalya-access-gate/                 root:root 0755
/opt/hakalya-access-gate/state/           root:root 0755
/opt/hakalya-access-gate/keys/            root:root 0755
/opt/hakalya-access-gate/keys/hakalya-approval.pub root:root 0644
/home/access-gated/.ssh/                  access-gated:access-gated 0700
/home/access-gated/.ssh/authorized_keys   access-gated:access-gated 0600
/home/access-request/.ssh/                access-request:access-request 0700
/home/access-request/.ssh/authorized_keys access-request:access-request 0600
/usr/local/sbin/hakalya-gated-allow       root:root 0755
/usr/local/sbin/hakalya-gated-revoke      root:root 0755
/usr/local/bin/hakalya-access-bot         root:root 0755
/usr/local/sbin/hakalya-approved-sudo     root:root 0755
/usr/local/bin/hakalya-sudo               root:root 0755
/usr/local/bin/sudo                       root:root 0755
/usr/local/bin/hakalya-request-access     root:root 0755
/usr/local/sbin/hakalya-request-access-root root:root 0755
/etc/sudoers.d/hakalya-gated              root:root 0440
/etc/sudoers.d/hakalya-request            root:root 0440

Updating an existing host

Use install-host.sh only for first bootstrap. For normal code updates, run the updater from this repo:

./update-host.sh \
  --host example-server \
  --server example-server \
  --command example_server \
  --approval-chat-id -1001234567890 \
  --icon 🖥️

The SSH window must already be open, or the command must be run from a console/root path. If hakalya-sudo has no active sudo session, it will ask through Telegram before applying the update.

The updater:

  • runs local shell and Python syntax checks,
  • uploads the scripts to /tmp/hakalya-access-gate-update by default,
  • installs them through hakalya-sudo,
  • records the SSH login user as ACCESS_GATE_WORKING_USER unless --working-user is given,
  • optionally updates TELEGRAM_APPROVAL_CHAT_ID, ACCESS_GATE_SERVER_NAME, ACCESS_GATE_COMMAND, and ACCESS_GATE_ICON,
  • restarts hakalya-access-bot.service,
  • verifies remote hashes against the local repo files.

It does not recreate users, keys, sudoers, or systemd units. If the host is not installed yet, use install-host.sh first.

For repeatable local deploys without committing hostnames, chat ids, server names, or other environment-specific values, use the env wrapper:

ACCESS_GATE_DEPLOY_HOST=example-server \
ACCESS_GATE_SERVER=example-server \
ACCESS_GATE_COMMAND=example_server \
ACCESS_GATE_WORKING_USER=access-gated \
./deploy-env.sh

Or with just:

ACCESS_GATE_DEPLOY_HOST=example-server \
ACCESS_GATE_SERVER=example-server \
ACCESS_GATE_COMMAND=example_server \
just deploy

You can copy deploy.env.example to a private local env file and source it before running ./deploy-env.sh.

Optional env vars: ACCESS_GATE_APPROVAL_CHAT_ID, ACCESS_GATE_ICON, ACCESS_GATE_WORKING_USER, ACCESS_GATE_SUDO_WRAPPER, and ACCESS_GATE_REMOTE_STAGE.

The deploy wrapper never reads or deploys TELEGRAM_BOT_TOKEN; tokens stay in /etc/hakalya-access-gate/bot.env on each host.