server_forge/
setup.rs

1//! # Setup Module
2//!
3//! This module provides functionality for performing initial setup tasks on a Linux server.
4//! It includes functions for updating the system, installing essential packages,
5//! setting up a firewall, and configuring SSH for improved security.
6//!
7//! The module is designed to work across different Linux distributions by using
8//! distribution-specific commands where necessary.
9use crate::config::Config;
10use crate::rollback::RollbackManager;
11use crate::utils::run_command;
12use log::info;
13use std::error::Error;
14use std::fs;
15
16/// Performs the initial setup of the server based on the provided configuration.
17///
18/// This function orchestrates the entire initial setup process, including:
19/// - Updating the system
20/// - Installing essential packages
21/// - Setting up the firewall
22/// - Configuring SSH
23///
24/// It creates a snapshot before starting the setup process for potential rollback.
25///
26/// # Arguments
27///
28/// * `config` - A reference to the `Config` struct containing setup configuration
29/// * `rollback` - A reference to the `RollbackManager` for creating snapshots
30///
31/// # Returns
32///
33/// Returns `Ok(())` if the initial setup is completed successfully, or an error if setup fails.
34pub fn initial_setup(config: &Config, rollback: &RollbackManager) -> Result<(), Box<dyn Error>> {
35    info!("Performing initial setup...");
36
37    let snapshot = rollback.create_snapshot()?;
38
39    update_system(config)?;
40    install_essential_packages(config)?;
41    setup_firewall(config)?;
42    setup_ssh()?;
43
44    rollback.commit_snapshot(snapshot)?;
45
46    info!("Initial setup completed");
47    Ok(())
48}
49
50/// Updates the system using the appropriate package manager for the Linux distribution.
51///
52/// This function runs system update commands specific to Ubuntu, CentOS, or Fedora.
53///
54/// # Arguments
55///
56/// * `config` - A reference to the `Config` struct containing the Linux distribution information
57///
58/// # Returns
59///
60/// Returns `Ok(())` if the system is updated successfully, or an error if the update fails.
61pub fn update_system(config: &Config) -> Result<(), Box<dyn Error>> {
62    match config.linux_distro.as_str() {
63        "ubuntu" => {
64            run_command("apt", &["update"])?;
65            run_command("apt", &["upgrade", "-y"])?;
66        }
67        "centos" => {
68            run_command("yum", &["update", "-y"])?;
69        }
70        "fedora" => {
71            run_command("dnf", &["upgrade", "-y"])?;
72        }
73        _ => return Err("Unsupported Linux distribution".into()),
74    }
75    Ok(())
76}
77
78/// Installs essential packages on the system.
79///
80/// This function installs a predefined list of essential packages using
81/// the appropriate package manager for the Linux distribution.
82///
83/// # Arguments
84///
85/// * `config` - A reference to the `Config` struct containing the Linux distribution information
86///
87/// # Returns
88///
89/// Returns `Ok(())` if all packages are installed successfully, or an error if installation fails.
90pub fn install_essential_packages(config: &Config) -> Result<(), Box<dyn Error>> {
91    let essential_packages = [
92        "curl",
93        "wget",
94        "vim",
95        "ufw",
96        "fail2ban",
97        "apt-listchanges",
98        "needrestart",
99        "debsums",
100        "apt-show-versions",
101    ];
102
103    match config.linux_distro.as_str() {
104        "ubuntu" => {
105            for package in &essential_packages {
106                run_command("apt", &["install", "-y", package])?;
107            }
108        }
109        "centos" => {
110            for package in &essential_packages {
111                run_command("yum", &["install", "-y", package])?;
112            }
113        }
114        "fedora" => {
115            for package in &essential_packages {
116                run_command("dnf", &["install", "-y", package])?;
117            }
118        }
119        _ => return Err("Unsupported Linux distribution".into()),
120    }
121    Ok(())
122}
123
124/// Sets up the firewall with basic rules and any custom rules specified in the configuration.
125///
126/// This function configures either UFW (for Ubuntu) or firewalld (for CentOS/Fedora)
127/// with default deny incoming, allow outgoing policy, and opens ports for SSH and any custom rules.
128///
129/// # Arguments
130///
131/// * `config` - A reference to the `Config` struct containing firewall configuration and Linux distribution information
132///
133/// # Returns
134///
135/// Returns `Ok(())` if the firewall is set up successfully, or an error if setup fails.
136pub fn setup_firewall(config: &Config) -> Result<(), Box<dyn Error>> {
137    match config.linux_distro.as_str() {
138        "ubuntu" => {
139            run_command("ufw", &["default", "deny", "incoming"])?;
140            run_command("ufw", &["default", "allow", "outgoing"])?;
141            run_command("ufw", &["allow", "OpenSSH"])?;
142            for rule in &config.custom_firewall_rules {
143                run_command("ufw", &["allow", rule])?;
144            }
145            run_command("ufw", &["enable"])?;
146        }
147        "centos" | "fedora" => {
148            run_command("systemctl", &["start", "firewalld"])?;
149            run_command("systemctl", &["enable", "firewalld"])?;
150            run_command(
151                "firewall-cmd",
152                &["--zone=public", "--add-service=ssh", "--permanent"],
153            )?;
154            for rule in &config.custom_firewall_rules {
155                run_command(
156                    "firewall-cmd",
157                    &["--zone=public", "--add-port=", rule, "--permanent"],
158                )?;
159            }
160            run_command("firewall-cmd", &["--reload"])?;
161        }
162        _ => return Err("Unsupported Linux distribution".into()),
163    }
164    Ok(())
165}
166
167/// Configures SSH for improved security.
168///
169/// This function modifies the SSH configuration to:
170/// - Disable root login
171/// - Disable password authentication (requiring key-based authentication)
172/// - Change the default SSH port (TODO: implement this securely)
173///
174/// After making changes, it restarts the SSH service to apply the new configuration.
175///
176/// # Returns
177///
178/// Returns `Ok(())` if SSH is configured successfully, or an error if configuration fails.
179pub fn setup_ssh() -> Result<(), Box<dyn Error>> {
180    let ssh_config = "/etc/ssh/sshd_config";
181    let mut ssh_content = fs::read_to_string(ssh_config)?;
182    ssh_content = ssh_content
183        .replace("PermitRootLogin yes", "PermitRootLogin no")
184        .replace("#PasswordAuthentication yes", "PasswordAuthentication no")
185        .replace("#Port 22", "Port 2222"); //TODO: Change SSH port for better security
186    fs::write(ssh_config, ssh_content)?;
187
188    run_command("systemctl", &["restart", "sshd"])?;
189    Ok(())
190}