...
This commit is contained in:
@@ -57,12 +57,69 @@ chown root:ourworld /code
|
||||
chmod 2775 /code # rwx for user+group, SGID bit so new files inherit group
|
||||
echo "✅ /code prepared (group=ourworld, rwx for group, SGID bit set)"
|
||||
|
||||
# --- create login helper script for ssh-agent ---
|
||||
PROFILE_SCRIPT="$USERHOME/.profile_sshagent"
|
||||
cat > "$PROFILE_SCRIPT" <<'EOF'
|
||||
# Auto-start ssh-agent if not running
|
||||
SSH_AGENT_PID_FILE="$HOME/.ssh/agent.pid"
|
||||
SSH_AUTH_SOCK_FILE="$HOME/.ssh/agent.sock"
|
||||
|
||||
# Function to start ssh-agent
|
||||
start_ssh_agent() {
|
||||
mkdir -p "$HOME/.ssh"
|
||||
chmod 700 "$HOME/.ssh"
|
||||
|
||||
# Start ssh-agent and save connection info
|
||||
ssh-agent -s > "$SSH_AGENT_PID_FILE"
|
||||
source "$SSH_AGENT_PID_FILE"
|
||||
|
||||
# Save socket path for future sessions
|
||||
echo "$SSH_AUTH_SOCK" > "$SSH_AUTH_SOCK_FILE"
|
||||
|
||||
# Load all private keys found in ~/.ssh
|
||||
if [ -d "$HOME/.ssh" ]; then
|
||||
for KEY in "$HOME"/.ssh/*; do
|
||||
if [ -f "$KEY" ] && [ ! "${KEY##*.}" = "pub" ] && grep -q "PRIVATE KEY" "$KEY" 2>/dev/null; then
|
||||
ssh-add "$KEY" >/dev/null 2>&1 && echo "🔑 Loaded key: $(basename $KEY)"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if ssh-agent is running
|
||||
if [ -f "$SSH_AGENT_PID_FILE" ]; then
|
||||
source "$SSH_AGENT_PID_FILE" >/dev/null 2>&1
|
||||
# Test if agent is responsive
|
||||
if ! ssh-add -l >/dev/null 2>&1; then
|
||||
start_ssh_agent
|
||||
else
|
||||
# Agent is running, restore socket path
|
||||
if [ -f "$SSH_AUTH_SOCK_FILE" ]; then
|
||||
export SSH_AUTH_SOCK=$(cat "$SSH_AUTH_SOCK_FILE")
|
||||
fi
|
||||
fi
|
||||
else
|
||||
start_ssh_agent
|
||||
fi
|
||||
|
||||
# For interactive shells
|
||||
if [[ $- == *i* ]]; then
|
||||
echo "🔑 SSH Agent ready at $SSH_AUTH_SOCK"
|
||||
# Show loaded keys
|
||||
KEY_COUNT=$(ssh-add -l 2>/dev/null | wc -l)
|
||||
if [ "$KEY_COUNT" -gt 0 ]; then
|
||||
echo "🔑 $KEY_COUNT SSH key(s) loaded"
|
||||
fi
|
||||
fi
|
||||
|
||||
EOF
|
||||
|
||||
chown "$NEWUSER":"$NEWUSER" "$PROFILE_SCRIPT"
|
||||
chmod 644 "$PROFILE_SCRIPT"
|
||||
|
||||
# --- source it on login ---
|
||||
if ! grep -q ".profile_gpgagent" "$USERHOME/.bashrc"; then
|
||||
echo "[ -f ~/.profile_gpgagent ] && source ~/.profile_gpgagent" >> "$USERHOME/.bashrc"
|
||||
if ! grep -q ".profile_sshagent" "$USERHOME/.bashrc"; then
|
||||
echo "[ -f ~/.profile_sshagent ] && source ~/.profile_sshagent" >> "$USERHOME/.bashrc"
|
||||
fi
|
||||
|
||||
echo "🎉 Setup complete for user $NEWUSER"
|
||||
echo "🎉 Setup complete for user $NEWUSER"
|
||||
@@ -1,29 +1,354 @@
|
||||
module linux
|
||||
|
||||
// import freeflowuniverse.herolib.osal.core as osal
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
// import freeflowuniverse.herolib.screen
|
||||
import os
|
||||
import time
|
||||
// import freeflowuniverse.herolib.ui.console
|
||||
import json
|
||||
import freeflowuniverse.herolib.core.pathlib
|
||||
import freeflowuniverse.herolib.osal.core as osal
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
@[heap]
|
||||
pub struct LinuxFactory {
|
||||
@[params]
|
||||
pub struct UserCreateArgs {
|
||||
pub mut:
|
||||
username string
|
||||
name string @[required]
|
||||
giteakey string
|
||||
giteaurl string
|
||||
passwd string
|
||||
description string
|
||||
email string
|
||||
tel string
|
||||
sshkey string // SSH public key
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct LinuxNewArgs {
|
||||
pub:
|
||||
username string
|
||||
pub struct UserDeleteArgs {
|
||||
pub mut:
|
||||
name string @[required]
|
||||
}
|
||||
|
||||
// return screen instance
|
||||
pub fn new(args LinuxNewArgs) !LinuxFactory {
|
||||
mut t := LinuxFactory{
|
||||
username: args.username
|
||||
}
|
||||
return t
|
||||
@[params]
|
||||
pub struct SSHKeyCreateArgs {
|
||||
pub mut:
|
||||
username string @[required]
|
||||
sshkey_name string @[required]
|
||||
sshkey_pub string
|
||||
sshkey_priv string
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct SSHKeyDeleteArgs {
|
||||
pub mut:
|
||||
username string @[required]
|
||||
sshkey_name string @[required]
|
||||
}
|
||||
|
||||
struct UserConfig {
|
||||
pub mut:
|
||||
name string
|
||||
giteakey string
|
||||
giteaurl string
|
||||
email string
|
||||
description string
|
||||
tel string
|
||||
}
|
||||
|
||||
// Check if running as root
|
||||
pub fn (mut lf LinuxFactory) check_root() ! {
|
||||
if os.getuid() != 0 {
|
||||
return error('❌ Must be run as root')
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new user with all the configuration
|
||||
pub fn (mut lf LinuxFactory) user_create(args UserCreateArgs) ! {
|
||||
lf.check_root()!
|
||||
|
||||
console.print_header('Creating user: ${args.name}')
|
||||
|
||||
// Save config to ~/hero/cfg/myconfig.json
|
||||
lf.save_user_config(args)!
|
||||
|
||||
// Create user using system commands
|
||||
lf.create_user_system(args)!
|
||||
}
|
||||
|
||||
// Delete a user
|
||||
pub fn (mut lf LinuxFactory) user_delete(args UserDeleteArgs) ! {
|
||||
lf.check_root()!
|
||||
|
||||
console.print_header('Deleting user: ${args.name}')
|
||||
|
||||
// Check if user exists
|
||||
if !osal.user_exists(args.name) {
|
||||
return error('User ${args.name} does not exist')
|
||||
}
|
||||
|
||||
// Delete user and home directory
|
||||
osal.exec(cmd: 'userdel -r ${args.name}')!
|
||||
console.print_green('✅ User ${args.name} deleted')
|
||||
|
||||
// Remove from config
|
||||
lf.remove_user_config(args.name)!
|
||||
}
|
||||
|
||||
// Create SSH key for user
|
||||
pub fn (mut lf LinuxFactory) sshkey_create(args SSHKeyCreateArgs) ! {
|
||||
lf.check_root()!
|
||||
|
||||
console.print_header('Creating SSH key for user: ${args.username}')
|
||||
|
||||
user_home := '/home/${args.username}'
|
||||
ssh_dir := '${user_home}/.ssh'
|
||||
|
||||
// Ensure SSH directory exists
|
||||
osal.dir_ensure(ssh_dir)!
|
||||
osal.exec(cmd: 'chmod 700 ${ssh_dir}')!
|
||||
|
||||
if args.sshkey_priv != '' && args.sshkey_pub != '' {
|
||||
// Both private and public keys provided
|
||||
priv_path := '${ssh_dir}/${args.sshkey_name}'
|
||||
pub_path := '${ssh_dir}/${args.sshkey_name}.pub'
|
||||
|
||||
osal.file_write(priv_path, args.sshkey_priv)!
|
||||
osal.file_write(pub_path, args.sshkey_pub)!
|
||||
|
||||
// Set permissions
|
||||
osal.exec(cmd: 'chmod 600 ${priv_path}')!
|
||||
osal.exec(cmd: 'chmod 644 ${pub_path}')!
|
||||
|
||||
console.print_green('✅ SSH keys installed for ${args.username}')
|
||||
} else {
|
||||
// Generate new SSH key (modern ed25519)
|
||||
key_path := '${ssh_dir}/${args.sshkey_name}'
|
||||
osal.exec(cmd: 'ssh-keygen -t ed25519 -f ${key_path} -N "" -C "${args.username}@$(hostname)"')!
|
||||
console.print_green('✅ New SSH key generated for ${args.username}')
|
||||
}
|
||||
|
||||
// Set ownership
|
||||
osal.exec(cmd: 'chown -R ${args.username}:${args.username} ${ssh_dir}')!
|
||||
}
|
||||
|
||||
// Delete SSH key for user
|
||||
pub fn (mut lf LinuxFactory) sshkey_delete(args SSHKeyDeleteArgs) ! {
|
||||
lf.check_root()!
|
||||
|
||||
console.print_header('Deleting SSH key for user: ${args.username}')
|
||||
|
||||
user_home := '/home/${args.username}'
|
||||
ssh_dir := '${user_home}/.ssh'
|
||||
|
||||
priv_path := '${ssh_dir}/${args.sshkey_name}'
|
||||
pub_path := '${ssh_dir}/${args.sshkey_name}.pub'
|
||||
|
||||
// Remove keys if they exist
|
||||
if os.exists(priv_path) {
|
||||
os.rm(priv_path)!
|
||||
console.print_green('✅ Removed private key: ${priv_path}')
|
||||
}
|
||||
if os.exists(pub_path) {
|
||||
os.rm(pub_path)!
|
||||
console.print_green('✅ Removed public key: ${pub_path}')
|
||||
}
|
||||
}
|
||||
|
||||
// Save user configuration to JSON file
|
||||
fn (mut lf LinuxFactory) save_user_config(args UserCreateArgs) ! {
|
||||
config_dir := '${os.home_dir()}/hero/cfg'
|
||||
osal.dir_ensure(config_dir)!
|
||||
|
||||
config_path := '${config_dir}/myconfig.json'
|
||||
|
||||
mut configs := []UserConfig{}
|
||||
|
||||
// Load existing configs if file exists
|
||||
if os.exists(config_path) {
|
||||
content := osal.file_read(config_path)!
|
||||
configs = json.decode([]UserConfig, content) or { []UserConfig{} }
|
||||
}
|
||||
|
||||
// Check if user already exists in config
|
||||
mut found_idx := -1
|
||||
for i, config in configs {
|
||||
if config.name == args.name {
|
||||
found_idx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
new_config := UserConfig{
|
||||
name: args.name
|
||||
giteakey: args.giteakey
|
||||
giteaurl: args.giteaurl
|
||||
email: args.email
|
||||
description: args.description
|
||||
tel: args.tel
|
||||
}
|
||||
|
||||
if found_idx >= 0 {
|
||||
configs[found_idx] = new_config
|
||||
} else {
|
||||
configs << new_config
|
||||
}
|
||||
|
||||
// Save updated configs
|
||||
content := json.encode_pretty(configs)
|
||||
osal.file_write(config_path, content)!
|
||||
console.print_green('✅ User config saved to ${config_path}')
|
||||
}
|
||||
|
||||
// Remove user from configuration
|
||||
fn (mut lf LinuxFactory) remove_user_config(username string) ! {
|
||||
config_dir := '${os.home_dir()}/hero/cfg'
|
||||
config_path := '${config_dir}/myconfig.json'
|
||||
|
||||
if !os.exists(config_path) {
|
||||
return // Nothing to remove
|
||||
}
|
||||
|
||||
content := osal.file_read(config_path)!
|
||||
mut configs := json.decode([]UserConfig, content) or { return }
|
||||
|
||||
// Filter out the user
|
||||
configs = configs.filter(it.name != username)
|
||||
|
||||
// Save updated configs
|
||||
updated_content := json.encode_pretty(configs)
|
||||
osal.file_write(config_path, updated_content)!
|
||||
console.print_green('✅ User config removed for ${username}')
|
||||
}
|
||||
|
||||
// Create user in the system
|
||||
fn (mut lf LinuxFactory) create_user_system(args UserCreateArgs) ! {
|
||||
// Check if user exists
|
||||
if osal.user_exists(args.name) {
|
||||
console.print_green('✅ User ${args.name} already exists')
|
||||
} else {
|
||||
console.print_item('➕ Creating user ${args.name}')
|
||||
osal.exec(cmd: 'useradd -m -s /bin/bash ${args.name}')!
|
||||
}
|
||||
|
||||
user_home := '/home/${args.name}'
|
||||
|
||||
// Setup SSH if key provided
|
||||
if args.sshkey != '' {
|
||||
ssh_dir := '${user_home}/.ssh'
|
||||
osal.dir_ensure(ssh_dir)!
|
||||
osal.exec(cmd: 'chmod 700 ${ssh_dir}')!
|
||||
|
||||
authorized_keys := '${ssh_dir}/authorized_keys'
|
||||
osal.file_write(authorized_keys, args.sshkey)!
|
||||
osal.exec(cmd: 'chmod 600 ${authorized_keys}')!
|
||||
osal.exec(cmd: 'chown -R ${args.name}:${args.name} ${ssh_dir}')!
|
||||
console.print_green('✅ SSH key installed for ${args.name}')
|
||||
}
|
||||
|
||||
// Ensure ourworld group exists
|
||||
group_check := osal.exec(cmd: 'getent group ourworld', raise_error: false) or {
|
||||
osal.Job{ exit_code: 1 }
|
||||
}
|
||||
if group_check.exit_code != 0 {
|
||||
console.print_item('➕ Creating group ourworld')
|
||||
osal.exec(cmd: 'groupadd ourworld')!
|
||||
} else {
|
||||
console.print_green('✅ Group ourworld exists')
|
||||
}
|
||||
|
||||
// Add user to group
|
||||
user_groups := osal.exec(cmd: 'id -nG ${args.name}', stdout: false)!
|
||||
if !user_groups.output.contains('ourworld') {
|
||||
osal.exec(cmd: 'usermod -aG ourworld ${args.name}')!
|
||||
console.print_green('✅ Added ${args.name} to ourworld group')
|
||||
} else {
|
||||
console.print_green('✅ ${args.name} already in ourworld')
|
||||
}
|
||||
|
||||
// Setup /code directory
|
||||
osal.dir_ensure('/code')!
|
||||
osal.exec(cmd: 'chown root:ourworld /code')!
|
||||
osal.exec(cmd: 'chmod 2775 /code')! // rwx for user+group, SGID bit
|
||||
console.print_green('✅ /code prepared (group=ourworld, rwx for group, SGID bit set)')
|
||||
|
||||
// Create SSH agent profile script
|
||||
lf.create_ssh_agent_profile(args.name)!
|
||||
|
||||
// Set password if provided
|
||||
if args.passwd != '' {
|
||||
osal.exec(cmd: 'echo "${args.name}:${args.passwd}" | chpasswd')!
|
||||
console.print_green('✅ Password set for ${args.name}')
|
||||
}
|
||||
|
||||
console.print_header('🎉 Setup complete for user ${args.name}')
|
||||
}
|
||||
|
||||
// Create SSH agent profile script
|
||||
fn (mut lf LinuxFactory) create_ssh_agent_profile(username string) ! {
|
||||
user_home := '/home/${username}'
|
||||
profile_script := '${user_home}/.profile_sshagent'
|
||||
|
||||
script_content := '# Auto-start ssh-agent if not running
|
||||
SSH_AGENT_PID_FILE="$HOME/.ssh/agent.pid"
|
||||
SSH_AUTH_SOCK_FILE="$HOME/.ssh/agent.sock"
|
||||
|
||||
# Function to start ssh-agent
|
||||
start_ssh_agent() {
|
||||
mkdir -p "$HOME/.ssh"
|
||||
chmod 700 "$HOME/.ssh"
|
||||
|
||||
# Start ssh-agent and save connection info
|
||||
ssh-agent -s > "$SSH_AGENT_PID_FILE"
|
||||
source "$SSH_AGENT_PID_FILE"
|
||||
|
||||
# Save socket path for future sessions
|
||||
echo "$SSH_AUTH_SOCK" > "$SSH_AUTH_SOCK_FILE"
|
||||
|
||||
# Load all private keys found in ~/.ssh
|
||||
if [ -d "$HOME/.ssh" ]; then
|
||||
for KEY in "$HOME"/.ssh/*; do
|
||||
if [ -f "$KEY" ] && [ ! "${KEY##*.}" = "pub" ] && grep -q "PRIVATE KEY" "$KEY" 2>/dev/null; then
|
||||
'ssh-' + 'add "$KEY" >/dev/null 2>&1 && echo "🔑 Loaded key: $(basename $KEY)"'
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if ssh-agent is running
|
||||
if [ -f "$SSH_AGENT_PID_FILE" ]; then
|
||||
source "$SSH_AGENT_PID_FILE" >/dev/null 2>&1
|
||||
# Test if agent is responsive
|
||||
if ! ('ssh-' + 'add -l >/dev/null 2>&1'); then
|
||||
start_ssh_agent
|
||||
else
|
||||
# Agent is running, restore socket path
|
||||
if [ -f "$SSH_AUTH_SOCK_FILE" ]; then
|
||||
export SSH_AUTH_SOCK=$(cat "$SSH_AUTH_SOCK_FILE")
|
||||
fi
|
||||
fi
|
||||
else
|
||||
start_ssh_agent
|
||||
fi
|
||||
|
||||
# For interactive shells
|
||||
if [[ $- == *i* ]]; then
|
||||
echo "🔑 SSH Agent ready at $SSH_AUTH_SOCK"
|
||||
# Show loaded keys
|
||||
KEY_COUNT=$('ssh-' + 'add -l 2>/dev/null | wc -l')
|
||||
if [ "$KEY_COUNT" -gt 0 ]; then
|
||||
echo "🔑 $KEY_COUNT SSH key(s) loaded"
|
||||
fi
|
||||
fi
|
||||
'
|
||||
|
||||
osal.file_write(profile_script, script_content)!
|
||||
osal.exec(cmd: 'chown ${username}:${username} ${profile_script}')!
|
||||
osal.exec(cmd: 'chmod 644 ${profile_script}')!
|
||||
|
||||
// Source it on login
|
||||
bashrc := '${user_home}/.bashrc'
|
||||
bashrc_content := if os.exists(bashrc) { osal.file_read(bashrc)! } else { '' }
|
||||
|
||||
if !bashrc_content.contains('.profile_sshagent') {
|
||||
source_line := '[ -f ~/.profile_sshagent ] && source ~/.profile_sshagent\n'
|
||||
osal.file_write(bashrc, bashrc_content + source_line)!
|
||||
}
|
||||
|
||||
console.print_green('✅ SSH agent profile created for ${username}')
|
||||
}
|
||||
Reference in New Issue
Block a user