Prepare, configure, and manage Firecracker microVMs in seconds!
virtualization linux microvm firecracker

Initial Commit

+1695
+1
.gitignore
··· 1 + target/
+132
CODE_OF_CONDUCT.md
··· 1 + # Contributor Covenant Code of Conduct 2 + 3 + ## Our Pledge 4 + 5 + We as members, contributors, and leaders pledge to make participation in our 6 + community a harassment-free experience for everyone, regardless of age, body 7 + size, visible or invisible disability, ethnicity, sex characteristics, gender 8 + identity and expression, level of experience, education, socio-economic status, 9 + nationality, personal appearance, race, caste, color, religion, or sexual 10 + identity and orientation. 11 + 12 + We pledge to act and interact in ways that contribute to an open, welcoming, 13 + diverse, inclusive, and healthy community. 14 + 15 + ## Our Standards 16 + 17 + Examples of behavior that contributes to a positive environment for our 18 + community include: 19 + 20 + - Demonstrating empathy and kindness toward other people 21 + - Being respectful of differing opinions, viewpoints, and experiences 22 + - Giving and gracefully accepting constructive feedback 23 + - Accepting responsibility and apologizing to those affected by our mistakes, 24 + and learning from the experience 25 + - Focusing on what is best not just for us as individuals, but for the overall 26 + community 27 + 28 + Examples of unacceptable behavior include: 29 + 30 + - The use of sexualized language or imagery, and sexual attention or advances of 31 + any kind 32 + - Trolling, insulting or derogatory comments, and personal or political attacks 33 + - Public or private harassment 34 + - Publishing others' private information, such as a physical or email address, 35 + without their explicit permission 36 + - Other conduct which could reasonably be considered inappropriate in a 37 + professional setting 38 + 39 + ## Enforcement Responsibilities 40 + 41 + Community leaders are responsible for clarifying and enforcing our standards of 42 + acceptable behavior and will take appropriate and fair corrective action in 43 + response to any behavior that they deem inappropriate, threatening, offensive, 44 + or harmful. 45 + 46 + Community leaders have the right and responsibility to remove, edit, or reject 47 + comments, commits, code, wiki edits, issues, and other contributions that are 48 + not aligned to this Code of Conduct, and will communicate reasons for moderation 49 + decisions when appropriate. 50 + 51 + ## Scope 52 + 53 + This Code of Conduct applies within all community spaces, and also applies when 54 + an individual is officially representing the community in public spaces. 55 + Examples of representing our community include using an official e-mail address, 56 + posting via an official social media account, or acting as an appointed 57 + representative at an online or offline event. 58 + 59 + ## Enforcement 60 + 61 + Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 + reported to the community leaders responsible for enforcement at 63 + [GitHub Issues](https://github.com/tsirysndr/fireup/issues). All 64 + complaints will be reviewed and investigated promptly and fairly. 65 + 66 + All community leaders are obligated to respect the privacy and security of the 67 + reporter of any incident. 68 + 69 + ## Enforcement Guidelines 70 + 71 + Community leaders will follow these Community Impact Guidelines in determining 72 + the consequences for any action they deem in violation of this Code of Conduct: 73 + 74 + ### 1. Correction 75 + 76 + **Community Impact**: Use of inappropriate language or other behavior deemed 77 + unprofessional or unwelcome in the community. 78 + 79 + **Consequence**: A private, written warning from community leaders, providing 80 + clarity around the nature of the violation and an explanation of why the 81 + behavior was inappropriate. A public apology may be requested. 82 + 83 + ### 2. Warning 84 + 85 + **Community Impact**: A violation through a single incident or series of 86 + actions. 87 + 88 + **Consequence**: A warning with consequences for continued behavior. No 89 + interaction with the people involved, including unsolicited interaction with 90 + those enforcing the Code of Conduct, for a specified period of time. This 91 + includes avoiding interactions in community spaces as well as external channels 92 + like social media. Violating these terms may lead to a temporary or permanent 93 + ban. 94 + 95 + ### 3. Temporary Ban 96 + 97 + **Community Impact**: A serious violation of community standards, including 98 + sustained inappropriate behavior. 99 + 100 + **Consequence**: A temporary ban from any sort of interaction or public 101 + communication with the community for a specified period of time. No public or 102 + private interaction with the people involved, including unsolicited interaction 103 + with those enforcing the Code of Conduct, is allowed during this period. 104 + Violating these terms may lead to a permanent ban. 105 + 106 + ### 4. Permanent Ban 107 + 108 + **Community Impact**: Demonstrating a pattern of violation of community 109 + standards, including sustained inappropriate behavior, harassment of an 110 + individual, or aggression toward or disparagement of classes of individuals. 111 + 112 + **Consequence**: A permanent ban from any sort of public interaction within the 113 + community. 114 + 115 + ## Attribution 116 + 117 + This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 + version 2.1, available at 119 + [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 120 + 121 + Community Impact Guidelines were inspired by 122 + [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 + 124 + For answers to common questions about this code of conduct, see the FAQ at 125 + [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 126 + [https://www.contributor-covenant.org/translations][translations]. 127 + 128 + [homepage]: https://www.contributor-covenant.org 129 + [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 130 + [Mozilla CoC]: https://github.com/mozilla/diversity 131 + [FAQ]: https://www.contributor-covenant.org/faq 132 + [translations]: https://www.contributor-covenant.org/translations
+70
CONTRIBUTING.md
··· 1 + # Contributing Guidelines 2 + 3 + Thank you for your interest in contributing to our project. Whether it's a bug 4 + report, new feature, correction, or additional documentation, we greatly value 5 + feedback and contributions from our community. 6 + 7 + Please read through this document before submitting any issues or pull requests 8 + to ensure we have all the necessary information to effectively respond to your 9 + bug report or contribution. 10 + 11 + ## Reporting Bugs/Feature Requests 12 + 13 + We welcome you to use the GitHub issue tracker to report bugs or suggest 14 + features. 15 + 16 + When filing an issue, please check existing open, or recently closed, issues to 17 + make sure somebody else hasn't already reported the issue. Please try to include 18 + as much information as you can. Details like these are incredibly useful: 19 + 20 + - A reproducible test case or series of steps 21 + - The version of our code being used 22 + - Any modifications you've made relevant to the bug 23 + - Anything unusual about your environment or deployment 24 + 25 + ## Contributing via Pull Requests 26 + 27 + Contributions via pull requests are much appreciated. Before sending us a pull 28 + request, please ensure that: 29 + 30 + 1. You are working against the latest source on the _master_ branch. 31 + 2. You check existing open, and recently merged, pull requests to make sure 32 + someone else hasn't addressed the problem already. 33 + 3. You open an issue to discuss any significant work - we would hate for your 34 + time to be wasted. 35 + 36 + To send us a pull request, please: 37 + 38 + 1. Fork the repository. 39 + 2. Modify the source; please focus on the specific change you are contributing. 40 + If you also reformat all the code, it will be hard for us to focus on your 41 + change. 42 + 3. Ensure local tests pass. 43 + 4. Commit to your fork using clear commit messages. 44 + 5. Send us a pull request, answering any default questions in the pull request 45 + interface. 46 + 6. Pay attention to any automated CI failures reported in the pull request, and 47 + stay involved in the conversation. 48 + 49 + GitHub provides additional document on 50 + [forking a repository](https://help.github.com/articles/fork-a-repo/) and 51 + [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 52 + 53 + ## Finding contributions to work on 54 + 55 + Looking at the existing issues is a great way to find something to contribute 56 + on. As our projects, by default, use the default GitHub issue labels 57 + (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 58 + 'help wanted' issues is a great place to start. 59 + 60 + ## Code of Conduct 61 + 62 + This project has adopted the 63 + [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1, 64 + available at 65 + https://www.contributor-covenant.org/version/2/1/code_of_conduct.html. 66 + 67 + ## Licensing 68 + 69 + See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to 70 + confirm the licensing of your contribution.
+388
Cargo.lock
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 4 4 + 5 + [[package]] 6 + name = "aho-corasick" 7 + version = "1.1.3" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 + dependencies = [ 11 + "memchr", 12 + ] 13 + 14 + [[package]] 15 + name = "anstream" 16 + version = "0.6.19" 17 + source = "registry+https://github.com/rust-lang/crates.io-index" 18 + checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" 19 + dependencies = [ 20 + "anstyle", 21 + "anstyle-parse", 22 + "anstyle-query", 23 + "anstyle-wincon", 24 + "colorchoice", 25 + "is_terminal_polyfill", 26 + "utf8parse", 27 + ] 28 + 29 + [[package]] 30 + name = "anstyle" 31 + version = "1.0.11" 32 + source = "registry+https://github.com/rust-lang/crates.io-index" 33 + checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" 34 + 35 + [[package]] 36 + name = "anstyle-parse" 37 + version = "0.2.7" 38 + source = "registry+https://github.com/rust-lang/crates.io-index" 39 + checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 40 + dependencies = [ 41 + "utf8parse", 42 + ] 43 + 44 + [[package]] 45 + name = "anstyle-query" 46 + version = "1.1.3" 47 + source = "registry+https://github.com/rust-lang/crates.io-index" 48 + checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" 49 + dependencies = [ 50 + "windows-sys", 51 + ] 52 + 53 + [[package]] 54 + name = "anstyle-wincon" 55 + version = "3.0.9" 56 + source = "registry+https://github.com/rust-lang/crates.io-index" 57 + checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" 58 + dependencies = [ 59 + "anstyle", 60 + "once_cell_polyfill", 61 + "windows-sys", 62 + ] 63 + 64 + [[package]] 65 + name = "anyhow" 66 + version = "1.0.98" 67 + source = "registry+https://github.com/rust-lang/crates.io-index" 68 + checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 69 + 70 + [[package]] 71 + name = "clap" 72 + version = "4.5.41" 73 + source = "registry+https://github.com/rust-lang/crates.io-index" 74 + checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" 75 + dependencies = [ 76 + "clap_builder", 77 + ] 78 + 79 + [[package]] 80 + name = "clap_builder" 81 + version = "4.5.41" 82 + source = "registry+https://github.com/rust-lang/crates.io-index" 83 + checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" 84 + dependencies = [ 85 + "anstream", 86 + "anstyle", 87 + "clap_lex", 88 + "strsim", 89 + ] 90 + 91 + [[package]] 92 + name = "clap_lex" 93 + version = "0.7.5" 94 + source = "registry+https://github.com/rust-lang/crates.io-index" 95 + checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" 96 + 97 + [[package]] 98 + name = "colorchoice" 99 + version = "1.0.4" 100 + source = "registry+https://github.com/rust-lang/crates.io-index" 101 + checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 102 + 103 + [[package]] 104 + name = "firecracker-prepare" 105 + version = "0.1.0" 106 + dependencies = [ 107 + "anyhow", 108 + "libc", 109 + "owo-colors", 110 + "regex", 111 + ] 112 + 113 + [[package]] 114 + name = "firecracker-process" 115 + version = "0.1.0" 116 + dependencies = [ 117 + "anyhow", 118 + "libc", 119 + "owo-colors", 120 + ] 121 + 122 + [[package]] 123 + name = "firecracker-vm" 124 + version = "0.1.0" 125 + dependencies = [ 126 + "anyhow", 127 + "glob", 128 + "libc", 129 + "num_cpus", 130 + "owo-colors", 131 + "serde_json", 132 + ] 133 + 134 + [[package]] 135 + name = "fireup" 136 + version = "0.1.0" 137 + dependencies = [ 138 + "anyhow", 139 + "clap", 140 + "firecracker-prepare", 141 + "firecracker-process", 142 + "firecracker-vm", 143 + ] 144 + 145 + [[package]] 146 + name = "glob" 147 + version = "0.3.2" 148 + source = "registry+https://github.com/rust-lang/crates.io-index" 149 + checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" 150 + 151 + [[package]] 152 + name = "hermit-abi" 153 + version = "0.5.2" 154 + source = "registry+https://github.com/rust-lang/crates.io-index" 155 + checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" 156 + 157 + [[package]] 158 + name = "is_terminal_polyfill" 159 + version = "1.70.1" 160 + source = "registry+https://github.com/rust-lang/crates.io-index" 161 + checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 162 + 163 + [[package]] 164 + name = "itoa" 165 + version = "1.0.15" 166 + source = "registry+https://github.com/rust-lang/crates.io-index" 167 + checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 168 + 169 + [[package]] 170 + name = "libc" 171 + version = "0.2.174" 172 + source = "registry+https://github.com/rust-lang/crates.io-index" 173 + checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" 174 + 175 + [[package]] 176 + name = "memchr" 177 + version = "2.7.5" 178 + source = "registry+https://github.com/rust-lang/crates.io-index" 179 + checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 180 + 181 + [[package]] 182 + name = "num_cpus" 183 + version = "1.17.0" 184 + source = "registry+https://github.com/rust-lang/crates.io-index" 185 + checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" 186 + dependencies = [ 187 + "hermit-abi", 188 + "libc", 189 + ] 190 + 191 + [[package]] 192 + name = "once_cell_polyfill" 193 + version = "1.70.1" 194 + source = "registry+https://github.com/rust-lang/crates.io-index" 195 + checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 196 + 197 + [[package]] 198 + name = "owo-colors" 199 + version = "4.2.2" 200 + source = "registry+https://github.com/rust-lang/crates.io-index" 201 + checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e" 202 + 203 + [[package]] 204 + name = "proc-macro2" 205 + version = "1.0.95" 206 + source = "registry+https://github.com/rust-lang/crates.io-index" 207 + checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 208 + dependencies = [ 209 + "unicode-ident", 210 + ] 211 + 212 + [[package]] 213 + name = "quote" 214 + version = "1.0.40" 215 + source = "registry+https://github.com/rust-lang/crates.io-index" 216 + checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 217 + dependencies = [ 218 + "proc-macro2", 219 + ] 220 + 221 + [[package]] 222 + name = "regex" 223 + version = "1.11.1" 224 + source = "registry+https://github.com/rust-lang/crates.io-index" 225 + checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 226 + dependencies = [ 227 + "aho-corasick", 228 + "memchr", 229 + "regex-automata", 230 + "regex-syntax", 231 + ] 232 + 233 + [[package]] 234 + name = "regex-automata" 235 + version = "0.4.9" 236 + source = "registry+https://github.com/rust-lang/crates.io-index" 237 + checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 238 + dependencies = [ 239 + "aho-corasick", 240 + "memchr", 241 + "regex-syntax", 242 + ] 243 + 244 + [[package]] 245 + name = "regex-syntax" 246 + version = "0.8.5" 247 + source = "registry+https://github.com/rust-lang/crates.io-index" 248 + checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 249 + 250 + [[package]] 251 + name = "ryu" 252 + version = "1.0.20" 253 + source = "registry+https://github.com/rust-lang/crates.io-index" 254 + checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 255 + 256 + [[package]] 257 + name = "serde" 258 + version = "1.0.219" 259 + source = "registry+https://github.com/rust-lang/crates.io-index" 260 + checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 261 + dependencies = [ 262 + "serde_derive", 263 + ] 264 + 265 + [[package]] 266 + name = "serde_derive" 267 + version = "1.0.219" 268 + source = "registry+https://github.com/rust-lang/crates.io-index" 269 + checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 270 + dependencies = [ 271 + "proc-macro2", 272 + "quote", 273 + "syn", 274 + ] 275 + 276 + [[package]] 277 + name = "serde_json" 278 + version = "1.0.141" 279 + source = "registry+https://github.com/rust-lang/crates.io-index" 280 + checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" 281 + dependencies = [ 282 + "itoa", 283 + "memchr", 284 + "ryu", 285 + "serde", 286 + ] 287 + 288 + [[package]] 289 + name = "strsim" 290 + version = "0.11.1" 291 + source = "registry+https://github.com/rust-lang/crates.io-index" 292 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 293 + 294 + [[package]] 295 + name = "syn" 296 + version = "2.0.104" 297 + source = "registry+https://github.com/rust-lang/crates.io-index" 298 + checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" 299 + dependencies = [ 300 + "proc-macro2", 301 + "quote", 302 + "unicode-ident", 303 + ] 304 + 305 + [[package]] 306 + name = "unicode-ident" 307 + version = "1.0.18" 308 + source = "registry+https://github.com/rust-lang/crates.io-index" 309 + checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 310 + 311 + [[package]] 312 + name = "utf8parse" 313 + version = "0.2.2" 314 + source = "registry+https://github.com/rust-lang/crates.io-index" 315 + checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 316 + 317 + [[package]] 318 + name = "windows-sys" 319 + version = "0.59.0" 320 + source = "registry+https://github.com/rust-lang/crates.io-index" 321 + checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 322 + dependencies = [ 323 + "windows-targets", 324 + ] 325 + 326 + [[package]] 327 + name = "windows-targets" 328 + version = "0.52.6" 329 + source = "registry+https://github.com/rust-lang/crates.io-index" 330 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 331 + dependencies = [ 332 + "windows_aarch64_gnullvm", 333 + "windows_aarch64_msvc", 334 + "windows_i686_gnu", 335 + "windows_i686_gnullvm", 336 + "windows_i686_msvc", 337 + "windows_x86_64_gnu", 338 + "windows_x86_64_gnullvm", 339 + "windows_x86_64_msvc", 340 + ] 341 + 342 + [[package]] 343 + name = "windows_aarch64_gnullvm" 344 + version = "0.52.6" 345 + source = "registry+https://github.com/rust-lang/crates.io-index" 346 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 347 + 348 + [[package]] 349 + name = "windows_aarch64_msvc" 350 + version = "0.52.6" 351 + source = "registry+https://github.com/rust-lang/crates.io-index" 352 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 353 + 354 + [[package]] 355 + name = "windows_i686_gnu" 356 + version = "0.52.6" 357 + source = "registry+https://github.com/rust-lang/crates.io-index" 358 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 359 + 360 + [[package]] 361 + name = "windows_i686_gnullvm" 362 + version = "0.52.6" 363 + source = "registry+https://github.com/rust-lang/crates.io-index" 364 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 365 + 366 + [[package]] 367 + name = "windows_i686_msvc" 368 + version = "0.52.6" 369 + source = "registry+https://github.com/rust-lang/crates.io-index" 370 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 371 + 372 + [[package]] 373 + name = "windows_x86_64_gnu" 374 + version = "0.52.6" 375 + source = "registry+https://github.com/rust-lang/crates.io-index" 376 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 377 + 378 + [[package]] 379 + name = "windows_x86_64_gnullvm" 380 + version = "0.52.6" 381 + source = "registry+https://github.com/rust-lang/crates.io-index" 382 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 383 + 384 + [[package]] 385 + name = "windows_x86_64_msvc" 386 + version = "0.52.6" 387 + source = "registry+https://github.com/rust-lang/crates.io-index" 388 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+12
Cargo.toml
··· 1 + [workspace] 2 + default-members = ["crates/*"] 3 + members = ["crates/*"] 4 + resolver = "2" 5 + 6 + [workspace.package] 7 + authors = ["Tsiry Sandratraina <tsiry.sndr@rocksky.app"] 8 + edition = "2021" 9 + license = "MIT" 10 + repository = "https://github.com/tsirysndr/fireup" 11 + 12 + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+19
LICENSE
··· 1 + Copyright (c) 2025 Tsiry Sandratraina <tsiry.sndr@rocksky.app> 2 + 3 + Permission is hereby granted, free of charge, to any person obtaining a copy 4 + of this software and associated documentation files (the "Software"), to deal 5 + in the Software without restriction, including without limitation the rights 6 + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 + copies of the Software, and to permit persons to whom the Software is 8 + furnished to do so, subject to the following conditions: 9 + 10 + The above copyright notice and this permission notice shall be included in all 11 + copies or substantial portions of the Software. 12 + 13 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 + SOFTWARE.
+5
README.md
··· 1 + # Fireup 2 + 3 + `fireup` is a tool designed to simplify the process of setting up and managing Firecracker microVMs. It automates the preparation of the necessary files, including kernel images, root filesystems, and SSH keys, to quickly get you started with [Firecracker](https://firecracker-microvm.github.io/). 4 + 5 + ![Fireup Preview](./preview.png)
+13
crates/firecracker-prepare/Cargo.toml
··· 1 + [package] 2 + name = "firecracker-prepare" 3 + version = "0.1.0" 4 + authors.workspace = true 5 + edition.workspace = true 6 + license.workspace = true 7 + repository.workspace = true 8 + 9 + [dependencies] 10 + anyhow = "1.0.98" 11 + libc = "0.2.174" 12 + owo-colors = "4.2.2" 13 + regex = "1.11.1"
+87
crates/firecracker-prepare/src/command.rs
··· 1 + use anyhow::{anyhow, Context, Result}; 2 + use std::process::{Command, Output, Stdio}; 3 + 4 + pub fn has_sudo() -> bool { 5 + Command::new("sudo") 6 + .arg("-h") 7 + .output() 8 + .map(|output| output.status.success()) 9 + .unwrap_or(false) 10 + } 11 + 12 + pub fn is_root() -> bool { 13 + unsafe { libc::getuid() == 0 } 14 + } 15 + 16 + pub fn run_command(command: &str, args: &[&str], use_sudo: bool) -> Result<Output> { 17 + let mut cmd = if use_sudo { 18 + if !has_sudo() && !is_root() { 19 + return Err(anyhow!( 20 + "sudo is required for command '{}', but not available", 21 + command 22 + )); 23 + } 24 + let mut c = Command::new("sudo"); 25 + c.arg(command); 26 + 27 + match is_root() { 28 + true => Command::new(command), 29 + false => c, 30 + } 31 + } else { 32 + Command::new(command) 33 + }; 34 + 35 + let output = cmd 36 + .args(args) 37 + .stdin(Stdio::inherit()) 38 + .stderr(Stdio::piped()) 39 + .output() 40 + .with_context(|| format!("Failed to execute {}", command))?; 41 + 42 + if !output.status.success() { 43 + let stderr = String::from_utf8_lossy(&output.stderr); 44 + return Err(anyhow!("Command {} failed: {}", command, stderr)); 45 + } 46 + Ok(output) 47 + } 48 + 49 + pub fn run_command_with_stdout_inherit(command: &str, args: &[&str], use_sudo: bool) -> Result<()> { 50 + let mut cmd = if use_sudo { 51 + if !has_sudo() && !is_root() { 52 + return Err(anyhow!( 53 + "sudo is required for command '{}', but not available", 54 + command 55 + )); 56 + } 57 + let mut c = Command::new("sudo"); 58 + c.arg(command); 59 + 60 + match is_root() { 61 + true => Command::new(command), 62 + false => c, 63 + } 64 + } else { 65 + Command::new(command) 66 + }; 67 + 68 + let mut child = cmd 69 + .args(args) 70 + .stdin(Stdio::inherit()) 71 + .stderr(Stdio::inherit()) 72 + .stdout(Stdio::inherit()) 73 + .spawn() 74 + .with_context(|| format!("Failed to execute {}", command))?; 75 + 76 + let status = child.wait()?; 77 + 78 + if !status.success() { 79 + return Err(anyhow!( 80 + "Command {} failed with status: {}", 81 + command, 82 + status 83 + )); 84 + } 85 + 86 + Ok(()) 87 + }
+156
crates/firecracker-prepare/src/downloader.rs
··· 1 + use anyhow::{anyhow, Context, Result}; 2 + use owo_colors::OwoColorize; 3 + use regex::Regex; 4 + use std::fs; 5 + use std::path::Path; 6 + 7 + use crate::command::{run_command, run_command_with_stdout_inherit}; 8 + 9 + pub fn download_files(arch: &str) -> Result<(String, String, String)> { 10 + // Get latest version 11 + let output = run_command( 12 + "curl", 13 + &[ 14 + "-fsSLI", 15 + "-o", 16 + "/dev/null", 17 + "-w", 18 + "%{url_effective}", 19 + "https://github.com/firecracker-microvm/firecracker/releases/latest", 20 + ], 21 + false, 22 + )?; 23 + let url = String::from_utf8_lossy(&output.stdout); 24 + let version = url 25 + .split('/') 26 + .last() 27 + .ok_or_else(|| anyhow!("Failed to parse version from URL"))? 28 + .trim(); 29 + let ci_version = version 30 + .rsplitn(2, '.') 31 + .last() 32 + .ok_or_else(|| anyhow!("Failed to parse CI version"))?; 33 + 34 + // Fetch and download kernel 35 + let kernel_prefix = format!("firecracker-ci/{}/{}/vmlinux-", ci_version, arch); 36 + let latest_kernel_key = 37 + get_latest_key("http://spec.ccfc.min.s3.amazonaws.com/", &kernel_prefix)?; 38 + let kernel_file = Path::new(&latest_kernel_key) 39 + .file_name() 40 + .ok_or_else(|| anyhow!("Failed to get kernel filename"))? 41 + .to_string_lossy() 42 + .to_string(); 43 + 44 + let kernel_abs = fs::canonicalize(&kernel_file) 45 + .unwrap_or_else(|_| Path::new(&kernel_file).to_path_buf()) 46 + .display() 47 + .to_string(); 48 + download_file( 49 + &format!( 50 + "https://s3.amazonaws.com/spec.ccfc.min/{}", 51 + latest_kernel_key 52 + ), 53 + &kernel_abs, 54 + )?; 55 + 56 + let ubuntu_prefix = format!("firecracker-ci/{}/{}/ubuntu-", ci_version, arch); 57 + let latest_ubuntu_key = 58 + get_latest_key("http://spec.ccfc.min.s3.amazonaws.com/", &ubuntu_prefix)?; 59 + let ubuntu_version = Path::new(&latest_ubuntu_key) 60 + .file_name() 61 + .ok_or_else(|| anyhow!("Failed to get ubuntu filename"))? 62 + .to_string_lossy() 63 + .to_string(); 64 + let re_version = Regex::new(r"^\d+\.\d+$").with_context(|| "Failed to create version regex")?; 65 + let ubuntu_version = re_version 66 + .find(&ubuntu_version) 67 + .map(|m| m.as_str().to_string()) 68 + .unwrap_or_else(|| { 69 + Path::new(&latest_ubuntu_key) 70 + .file_name() 71 + .unwrap() 72 + .to_string_lossy() 73 + .strip_prefix("ubuntu-") 74 + .unwrap() 75 + .strip_suffix(".squashfs") 76 + .unwrap() 77 + .to_string() 78 + }); 79 + let ubuntu_file = format!("ubuntu-{}.squashfs.upstream", ubuntu_version); 80 + 81 + let ubuntu_abs = fs::canonicalize(&ubuntu_file) 82 + .unwrap_or_else(|_| Path::new(&ubuntu_file).to_path_buf()) 83 + .display() 84 + .to_string(); 85 + download_file( 86 + &format!( 87 + "https://s3.amazonaws.com/spec.ccfc.min/{}", 88 + latest_ubuntu_key 89 + ), 90 + &ubuntu_abs, 91 + )?; 92 + 93 + Ok((kernel_abs, ubuntu_abs, ubuntu_version)) 94 + } 95 + 96 + fn get_latest_key(url: &str, prefix: &str) -> Result<String> { 97 + let output = run_command( 98 + "curl", 99 + &["-s", &format!("{}?prefix={}&list-type=2", url, prefix)], 100 + false, 101 + )?; 102 + let xml_str = String::from_utf8_lossy(&output.stdout); 103 + // Match vmlinux-X.Y.Z or ubuntu-X.Y.squashfs 104 + let re = Regex::new(r"<Key>(firecracker-ci/[^<]+/[^<]+/(?:vmlinux-\d+\.\d+\.\d{1,3}|ubuntu-\d+\.\d+\.squashfs))</Key>") 105 + .with_context(|| "Failed to create regex")?; 106 + 107 + let mut keys: Vec<String> = re 108 + .captures_iter(&xml_str) 109 + .filter_map(|cap| cap.get(1).map(|m| m.as_str().to_string())) 110 + .collect(); 111 + 112 + if keys.is_empty() { 113 + return Err(anyhow!("No matching keys found for prefix: {}", prefix)); 114 + } 115 + 116 + // Sort by version number using version sorting similar to sort -V 117 + keys.sort_by(|a, b| { 118 + let a_version = Path::new(a) 119 + .file_name() 120 + .and_then(|f| f.to_str()) 121 + .and_then(|f| f.split('-').last()) 122 + .and_then(|v| v.strip_suffix(".squashfs")) 123 + .unwrap_or("") 124 + .split('.') 125 + .map(|n| n.parse::<u32>().unwrap_or(0)) 126 + .collect::<Vec<u32>>(); 127 + let b_version = Path::new(b) 128 + .file_name() 129 + .and_then(|f| f.to_str()) 130 + .and_then(|f| f.split('-').last()) 131 + .and_then(|v| v.strip_suffix(".squashfs")) 132 + .unwrap_or("") 133 + .split('.') 134 + .map(|n| n.parse::<u32>().unwrap_or(0)) 135 + .collect::<Vec<u32>>(); 136 + 137 + a_version.cmp(&b_version) 138 + }); 139 + 140 + keys.last() 141 + .ok_or_else(|| anyhow!("No matching keys found after sorting")) 142 + .cloned() 143 + } 144 + 145 + fn download_file(url: &str, output: &str) -> Result<()> { 146 + if Path::new(output).exists() { 147 + println!( 148 + "File already exists: {}, skipping download.", 149 + output.bright_green() 150 + ); 151 + return Ok(()); 152 + } 153 + println!("Downloading: {}", output.bright_green()); 154 + run_command_with_stdout_inherit("wget", &["-O", output, url], false)?; 155 + Ok(()) 156 + }
+48
crates/firecracker-prepare/src/lib.rs
··· 1 + use anyhow::{Context, Result}; 2 + use owo_colors::OwoColorize; 3 + use std::fs; 4 + 5 + pub mod command; 6 + pub mod downloader; 7 + pub mod rootfs; 8 + pub mod ssh; 9 + 10 + pub fn prepare() -> Result<()> { 11 + let arch = command::run_command("uname", &["-m"], false)?.stdout; 12 + let arch = String::from_utf8_lossy(&arch).trim().to_string(); 13 + 14 + println!("[+] Detected architecture: {}", arch.bright_green()); 15 + 16 + let (kernel_file, ubuntu_file, ubuntu_version) = downloader::download_files(&arch)?; 17 + let squashfs_root_dir = "squashfs-root"; 18 + 19 + rootfs::extract_squashfs(&ubuntu_file, squashfs_root_dir)?; 20 + 21 + let ssh_key_name = format!("ubuntu-{}.id_rsa", ubuntu_version); 22 + ssh::generate_and_copy_ssh_key(&ssh_key_name, squashfs_root_dir)?; 23 + 24 + let ext4_file = format!("ubuntu-{}.ext4", ubuntu_version); 25 + 26 + rootfs::create_ext4_filesystem(squashfs_root_dir, &ext4_file)?; 27 + 28 + let kernel_abs = fs::canonicalize(&kernel_file).with_context(|| { 29 + format!( 30 + "Failed to resolve absolute path for kernel: {}", 31 + kernel_file 32 + ) 33 + })?; 34 + let ext4_abs = fs::canonicalize(&ext4_file) 35 + .with_context(|| format!("Failed to resolve absolute path for rootfs: {}", ext4_file))?; 36 + let ssh_key_abs = fs::canonicalize(&ssh_key_name).with_context(|| { 37 + format!( 38 + "Failed to resolve absolute path for SSH key: {}", 39 + ssh_key_name 40 + ) 41 + })?; 42 + 43 + println!("[✓] Kernel: {}", kernel_abs.display().bright_green()); 44 + println!("[✓] Rootfs: {}", ext4_abs.display().bright_green()); 45 + println!("[✓] SSH Key: {}", ssh_key_abs.display().bright_green()); 46 + 47 + Ok(()) 48 + }
+17
crates/firecracker-prepare/src/rootfs.rs
··· 1 + use anyhow::Result; 2 + 3 + use crate::command::run_command; 4 + 5 + pub fn extract_squashfs(squashfs_file: &str, output_dir: &str) -> Result<()> { 6 + run_command("rm", &["-rf", output_dir], true)?; 7 + println!("Extracting rootfs..."); 8 + run_command("unsquashfs", &[squashfs_file], false)?; 9 + Ok(()) 10 + } 11 + 12 + pub fn create_ext4_filesystem(squashfs_dir: &str, output_file: &str) -> Result<()> { 13 + run_command("chown", &["-R", "root:root", squashfs_dir], true)?; 14 + run_command("truncate", &["-s", "400M", output_file], false)?; 15 + run_command("mkfs.ext4", &["-d", squashfs_dir, "-F", output_file], true)?; 16 + Ok(()) 17 + }
+23
crates/firecracker-prepare/src/ssh.rs
··· 1 + use anyhow::Result; 2 + 3 + use crate::command::{run_command, run_command_with_stdout_inherit}; 4 + 5 + pub fn generate_and_copy_ssh_key(key_name: &str, squashfs_root_dir: &str) -> Result<()> { 6 + if std::path::Path::new(key_name).exists() { 7 + println!( 8 + "[!] Warning: {} already exists, skipping key generation.", 9 + key_name 10 + ); 11 + let pub_key_path = format!("{}.pub", key_name); 12 + let auth_keys_path = format!("{}/root/.ssh/authorized_keys", squashfs_root_dir); 13 + run_command("cp", &[&pub_key_path, &auth_keys_path], true)?; 14 + return Ok(()); 15 + } 16 + 17 + run_command_with_stdout_inherit("ssh-keygen", &["-f", key_name, "-N", ""], false)?; 18 + 19 + let pub_key_path = format!("{}.pub", key_name); 20 + let auth_keys_path = format!("{}/root/.ssh/authorized_keys", squashfs_root_dir); 21 + run_command("cp", &[&pub_key_path, &auth_keys_path], true)?; 22 + Ok(()) 23 + }
+12
crates/firecracker-process/Cargo.toml
··· 1 + [package] 2 + name = "firecracker-process" 3 + version = "0.1.0" 4 + authors.workspace = true 5 + edition.workspace = true 6 + license.workspace = true 7 + repository.workspace = true 8 + 9 + [dependencies] 10 + anyhow = "1.0.98" 11 + libc = "0.2.174" 12 + owo-colors = "4.2.2"
+90
crates/firecracker-process/src/command.rs
··· 1 + use anyhow::{anyhow, Context, Error, Result}; 2 + use std::{ 3 + process::{Command, Output, Stdio}, 4 + thread, 5 + }; 6 + 7 + pub fn has_sudo() -> bool { 8 + Command::new("sudo") 9 + .arg("-h") 10 + .output() 11 + .map(|output| output.status.success()) 12 + .unwrap_or(false) 13 + } 14 + 15 + pub fn is_root() -> bool { 16 + unsafe { libc::getuid() == 0 } 17 + } 18 + 19 + pub fn run_command(command: &str, args: &[&str], use_sudo: bool) -> Result<Output> { 20 + let mut cmd = if use_sudo { 21 + if !has_sudo() && !is_root() { 22 + return Err(anyhow!( 23 + "sudo is required for command '{}', but not available", 24 + command 25 + )); 26 + } 27 + let mut c = Command::new("sudo"); 28 + c.arg(command); 29 + 30 + match is_root() { 31 + true => Command::new(command), 32 + false => c, 33 + } 34 + } else { 35 + Command::new(command) 36 + }; 37 + 38 + let output = cmd 39 + .args(args) 40 + .stdin(Stdio::inherit()) 41 + .stderr(Stdio::piped()) 42 + .output() 43 + .with_context(|| format!("Failed to execute {}", command))?; 44 + 45 + if !output.status.success() { 46 + let stderr = String::from_utf8_lossy(&output.stderr); 47 + return Err(anyhow!("Command {} failed: {}", command, stderr)); 48 + } 49 + Ok(output) 50 + } 51 + 52 + pub fn run_command_in_background(command: &str, args: &[&str], use_sudo: bool) -> Result<()> { 53 + let mut cmd = if use_sudo { 54 + if !has_sudo() && !is_root() { 55 + return Err(anyhow!( 56 + "sudo is required for command '{}', but not available", 57 + command 58 + )); 59 + } 60 + let mut c = Command::new("sudo"); 61 + c.arg(command); 62 + 63 + match is_root() { 64 + true => Command::new(command), 65 + false => c, 66 + } 67 + } else { 68 + Command::new(command) 69 + }; 70 + 71 + let command_owned = command.to_string(); 72 + let args_owned: Vec<String> = args.iter().map(|s| s.to_string()).collect(); 73 + 74 + thread::spawn(move || { 75 + let mut child = cmd 76 + .args(&args_owned) 77 + .stdin(Stdio::null()) 78 + .stdout(Stdio::null()) 79 + .stderr(Stdio::null()) 80 + .spawn() 81 + .with_context(|| format!("Failed to execute {}", command_owned))?; 82 + 83 + child 84 + .wait() 85 + .with_context(|| format!("Failed to wait for command {}", command_owned))?; 86 + Ok::<(), Error>(()) 87 + }); 88 + 89 + Ok(()) 90 + }
+30
crates/firecracker-process/src/lib.rs
··· 1 + use anyhow::Result; 2 + 3 + use crate::command::{run_command, run_command_in_background}; 4 + 5 + pub mod command; 6 + 7 + pub const FIRECRACKER_SOCKET: &str = "/tmp/firecracker.sock"; 8 + 9 + pub fn start() -> Result<()> { 10 + stop()?; 11 + println!("[+] Starting Firecracker..."); 12 + run_command_in_background("firecracker", &["--api-sock", FIRECRACKER_SOCKET], true)?; 13 + Ok(()) 14 + } 15 + 16 + pub fn stop() -> Result<()> { 17 + if !is_running() { 18 + println!("[!] Firecracker is not running."); 19 + run_command("rm", &["-rf", FIRECRACKER_SOCKET], true)?; 20 + return Ok(()); 21 + } 22 + run_command("killall", &["-s", "KILL", "firecracker"], true)?; 23 + run_command("rm", &["-rf", FIRECRACKER_SOCKET], true)?; 24 + println!("[+] Firecracker stopped."); 25 + Ok(()) 26 + } 27 + 28 + pub fn is_running() -> bool { 29 + run_command("pgrep", &["firecracker"], false).is_ok() 30 + }
+14
crates/firecracker-up/Cargo.toml
··· 1 + [package] 2 + name = "fireup" 3 + version = "0.1.0" 4 + authors.workspace = true 5 + edition.workspace = true 6 + license.workspace = true 7 + repository.workspace = true 8 + 9 + [dependencies] 10 + anyhow = "1.0.98" 11 + firecracker-vm = { path = "../firecracker-vm" } 12 + firecracker-prepare = { path = "../firecracker-prepare" } 13 + firecracker-process = { path = "../firecracker-process" } 14 + clap = "4.5.41"
+19
crates/firecracker-up/src/main.rs
··· 1 + use std::thread; 2 + 3 + use anyhow::Result; 4 + 5 + fn main() -> Result<()> { 6 + firecracker_process::start()?; 7 + 8 + loop { 9 + thread::sleep(std::time::Duration::from_secs(1)); 10 + if firecracker_process::is_running() { 11 + println!("[+] Firecracker is running."); 12 + break; 13 + } 14 + } 15 + 16 + firecracker_prepare::prepare()?; 17 + firecracker_vm::setup()?; 18 + Ok(()) 19 + }
+15
crates/firecracker-vm/Cargo.toml
··· 1 + [package] 2 + name = "firecracker-vm" 3 + version = "0.1.0" 4 + authors.workspace = true 5 + edition.workspace = true 6 + license.workspace = true 7 + repository.workspace = true 8 + 9 + [dependencies] 10 + anyhow = "1.0.98" 11 + glob = "0.3.2" 12 + libc = "0.2.174" 13 + num_cpus = "1.17.0" 14 + owo-colors = "4.2.2" 15 + serde_json = "1.0.141"
+58
crates/firecracker-vm/src/command.rs
··· 1 + use anyhow::{anyhow, Context, Result}; 2 + use std::process::{Command, Output, Stdio}; 3 + 4 + pub fn has_sudo() -> bool { 5 + Command::new("sudo") 6 + .arg("-h") 7 + .output() 8 + .map(|output| output.status.success()) 9 + .unwrap_or(false) 10 + } 11 + 12 + pub fn is_root() -> bool { 13 + unsafe { libc::getuid() == 0 } 14 + } 15 + 16 + pub fn run_command(command: &str, args: &[&str], use_sudo: bool) -> Result<Output> { 17 + let mut cmd = if use_sudo { 18 + if !has_sudo() && !is_root() { 19 + return Err(anyhow!( 20 + "sudo is required for command '{}', but not available", 21 + command 22 + )); 23 + } 24 + let mut c = Command::new("sudo"); 25 + c.arg(command); 26 + 27 + match is_root() { 28 + true => Command::new(command), 29 + false => c, 30 + } 31 + } else { 32 + Command::new(command) 33 + }; 34 + 35 + let output = cmd 36 + .args(args) 37 + .stdin(Stdio::inherit()) 38 + .stderr(Stdio::piped()) 39 + .output() 40 + .with_context(|| format!("Failed to execute {}", command))?; 41 + 42 + if !output.status.success() { 43 + let stderr = String::from_utf8_lossy(&output.stderr); 44 + let stdout = String::from_utf8_lossy(&output.stdout); 45 + return Err(anyhow!( 46 + "Command {} failed: {} {} {} {}", 47 + command, 48 + stderr, 49 + stdout, 50 + args.iter() 51 + .map(|s| s.to_string()) 52 + .collect::<Vec<_>>() 53 + .join(" "), 54 + output.status.code().unwrap_or(-1), 55 + )); 56 + } 57 + Ok(output) 58 + }
+6
crates/firecracker-vm/src/constants.rs
··· 1 + pub const TAP_DEV: &str = "tap0"; 2 + pub const API_SOCKET: &str = "/tmp/firecracker.sock"; 3 + pub const GUEST_IP: &str = "172.16.0.2"; 4 + pub const TAP_IP: &str = "172.16.0.1"; 5 + pub const FC_MAC: &str = "06:00:AC:10:00:02"; 6 + pub const MASK_SHORT: &str = "/30";
+170
crates/firecracker-vm/src/firecracker.rs
··· 1 + use crate::constants::{API_SOCKET, FC_MAC, TAP_DEV}; 2 + use anyhow::Result; 3 + use serde_json::json; 4 + use std::thread::sleep; 5 + use std::time::Duration; 6 + 7 + use crate::command::run_command; 8 + 9 + pub fn configure(logfile: &str, kernel: &str, rootfs: &str, arch: &str) -> Result<()> { 10 + configure_logger(logfile)?; 11 + setup_boot_source(kernel, arch)?; 12 + setup_rootfs(rootfs)?; 13 + setup_network_interface()?; 14 + setup_vcpu_and_memory(num_cpus::get(), 512)?; 15 + 16 + // Wait before starting instance 17 + sleep(Duration::from_millis(15)); 18 + 19 + start_microvm()?; 20 + 21 + // Wait for VM to boot 22 + sleep(Duration::from_secs(2)); 23 + Ok(()) 24 + } 25 + 26 + fn configure_logger(logfile: &str) -> Result<()> { 27 + println!("[+] Configuring logger..."); 28 + let payload = json!({ 29 + "log_path": logfile, 30 + "level": "Debug", 31 + "show_level": true, 32 + "show_log_origin": true 33 + }); 34 + run_command( 35 + "curl", 36 + &[ 37 + "-s", 38 + "-X", 39 + "PUT", 40 + "--unix-socket", 41 + API_SOCKET, 42 + "--data", 43 + &payload.to_string(), 44 + "http://localhost/logger", 45 + ], 46 + true, 47 + )?; 48 + Ok(()) 49 + } 50 + 51 + fn setup_boot_source(kernel: &str, arch: &str) -> Result<()> { 52 + println!("[+] Setting boot source..."); 53 + let mut boot_args = "console=ttyS0 reboot=k panic=1 pci=off".to_string(); 54 + if arch == "aarch64" { 55 + boot_args = format!("keep_bootcon {}", boot_args); 56 + } 57 + let payload = json!({ 58 + "kernel_image_path": kernel, 59 + "boot_args": boot_args 60 + }); 61 + run_command( 62 + "curl", 63 + &[ 64 + "-s", 65 + "-X", 66 + "PUT", 67 + "--unix-socket", 68 + API_SOCKET, 69 + "--data", 70 + &payload.to_string(), 71 + "http://localhost/boot-source", 72 + ], 73 + true, 74 + )?; 75 + Ok(()) 76 + } 77 + 78 + fn setup_rootfs(rootfs: &str) -> Result<()> { 79 + println!("[+] Setting rootfs..."); 80 + let payload = json!({ 81 + "drive_id": "rootfs", 82 + "path_on_host": rootfs, 83 + "is_root_device": true, 84 + "is_read_only": false 85 + }); 86 + run_command( 87 + "curl", 88 + &[ 89 + "-s", 90 + "-X", 91 + "PUT", 92 + "--unix-socket", 93 + API_SOCKET, 94 + "--data", 95 + &payload.to_string(), 96 + "http://localhost/drives/rootfs", 97 + ], 98 + true, 99 + )?; 100 + Ok(()) 101 + } 102 + 103 + fn setup_network_interface() -> Result<()> { 104 + println!("[+] Setting network interface..."); 105 + let payload = json!({ 106 + "iface_id": "net1", 107 + "guest_mac": FC_MAC, 108 + "host_dev_name": TAP_DEV 109 + }); 110 + run_command( 111 + "curl", 112 + &[ 113 + "-s", 114 + "-X", 115 + "PUT", 116 + "--unix-socket", 117 + API_SOCKET, 118 + "--data", 119 + &payload.to_string(), 120 + "http://localhost/network-interfaces/net1", 121 + ], 122 + true, 123 + )?; 124 + Ok(()) 125 + } 126 + 127 + fn start_microvm() -> Result<()> { 128 + println!("[+] Starting microVM..."); 129 + let payload = json!({ 130 + "action_type": "InstanceStart" 131 + }); 132 + run_command( 133 + "curl", 134 + &[ 135 + "-s", 136 + "-X", 137 + "PUT", 138 + "--unix-socket", 139 + API_SOCKET, 140 + "--data", 141 + &payload.to_string(), 142 + "http://localhost/actions", 143 + ], 144 + true, 145 + )?; 146 + Ok(()) 147 + } 148 + 149 + fn setup_vcpu_and_memory(n: usize, memory: usize) -> Result<()> { 150 + println!("[+] Setting vCPU and memory..."); 151 + let payload = json!({ 152 + "vcpu_count": n, 153 + "mem_size_mib": memory 154 + }); 155 + run_command( 156 + "curl", 157 + &[ 158 + "-s", 159 + "-X", 160 + "PUT", 161 + "--unix-socket", 162 + API_SOCKET, 163 + "--data", 164 + &payload.to_string(), 165 + "http://localhost/machine-config", 166 + ], 167 + true, 168 + )?; 169 + Ok(()) 170 + }
+32
crates/firecracker-vm/src/guest.rs
··· 1 + use crate::command::run_command; 2 + use crate::constants::GUEST_IP; 3 + use anyhow::Result; 4 + 5 + pub fn configure_guest_network(key_name: &str) -> Result<()> { 6 + println!("[+] Configuring network in guest..."); 7 + run_command( 8 + "ssh", 9 + &[ 10 + "-i", 11 + key_name, 12 + "-o", 13 + "StrictHostKeyChecking=no", 14 + &format!("root@{}", GUEST_IP), 15 + "ip route add default via 172.16.0.1 dev eth0", 16 + ], 17 + false, 18 + )?; 19 + run_command( 20 + "ssh", 21 + &[ 22 + "-i", 23 + key_name, 24 + "-o", 25 + "StrictHostKeyChecking=no", 26 + &format!("root@{}", GUEST_IP), 27 + "echo 'nameserver 8.8.8.8' > /etc/resolv.conf", 28 + ], 29 + false, 30 + )?; 31 + Ok(()) 32 + }
+83
crates/firecracker-vm/src/lib.rs
··· 1 + use anyhow::{anyhow, Context, Result}; 2 + use owo_colors::OwoColorize; 3 + use std::fs; 4 + 5 + use crate::constants::GUEST_IP; 6 + 7 + mod command; 8 + mod constants; 9 + mod firecracker; 10 + mod guest; 11 + mod network; 12 + 13 + pub fn setup() -> Result<()> { 14 + let logfile = format!("{}/firecracker.log", std::env::current_dir()?.display()); 15 + fs::File::create(&logfile) 16 + .with_context(|| format!("Failed to create log file: {}", logfile))?; 17 + 18 + let kernel = glob::glob("vmlinux*") 19 + .with_context(|| "Failed to glob kernel files")? 20 + .last() 21 + .ok_or_else(|| anyhow!("No kernel file found"))? 22 + .with_context(|| "Failed to get kernel path")?; 23 + let kernel = fs::canonicalize(&kernel) 24 + .with_context(|| { 25 + format!( 26 + "Failed to resolve absolute path for kernel: {}", 27 + kernel.display() 28 + ) 29 + })? 30 + .display() 31 + .to_string(); 32 + 33 + let rootfs = glob::glob("*.ext4") 34 + .with_context(|| "Failed to glob rootfs files")? 35 + .last() 36 + .ok_or_else(|| anyhow!("No rootfs file found"))? 37 + .with_context(|| "Failed to get rootfs path")?; 38 + let rootfs = fs::canonicalize(&rootfs) 39 + .with_context(|| { 40 + format!( 41 + "Failed to resolve absolute path for rootfs: {}", 42 + rootfs.display() 43 + ) 44 + })? 45 + .display() 46 + .to_string(); 47 + 48 + let key_name = glob::glob("*.id_rsa") 49 + .with_context(|| "Failed to glob ssh key files")? 50 + .last() 51 + .ok_or_else(|| anyhow!("No SSH key file found"))? 52 + .with_context(|| "Failed to get SSH key path")?; 53 + let key_name = fs::canonicalize(&key_name) 54 + .with_context(|| { 55 + format!( 56 + "Failed to resolve absolute path for SSH key: {}", 57 + key_name.display() 58 + ) 59 + })? 60 + .display() 61 + .to_string(); 62 + let arch = command::run_command("uname", &["-m"], false)?.stdout; 63 + let arch = String::from_utf8_lossy(&arch).trim().to_string(); 64 + network::setup_network()?; 65 + firecracker::configure(&logfile, &kernel, &rootfs, &arch)?; 66 + guest::configure_guest_network(&key_name)?; 67 + 68 + println!("[✓] MicroVM booted and network is configured."); 69 + 70 + let key_name = key_name 71 + .rsplit('/') 72 + .next() 73 + .ok_or_else(|| anyhow!("Failed to extract key name from path"))? 74 + .to_string(); 75 + 76 + println!("SSH into the VM using:"); 77 + println!( 78 + "{}", 79 + format!("ssh -i ./{} root@{}", key_name, GUEST_IP).bright_green() 80 + ); 81 + 82 + Ok(()) 83 + }
+89
crates/firecracker-vm/src/network.rs
··· 1 + use crate::constants::{MASK_SHORT, TAP_DEV, TAP_IP}; 2 + use anyhow::{anyhow, Context, Result}; 3 + use serde_json::Value; 4 + 5 + use crate::command::run_command; 6 + 7 + fn check_tap_exists() -> bool { 8 + run_command("ip", &["link", "show", TAP_DEV], false) 9 + .map(|output| output.status.success()) 10 + .unwrap_or(false) 11 + } 12 + 13 + pub fn setup_network() -> Result<()> { 14 + if check_tap_exists() { 15 + println!("[✓] Network already configured. Skipping setup."); 16 + return Ok(()); 17 + } 18 + 19 + println!("[+] Configuring {}...", TAP_DEV); 20 + run_command( 21 + "ip", 22 + &["tuntap", "add", "dev", TAP_DEV, "mode", "tap"], 23 + true, 24 + )?; 25 + run_command( 26 + "ip", 27 + &[ 28 + "addr", 29 + "add", 30 + &format!("{}{}", TAP_IP, MASK_SHORT), 31 + "dev", 32 + TAP_DEV, 33 + ], 34 + true, 35 + )?; 36 + run_command("ip", &["link", "set", "dev", TAP_DEV, "up"], true)?; 37 + 38 + let ip_forward = run_command("cat", &["/proc/sys/net/ipv4/ip_forward"], false)?.stdout; 39 + if String::from_utf8_lossy(&ip_forward).trim() != "1" { 40 + println!("[+] Enabling IP forwarding..."); 41 + run_command("sysctl", &["-w", "net.ipv4.ip_forward=1"], true)?; 42 + } 43 + 44 + let output = run_command("ip", &["-j", "route", "list", "default"], false)?; 45 + let json: Value = 46 + serde_json::from_slice(&output.stdout).with_context(|| "Failed to parse route JSON")?; 47 + let host_iface = json[0]["dev"] 48 + .as_str() 49 + .ok_or_else(|| anyhow!("Failed to get host interface"))?; 50 + 51 + println!("[+] Setting up NAT on {}...", host_iface); 52 + 53 + let rule_exists = run_command( 54 + "iptables", 55 + &[ 56 + "-t", 57 + "nat", 58 + "-C", 59 + "POSTROUTING", 60 + "-o", 61 + host_iface, 62 + "-j", 63 + "MASQUERADE", 64 + ], 65 + true, 66 + ) 67 + .map(|output| output.status.success()) 68 + .unwrap_or(false); 69 + 70 + if !rule_exists { 71 + run_command( 72 + "iptables", 73 + &[ 74 + "-t", 75 + "nat", 76 + "-A", 77 + "POSTROUTING", 78 + "-o", 79 + host_iface, 80 + "-j", 81 + "MASQUERADE", 82 + ], 83 + true, 84 + )?; 85 + } 86 + 87 + run_command("iptables", &["-P", "FORWARD", "ACCEPT"], true)?; 88 + Ok(()) 89 + }
+106
install.sh
··· 1 + #!/usr/bin/env bash 2 + 3 + set -e -o pipefail 4 + 5 + readonly MAGENTA="$(tput setaf 5 2>/dev/null || echo '')" 6 + readonly GREEN="$(tput setaf 2 2>/dev/null || echo '')" 7 + readonly CYAN="$(tput setaf 6 2>/dev/null || echo '')" 8 + readonly ORANGE="$(tput setaf 3 2>/dev/null || echo '')" 9 + readonly NO_COLOR="$(tput sgr0 2>/dev/null || echo '')" 10 + 11 + if ! command -v curl >/dev/null 2>&1; then 12 + echo "Error: curl is required to install fireup." 13 + exit 1 14 + fi 15 + 16 + if ! command -v tar >/dev/null 2>&1; then 17 + echo "Error: tar is required to install fireup." 18 + exit 1 19 + fi 20 + 21 + export PATH="$HOME/.local/bin:$PATH" 22 + 23 + RELEASE_URL="https://api.github.com/repos/firecracker-microvm/firecracker/releases/latest" 24 + 25 + function detect_os() { 26 + # Determine the operating system 27 + OS=$(uname -s) 28 + if [ "$OS" = "Linux" ]; then 29 + # Determine the CPU architecture 30 + ARCH=$(uname -m) 31 + if [ "$ARCH" = "aarch64" ]; then 32 + ASSET_NAME="-aarch64.tgz" 33 + elif [ "$ARCH" = "x86_64" ]; then 34 + ASSET_NAME="-x86_64.tgz" 35 + else 36 + echo "Unsupported architecture: $ARCH" 37 + exit 1 38 + fi 39 + else 40 + echo "Unsupported operating system: $OS" 41 + echo "This script only supports Linux." 42 + exit 1 43 + fi; 44 + } 45 + 46 + detect_os 47 + 48 + DOWNLOAD_URL=$(curl -sSL "$RELEASE_URL" | grep -o "browser_download_url.*firecracker-.*$ASSET_NAME\"" | cut -d ' ' -f 2) 49 + 50 + DOWNLOAD_URL=`echo $DOWNLOAD_URL | tr -d '\"'` 51 + 52 + ASSET_NAME=$(basename $DOWNLOAD_URL) 53 + 54 + curl -SL $DOWNLOAD_URL -o /tmp/$ASSET_NAME 55 + 56 + tar -xzf /tmp/$ASSET_NAME -C /tmp 57 + 58 + mkdir -p "$HOME/.firecracker" 59 + 60 + VERSION=$(echo $ASSET_NAME | grep -oP 'v\d+\.\d+\.\d+') 61 + 62 + ARCH=$(uname -m) 63 + 64 + cp -r /tmp/release-${VERSION}-${ARCH} $HOME/.firecracker 65 + 66 + rm -rf /tmp/release-${VERSION}-${ARCH} 67 + 68 + rm $HOME/.firecracker/release-${VERSION}-${ARCH}/firecracker \ 69 + $HOME/.firecracker/release-${VERSION}-${ARCH}/cpu-template-helper \ 70 + $HOME/.firecracker/release-${VERSION}-${ARCH}/jailer \ 71 + $HOME/.firecracker/release-${VERSION}-${ARCH}/rebase-snap \ 72 + $HOME/.firecracker/release-${VERSION}-${ARCH}/seccompiler-bin \ 73 + $HOME/.firecracker/release-${VERSION}-${ARCH}/snapshot-editor 74 + 75 + ln -s ${HOME}/.firecracker/release-${VERSION}-${ARCH}/firecracker-${VERSION}-${ARCH} $HOME/.firecracker/release-${VERSION}-${ARCH}/firecracker 76 + 77 + ln -s ${HOME}/.firecracker/release-${VERSION}-${ARCH}/cpu-template-helper-${VERSION}-${ARCH} $HOME/.firecracker/release-${VERSION}-${ARCH}/cpu-template-helper 78 + 79 + ln -s ${HOME}/.firecracker/release-${VERSION}-${ARCH}/jailer-${VERSION}-${ARCH} $HOME/.firecracker/release-${VERSION}-${ARCH}/jailer 80 + 81 + ln -s ${HOME}/.firecracker/release-${VERSION}-${ARCH}/rebase-snap-${VERSION}-${ARCH} $HOME/.firecracker/release-${VERSION}-${ARCH}/rebase-snap 82 + 83 + 84 + ln -s ${HOME}/.firecracker/release-${VERSION}-${ARCH}/seccompiler-bin-${VERSION}-${ARCH} $HOME/.firecracker/release-${VERSION}-${ARCH}/seccompiler-bin 85 + 86 + 87 + ln -s ${HOME}/.firecracker/release-${VERSION}-${ARCH}/snapshot-editor-${VERSION}-${ARCH} $HOME/.firecracker/release-${VERSION}-${ARCH}/snapshot-editor 88 + 89 + SUDO="" 90 + 91 + if command -v sudo >/dev/null 2>&1; then 92 + SUDO=sudo 93 + fi 94 + 95 + $SUDO cp $HOME/.firecracker/release-${VERSION}-${ARCH}/firecracker /usr/sbin/firecracker 96 + 97 + $SUDO cp $HOME/.firecracker/release-${VERSION}-${ARCH}/jailer /usr/local/bin/jailer 98 + 99 + $SUDO cp $HOME/.firecracker/release-${VERSION}-${ARCH}/cpu-template-helper /usr/local/bin/cpu-template-helper 100 + 101 + $SUDO cp $HOME/.firecracker/release-${VERSION}-${ARCH}/rebase-snap /usr/local/bin/rebase-snap 102 + 103 + $SUDO cp $HOME/.firecracker/release-${VERSION}-${ARCH}/seccompiler-bin /usr/local/bin/seccompiler-bin 104 + 105 + $SUDO cp $HOME/.firecracker/release-${VERSION}-${ARCH}/snapshot-editor /usr/local/bin/snapshot-editor 106 +
preview.png

This is a binary file and will not be displayed.