lib/kitsune/kit/commands/setup_user.rb
require "thor"
require "net/ssh"
require_relative "../defaults"
require_relative "../options_builder"
module Kitsune
module Kit
module Commands
class SetupUser < Thor
namespace "setup_user"
class_option :server_ip, aliases: "-s", required: true, desc: "Server IP address or hostname"
class_option :ssh_port, aliases: "-p", desc: "SSH port"
class_option :ssh_key_path, aliases: "-k", desc: "Path to your private SSH key"
desc "create", "Create and configure 'deploy' user on remote server"
def create
filled_options = Kitsune::Kit::OptionsBuilder.build(
options,
required: [:server_ip],
defaults: Kitsune::Kit::Defaults.ssh
)
with_ssh_connection(false, filled_options) do |ssh|
perform_setup(ssh)
end
end
desc "rollback", "Revert configuration and remove 'deploy' user from remote server"
def rollback
filled_options = Kitsune::Kit::OptionsBuilder.build(
options,
required: [:server_ip],
defaults: Kitsune::Kit::Defaults.ssh
)
server = filled_options[:server_ip]
port = filled_options[:ssh_port]
key = File.expand_path(filled_options[:ssh_key_path])
# First, attempt SSH config restore as 'deploy'
begin
with_ssh_connection(true, filled_options) do |ssh|
perform_rollback_config(ssh)
end
rescue StandardError => e
say "⚠️ Skipping SSH config restore: #{e.message}", :yellow
end
# Then reconnect as 'root' to remove sudoers and delete user
say "🔑 Reconnecting as root@#{server}:#{port}", :green
Net::SSH.start(server, 'root', port: port, keys: [key], non_interactive: true) do |ssh|
perform_rollback_cleanup(ssh)
end
say "✅ Rollback completed", :green
end
no_commands do
def with_ssh_connection(rollback, filled_options)
server = filled_options[:server_ip]
port = filled_options[:ssh_port]
key = File.expand_path(filled_options[:ssh_key_path])
user = rollback ? 'deploy' : detect_remote_user(server, port, key)
say "🔑 Connecting as #{user}@#{server}:#{port}", :green
Net::SSH.start(server, user, port: port, keys: [key], non_interactive: true, timeout: 5) do |ssh|
yield ssh
end
end
def detect_remote_user(server, port, key)
%w[deploy root].each do |u|
begin
Net::SSH.start(server, u, port: port, keys: [key], non_interactive: true, timeout: 5) { }
say "✔️ Able to SSH as #{u}", :green
return u
rescue
next
end
end
abort("❌ Could not connect as deploy or root on #{server}:#{port}")
end
def perform_setup(ssh)
output = ssh.exec! <<~'EOH'
set -e
echo "✍🏻 Creating deploy user…"
if ! id deploy &>/dev/null; then
if command -v adduser &>/dev/null; then
sudo adduser --disabled-password --gecos "" deploy && echo " - user 'deploy' created"
else
sudo useradd -m -s /bin/bash deploy && echo " - user 'deploy' created"
fi
sudo usermod -aG sudo deploy && echo " - 'deploy' added to sudo"
else
echo " - user 'deploy' already exists"
fi
echo "✍🏻 Configuring passwordless sudo…"
if [ ! -f /etc/sudoers.d/deploy ]; then
echo 'deploy ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/deploy \
&& sudo chmod 440 /etc/sudoers.d/deploy \
&& echo " - sudoers entry created"
else
echo " - sudoers entry exists"
fi
echo "✍🏻 Backing up SSH config…"
sudo test -f /etc/ssh/sshd_config.bak || sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak && echo " - sshd_config backed up"
echo "✍🏻 Hardening SSH…"
grep -q '^PermitRootLogin no' /etc/ssh/sshd_config \
|| sudo sed -i 's/^#*PermitRootLogin .*/PermitRootLogin no/' /etc/ssh/sshd_config && echo " - PermitRootLogin no"
grep -q '^PasswordAuthentication no' /etc/ssh/sshd_config \
|| sudo sed -i 's/^#*PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config && echo " - PasswordAuthentication no"
sudo systemctl restart sshd && echo " - sshd restarted"
echo "✍🏻 Installing SSH keys for deploy…"
if [ ! -f /home/deploy/.ssh/authorized_keys ]; then
sudo mkdir -p /home/deploy/.ssh
sudo cp /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
sudo chown -R deploy:deploy /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh
sudo chmod 600 /home/deploy/.ssh/authorized_keys
echo " - authorized_keys copied"
else
echo " - authorized_keys already present"
fi
EOH
say output
say "✅ Setup completed", :green
end
def perform_rollback_config(ssh)
output = ssh.exec! <<~'EOH'
set -e
echo "🔁 Backing up SSH config…"
sudo test -f /etc/ssh/sshd_config.bak || sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak && echo " - sshd_config backed up"
echo "✍🏻 Restoring SSH config…"
grep -q '^PermitRootLogin yes' /etc/ssh/sshd_config \
|| sudo sed -i 's/^#*PermitRootLogin .*/PermitRootLogin yes/' /etc/ssh/sshd_config && echo " - PermitRootLogin yes"
grep -q '^PasswordAuthentication yes' /etc/ssh/sshd_config \
|| sudo sed -i 's/^#*PasswordAuthentication .*/PasswordAuthentication yes/' /etc/ssh/sshd_config && echo " - PasswordAuthentication yes"
sudo systemctl restart sshd && echo " - sshd restarted"
EOH
say output
say "✅ SSH config restored, closing deploy session", :green
end
def perform_rollback_cleanup(ssh)
output = ssh.exec! <<~'EOH'
set -e
echo "✍🏻 Removing sudoers file…"
if [ -f /etc/sudoers.d/deploy ]; then
sudo rm -f /etc/sudoers.d/deploy && echo " - /etc/sudoers.d/deploy removed"
else
echo " - no sudoers file to remove"
fi
echo "✍🏻 Killing remaining processes for deploy…"
if id deploy &>/dev/null; then
sudo pkill -u deploy && echo " - processes killed" || echo " - no processes found"
else
echo " - user 'deploy' does not exist, skipping"
fi
echo "✍🏻 Deleting deploy user…"
if id deploy &>/dev/null; then
if command -v deluser &>/dev/null; then
sudo deluser --remove-home deploy && echo " - deploy user removed"
else
sudo userdel -r deploy && echo " - deploy user removed"
fi
else
echo " - deploy user does not exist"
fi
EOH
say output
end
end
end
end
end
end