- Package watchdog scripts (killswitch, cpu-watchdog, egress-watchdog) with proper runtimeInputs, referenced directly by systemd - Package admin scripts (learner-add, learner-remove) in systemPackages - Fix ShellCheck issues in scripts (SC2129, SC2155, SC2115, SC2162) - Remove manual /usr/local/bin deployment, scripts deploy with nixos-rebuild - Update AGENTS.md with new deployment workflow Closes epic ops-jrz1-gwk 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
153 lines
4 KiB
Bash
Executable file
153 lines
4 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# learner-add.sh - Add a new dev user account
|
|
# Usage: learner-add.sh <username> <ssh-pubkey>
|
|
#
|
|
# Creates:
|
|
# - Unix user account with SSH key
|
|
# - Adds to learners group (Slack token access)
|
|
# - Outputs onboarding instructions
|
|
|
|
set -euo pipefail
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
|
|
|
|
usage() {
|
|
echo "Usage: $0 <username> <ssh-pubkey>"
|
|
echo ""
|
|
echo "Arguments:"
|
|
echo " username - Learner's username (alphanumeric, 3-16 chars)"
|
|
echo " ssh-pubkey - SSH public key (starts with ssh-ed25519, ssh-rsa, etc.)"
|
|
echo ""
|
|
echo "Example:"
|
|
echo " $0 alice 'ssh-ed25519 AAAA... alice@laptop'"
|
|
exit 1
|
|
}
|
|
|
|
validate_username() {
|
|
local username="$1"
|
|
if [[ ! "$username" =~ ^[a-z][a-z0-9_-]{2,15}$ ]]; then
|
|
log_error "Invalid username: must be 3-16 chars, start with letter, alphanumeric/underscore/dash only"
|
|
exit 1
|
|
fi
|
|
|
|
# Check if user already exists
|
|
if id "$username" &>/dev/null; then
|
|
log_error "User '$username' already exists"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
validate_ssh_key() {
|
|
local key="$1"
|
|
if [[ ! "$key" =~ ^ssh-(ed25519|rsa|ecdsa) ]]; then
|
|
log_error "Invalid SSH key: must start with ssh-ed25519, ssh-rsa, or ssh-ecdsa"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
create_user() {
|
|
local username="$1"
|
|
local ssh_key="$2"
|
|
|
|
log_info "Creating user '$username'..."
|
|
|
|
# Create user with home directory
|
|
# NixOS: don't specify shell (uses default), group is 'users'
|
|
useradd -m -g users "$username"
|
|
|
|
# Make home directory private (not world-readable)
|
|
chmod 700 "/home/$username"
|
|
|
|
# Add to learners group for Slack token access
|
|
usermod -aG learners "$username"
|
|
|
|
# Set up SSH key
|
|
local ssh_dir="/home/$username/.ssh"
|
|
mkdir -p "$ssh_dir"
|
|
echo "$ssh_key" > "$ssh_dir/authorized_keys"
|
|
chmod 700 "$ssh_dir"
|
|
chmod 600 "$ssh_dir/authorized_keys"
|
|
chown -R "$username:users" "$ssh_dir"
|
|
|
|
# Add Slack env vars to bashrc
|
|
{
|
|
echo ''
|
|
echo '# Slack bot development tokens'
|
|
echo 'source /etc/slack-learner.env'
|
|
} >> "/home/$username/.bashrc"
|
|
|
|
log_info "User created with SSH access"
|
|
}
|
|
|
|
print_onboarding() {
|
|
local username="$1"
|
|
local server_ip
|
|
# NixOS: use ip command instead of hostname -I
|
|
server_ip=$(ip -4 addr show scope global | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -1)
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo " Dev Environment Ready: $username"
|
|
echo "=========================================="
|
|
echo ""
|
|
echo "## SSH Config (~/.ssh/config on your laptop)"
|
|
echo ""
|
|
echo " Host dev-server"
|
|
echo " HostName ${server_ip:-<server-ip>}"
|
|
echo " User $username"
|
|
echo " LocalForward 8080 127.0.0.1:8080"
|
|
echo ""
|
|
echo "## Quick Start"
|
|
echo ""
|
|
echo "1. SSH in:"
|
|
echo " ssh dev-server"
|
|
echo ""
|
|
echo "2. Authenticate Claude (first time only):"
|
|
echo " claude"
|
|
echo " # Opens localhost URL - paste in your local browser"
|
|
echo " # Complete OAuth, token flows back automatically"
|
|
echo ""
|
|
echo "3. Start coding:"
|
|
echo " mkdir mybot && cd mybot"
|
|
echo " claude 'create a slack bot that responds to hello'"
|
|
echo ""
|
|
echo "## Tools Available"
|
|
echo " python3, uv, go (nix profile install nixpkgs#go)"
|
|
echo " Slack tokens: \$SLACK_BOT_TOKEN, \$SLACK_APP_TOKEN"
|
|
echo ""
|
|
echo "=========================================="
|
|
}
|
|
|
|
main() {
|
|
if [[ $# -lt 2 ]]; then
|
|
usage
|
|
fi
|
|
|
|
local username="$1"
|
|
local ssh_key="$2"
|
|
|
|
# Must run as root
|
|
if [[ $EUID -ne 0 ]]; then
|
|
log_error "This script must be run as root"
|
|
exit 1
|
|
fi
|
|
|
|
validate_username "$username"
|
|
validate_ssh_key "$ssh_key"
|
|
|
|
create_user "$username" "$ssh_key"
|
|
print_onboarding "$username"
|
|
|
|
log_info "Dev user '$username' setup complete!"
|
|
}
|
|
|
|
main "$@"
|