Files
Cascade/README.md
2026-05-02 02:16:46 +04:00

8.1 KiB
Raw Blame History

Cascade

Cascade is a lightweight, terminal-based interactive menu that lets administrators expose a curated set of shell commands to users without giving them a full shell. It is written in Go with no external dependencies.


Table of Contents


How It Works

When Cascade starts it:

  1. Reads the application config to know which shell to use and where the menu file lives.
  2. Reads the menu file and builds an in-memory tree from dot-separated keys.
  3. Switches stdin to raw mode and presents the current menu level.
  4. The user navigates and selects an item using arrow keys or by typing a number — both methods work simultaneously.
  5. 0 / selecting the Back/Exit item goes one level up (or exits at the root).
  6. The selected command is executed by spawning <shell> -c "<command>" with stdin/stdout/stderr inherited from the current terminal, so fully interactive programs (shells, less, psql, etc.) work correctly.
  7. After the command exits, the menu is redrawn. The menu file is reloaded on every redraw, so changes take effect without restarting Cascade.

Building and Installing

# Build binary into ./build/cascade
make build

# Build and run locally
make run

# Install to /opt/cascade/ (copies binary + config files)
sudo make install

# Remove /opt/cascade/
sudo make delete

# Remove ./build/
make clean

make install backs up existing /opt/cascade/config.ini and /opt/cascade/menu.ini before overwriting them (timestamped .old.* copies).


Configuration Files

Application Config (config.ini)

A simple key=value file. Blank lines and lines starting with # are ignored.

Key Default Description
shell /bin/sh The shell used to execute menu commands (<shell> -c "<cmd>").
menu (none) Explicit path to the menu file. Overridden by CASCADE_MENU.

Example (config.ini.example):

shell=/bin/bash
menu=/etc/cascade/menu.ini

Menu Config (menu.ini)

Defines the tree of commands shown to the user. See Menu File Format below.


Configuration Resolution Order

Application config

Cascade tries each candidate in order and stops at the first one it can read:

  1. Path in $CASCADE_CONF environment variable.
  2. $HOME/.cascade/config.ini
  3. /opt/cascade/config.ini
  4. Built-in defaults (shell=/bin/sh, no menu path).

Menu file

  1. Path in $CASCADE_MENU environment variable.
  2. menu= value from the application config.
  3. $HOME/.cascade/menu.ini (only if the file exists).
  4. /opt/cascade/menu.ini (only if the file exists).
  5. Fatal error — Cascade exits with a descriptive message.

Menu File Format

Comments and Section Headers

Lines starting with # are comments and are ignored entirely.

# This is a top-level comment
## This is a sub-section comment
Key=command

Dot-Separated Keys

Each non-comment, non-blank line must be Key=command. The key uses dots (.) as level separators to express a hierarchy:

Level1.Level2.Level3=shell command here

This creates a three-level menu: entering Level1 shows Level2; entering Level2 shows Level3; selecting Level3 runs the command.

Example:

Nginx.Restart=docker restart portal-nginx
Nginx.Logs.Access=docker exec -it portal-nginx less /var/log/nginx/access.log
Nginx.Logs.Error=docker exec -it portal-nginx less /var/log/nginx/error.log

Results in this navigation tree:

[ Cascade v0.01 ]

Select Option:

  1) Nginx <-

  0) Exit

Enter number:

After pressing Enter (or ↓ then Enter):

[ Nginx ]

Select Option:

  1) Logs
  2) Restart <-

  0) Back

Enter number:
[ Nginx > Logs ]

Select Option:

  1) Access
  2) Error

  0) Back <-

Enter number:

Keys at the same level are displayed sorted alphabetically.

Navigation Controls

Input Action
↑ / ↓ Move the cursor one item up or down. Wraps around at both ends. Moving clears any typed number.
Digit keys (09) Type a number directly. The cursor jumps to the matching item as you type.
Backspace Delete the last typed digit.
Enter Confirm the selection — uses the typed number if one is being entered, otherwise uses the cursor position.

Both input methods (arrows and number typing) can be mixed freely at any time.

Mixed Nodes (Branch + Leaf)

A node can have both a direct command and child items. When this happens, an extra option (run directly) appears at the top of the submenu, allowing the user to execute the node's own command without navigating further.

Hrm.Shell=docker exec -it portal-hrm-php sh
Hrm.Service.PhpFpm.Restart=docker restart portal-hrm-php

Hrm has its own command (Shell) and also has children (Service). Selecting Hrm opens a submenu that includes (run directly) as option 1.


Environment Variables

Variable Description
CASCADE_CONF Override the path to the application config file.
CASCADE_MENU Override the path to the menu config file.

These variables take highest priority in their respective resolution chains.


Use Case: SSH User Isolation

Cascade is designed to be used as a restricted ForceCommand in OpenSSH, so that certain SSH users see only the allowed menu instead of a real shell.

/etc/ssh/sshd_config (or a drop-in file):

Match User dev
    SetEnv CASCADE_MENU=/opt/cascade/dev.ini
    SetEnv CASCADE_CONF=/opt/cascade/config.ini
    ForceCommand /usr/bin/cascade

What this does:

  • ForceCommand /usr/bin/cascade — every time the user dev connects, Cascade is launched instead of their login shell, regardless of what command they pass to ssh.
  • SetEnv CASCADE_CONF — points to the application config (defines the shell used to run commands).
  • SetEnv CASCADE_MENU — points to a menu file specific to this user, so different users can have different sets of allowed commands.

Different users (or Match blocks) can each have their own CASCADE_MENU pointing to different .ini files, giving fine-grained control over which commands each user can run.

Deployment checklist:

  1. Copy the cascade binary to /usr/bin/cascade (or /opt/cascade/cascade).
  2. Create /opt/cascade/config.ini with at minimum shell=/bin/bash.
  3. Create per-user (or shared) menu files, e.g. /opt/cascade/dev.ini.
  4. Add the Match block(s) to sshd_config and reload sshd (systemctl reload sshd).

Security Notes

  • Raw terminal mode — stdin is switched to raw mode only while waiting for a key press and is restored before running any command, so executed programs see a normal terminal.
  • Input validation — typed number input is limited to 16 characters and must be all digits. Only arrow keys, digits, Backspace, and Enter are acted upon; all other key sequences are ignored.
  • Line length — config lines longer than 4096 bytes are rejected by the scanner.
  • No shell access — commands are defined entirely by the administrator in the menu file. The user can only pick a number; they cannot inject arbitrary commands.
  • ForceCommand — when used with SSH ForceCommand, the user cannot bypass Cascade by passing a command to ssh directly.
  • Menu hot-reload — the menu file is re-read on every screen redraw. Ensure the file is writable only by root/admin to prevent privilege escalation.