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}