Prepare, configure, and manage Firecracker microVMs in seconds!
virtualization
linux
microvm
firecracker
1use std::fs;
2
3use anyhow::anyhow;
4use anyhow::Context;
5use anyhow::Error;
6use firecracker_prepare::command::run_command_with_stdout_inherit;
7
8use crate::types::VmOptions;
9
10pub fn setup_tailscale(name: &str, config: &VmOptions) -> Result<(), Error> {
11 if let Some(tailscale) = &config.tailscale {
12 if let Some(auth_key) = &tailscale.auth_key {
13 let len = auth_key.len();
14 let display_key = if len > 16 {
15 format!("{}****{}", &auth_key[..16], &auth_key[len - 4..])
16 } else {
17 return Err(anyhow!("Tailscale auth key is too short"));
18 };
19 println!("[+] Setting up Tailscale with auth key: {}", display_key);
20 let key_path =
21 get_private_key_path().with_context(|| "Failed to get SSH private key path")?;
22
23 let guest_ip = format!("{}.firecracker", name);
24
25 if config.nixos.unwrap_or(false) {
26 run_ssh_command(
27 &key_path,
28 &guest_ip,
29 &format!("tailscale up --auth-key {} --hostname {}", auth_key, name),
30 )?;
31 run_ssh_command(&key_path, &guest_ip, "systemctl status tailscaled || true")?;
32 run_ssh_command(&key_path, &guest_ip, "tailscale status || true")?;
33 println!("[+] Tailscale setup completed.");
34 return Ok(());
35 }
36
37 run_ssh_command(&key_path, &guest_ip, "rm -f /etc/security/namespace.init")?;
38
39 if config.alpine.unwrap_or(false) {
40 run_ssh_command(&key_path, &guest_ip, "apk add openrc")?;
41 }
42
43 if config.gentoo.unwrap_or(false) {
44 run_ssh_command(&key_path, &guest_ip, "emerge --sync")?;
45 run_ssh_command(&key_path, &guest_ip, "emerge net-misc/curl")?;
46 }
47
48 if config.slackware.unwrap_or(false) {
49 // run_ssh_command(&key_path, &guest_ip, "slackpkg update")?;
50 run_ssh_command(
51 &key_path,
52 &guest_ip,
53 "yes | slackpkg install nghttp2 brotli zstd libidn2 libpsl cyrus-sasl perl",
54 )?;
55 run_ssh_command(&key_path, &guest_ip, "update-ca-certificates --fresh")?;
56 }
57
58 run_ssh_command(
59 &key_path,
60 &guest_ip,
61 "type tailscaled || curl -fsSL https://tailscale.com/install.sh | sh",
62 )?;
63
64 if config.alpine.unwrap_or(false) || config.slackware.unwrap_or(false) {
65 run_ssh_command(
66 &key_path,
67 &guest_ip,
68 &format!("tailscale up --auth-key {} --hostname {}", auth_key, name),
69 )?;
70 run_ssh_command(&key_path, &guest_ip, "rc-status")?;
71 } else {
72 run_ssh_command(
73 &key_path,
74 &guest_ip,
75 "systemctl enable tailscaled && systemctl start tailscaled || true",
76 )?;
77 run_ssh_command(
78 &key_path,
79 &guest_ip,
80 &format!("tailscale up --auth-key {} --hostname {}", auth_key, name),
81 )?;
82 run_ssh_command(&key_path, &guest_ip, "systemctl status tailscaled || true")?;
83 }
84
85 run_ssh_command(&key_path, &guest_ip, "tailscale status || true")?;
86
87 println!("[+] Tailscale setup completed.");
88 return Ok(());
89 }
90 }
91
92 println!("[+] Tailscale auth key not provided, skipping Tailscale setup.");
93 Ok(())
94}
95
96fn run_ssh_command(key_path: &str, guest_ip: &str, command: &str) -> Result<(), Error> {
97 run_command_with_stdout_inherit(
98 "ssh",
99 &[
100 "-i",
101 key_path,
102 "-o",
103 "StrictHostKeyChecking=no",
104 "-o",
105 "UserKnownHostsFile=/dev/null",
106 &format!("root@{}", guest_ip),
107 command,
108 ],
109 false,
110 )?;
111 Ok(())
112}
113
114fn get_private_key_path() -> Result<String, Error> {
115 let home_dir = dirs::home_dir().ok_or_else(|| anyhow!("Failed to get home directory"))?;
116 let app_dir = format!("{}/.fireup", home_dir.display());
117 let key_name = glob::glob(format!("{}/id_rsa", app_dir).as_str())
118 .with_context(|| "Failed to glob ssh key files")?
119 .last()
120 .ok_or_else(|| anyhow!("No SSH key file found"))?
121 .with_context(|| "Failed to get SSH key path")?;
122 let key_name = fs::canonicalize(&key_name)
123 .with_context(|| {
124 format!(
125 "Failed to resolve absolute path for SSH key: {}",
126 key_name.display()
127 )
128 })?
129 .display()
130 .to_string();
131 Ok(key_name)
132}