Skip to content

Commit c2cf3d0

Browse files
authored
Merge branch 'master' into little_fixes_4
2 parents 5fdaefc + 58edf03 commit c2cf3d0

File tree

13 files changed

+557
-20
lines changed

13 files changed

+557
-20
lines changed

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Changelog
22

3-
## Unreleased
3+
## [Unreleased]
4+
5+
### Added
6+
7+
- New command-line parameter for `firecracker`, named `--config-file`, which
8+
represents the path to a file that contains a JSON which can be used for
9+
configuring and starting a microVM without sending any API requests.
10+
- The jailer adheres to the "end of command options" convention, meaning
11+
all parameters specified after `--` are forwarded verbatim to Firecracker.
412

513
### Changed
614

FAQ.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,9 @@ Possible mitigations are:
196196
kernel so as to use `vmalloc` instead of `kmalloc` for them.
197197
- Reduce memory pressure on the host.
198198

199+
### How can I configure and start a microVM without sending API calls?
200+
201+
Passing an optional command line parameter, `--config-file`, to the Firecracker
202+
process allows this type of configuration. This parameter must be the path to a
203+
file that contains the JSON specification that will be used to configure and start
204+
the microVM. One example of such file can be found at `tests/framework/vm_config.json`.

docs/getting-started.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,29 @@ curl --unix-socket /tmp/firecracker.socket -i \
242242
}'
243243
```
244244

245+
#### Configuring the microVM without sending API requests
246+
247+
If you'd like to boot up a guest machine without using the API socket, you can do that
248+
by passing the parameter `--config-file` to the Firecracker process. The command for
249+
starting Firecracker with this option will look like this:
250+
251+
```bash
252+
./firecracker --api-sock /tmp/firecracker.socket --config-file
253+
<path_to_the_configuration_file>
254+
```
255+
256+
`path_to_the_configuration_file` should represent the path to a file that contains a
257+
JSON which stores the entire configuration for all of the microVM's resources. The
258+
JSON **must** contain the configuration for the guest kernel and rootfs, as these
259+
are mandatory, but all of the other resources are optional, so it's your choice
260+
if you want to configure them or not. Because using this configuration method will
261+
also start the microVM, you need to specify all desired pre-boot configurable resources
262+
in that JSON. The names of the resources are the ones from the `firecracker.yaml` file
263+
and the names of their fields are the same that are used in API requests.
264+
You can find an example of configuration file at `tests/framework/vm_config.json`.
265+
After the machine is booted, you can still use the socket to send API requests
266+
for post-boot operations.
267+
245268
## Building From Source
246269

247270
The quickest way to build and test Firecracker is by using our development

docs/jailer.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ jailer --id <id> \
1414
[--netns <netns>]
1515
[--daemonize]
1616
[--seccomp-level <level>]
17+
[--...extra arguments for Firecracker]
1718
```
1819

1920
- `id` is the unique VM identification string, which may contain alphanumeric
@@ -38,6 +39,14 @@ jailer --id <id> \
3839
Firecracker.
3940
- 2 (default): advanced filtering. This adds further checks on some of the
4041
parameters of the allowed syscalls.
42+
- The jailer adheres to the "end of command options" convention, meaning
43+
all parameters specified after `--` are forwarded to Firecracker. For
44+
example, this can be paired with the `--config-file` Firecracker argument to
45+
specify a configuration file when starting Firecracker via the jailer (the
46+
file path and the resources referenced within must be valid relative to a
47+
jailed Firecracker). Please note the jailer already passes the following
48+
parameters to the Firecracker process: `--api-sock`, `--seccomp-level` and
49+
`--id`.
4150

4251
## Jailer Operation
4352

@@ -79,8 +88,9 @@ After starting, the Jailer goes through the following operations:
7988
- Drop privileges via setting the provided `uid` and `gid`.
8089
- Exec into `<exec_file_name> --id=<id> --api-sock=/api.socket
8190
--seccomp-level=<level> --start-time-us=<opaque>
82-
--start-time-cpu-us=<opaque>`.
83-
Where:
91+
--start-time-cpu-us=<opaque>` (and also forward any extra arguments provided
92+
to the jailer after `--`, as mentioned in the **Jailer Usage** section),
93+
where:
8494
- `id`: (`string`) - The `id` argument provided to jailer.
8595
- `level`: (`number`) - the `--seccomp-level` argument provided to jailer.
8696
- `opaque`: (`number`) time calculated by the jailer that it spent doing

jailer/src/env.rs

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ pub struct Env {
5252
seccomp_level: u32,
5353
start_time_us: u64,
5454
start_time_cpu_us: u64,
55+
extra_args: Vec<String>,
5556
}
5657

5758
impl Env {
@@ -112,6 +113,13 @@ impl Env {
112113
.parse::<u32>()
113114
.map_err(Error::SeccompLevel)?;
114115

116+
let extra_args = args
117+
.values_of("extra-args")
118+
.into_iter()
119+
.flatten()
120+
.map(String::from)
121+
.collect();
122+
115123
Ok(Env {
116124
id: id.to_string(),
117125
numa_node,
@@ -124,6 +132,7 @@ impl Env {
124132
seccomp_level,
125133
start_time_us,
126134
start_time_cpu_us,
135+
extra_args,
127136
})
128137
}
129138

@@ -302,6 +311,7 @@ impl Env {
302311
.stderr(Stdio::inherit())
303312
.uid(self.uid())
304313
.gid(self.gid())
314+
.args(self.extra_args)
305315
.exec(),
306316
))
307317
}
@@ -323,6 +333,7 @@ mod tests {
323333
chroot_base: &str,
324334
netns: Option<&str>,
325335
daemonize: bool,
336+
extra_args: Vec<&str>,
326337
) -> ArgMatches<'a> {
327338
let app = clap_app();
328339

@@ -351,6 +362,11 @@ mod tests {
351362
arg_vec.push("--daemonize");
352363
}
353364

365+
arg_vec.push("--");
366+
for extra_arg in extra_args {
367+
arg_vec.push(extra_arg);
368+
}
369+
354370
app.get_matches_from_safe(arg_vec).unwrap()
355371
}
356372

@@ -363,6 +379,7 @@ mod tests {
363379
let gid = "1002";
364380
let chroot_base = "/";
365381
let netns = "zzzns";
382+
let extra_args = vec!["--config-file", "config_file_path"];
366383

367384
// This should be fine.
368385
let good_env = Env::new(
@@ -375,6 +392,7 @@ mod tests {
375392
chroot_base,
376393
Some(netns),
377394
true,
395+
extra_args,
378396
),
379397
0,
380398
0,
@@ -389,11 +407,26 @@ mod tests {
389407
assert_eq!(good_env.chroot_dir(), chroot_dir);
390408
assert_eq!(format!("{}", good_env.gid()), gid);
391409
assert_eq!(format!("{}", good_env.uid()), uid);
410+
392411
assert_eq!(good_env.netns, Some(netns.to_string()));
393412
assert!(good_env.daemonize);
413+
assert_eq!(
414+
good_env.extra_args,
415+
vec!["--config-file".to_string(), "config_file_path".to_string()]
416+
);
394417

395418
let another_good_env = Env::new(
396-
make_args(node, id, exec_file, uid, gid, chroot_base, None, false),
419+
make_args(
420+
node,
421+
id,
422+
exec_file,
423+
uid,
424+
gid,
425+
chroot_base,
426+
None,
427+
false,
428+
vec![],
429+
),
397430
0,
398431
0,
399432
)
@@ -402,7 +435,17 @@ mod tests {
402435

403436
// Not fine - invalid node.
404437
assert!(Env::new(
405-
make_args("zzz", id, exec_file, uid, gid, chroot_base, None, true),
438+
make_args(
439+
"zzz",
440+
id,
441+
exec_file,
442+
uid,
443+
gid,
444+
chroot_base,
445+
None,
446+
true,
447+
vec![],
448+
),
406449
0,
407450
0,
408451
)
@@ -418,7 +461,8 @@ mod tests {
418461
gid,
419462
chroot_base,
420463
None,
421-
true
464+
true,
465+
vec![],
422466
),
423467
0,
424468
0
@@ -435,7 +479,8 @@ mod tests {
435479
gid,
436480
chroot_base,
437481
None,
438-
true
482+
true,
483+
vec![],
439484
),
440485
0,
441486
0
@@ -444,15 +489,35 @@ mod tests {
444489

445490
// Not fine - invalid uid.
446491
assert!(Env::new(
447-
make_args(node, id, exec_file, "zzz", gid, chroot_base, None, true),
492+
make_args(
493+
node,
494+
id,
495+
exec_file,
496+
"zzz",
497+
gid,
498+
chroot_base,
499+
None,
500+
true,
501+
vec![],
502+
),
448503
0,
449504
0
450505
)
451506
.is_err());
452507

453508
// Not fine - invalid gid.
454509
assert!(Env::new(
455-
make_args(node, id, exec_file, uid, "zzz", chroot_base, None, true),
510+
make_args(
511+
node,
512+
id,
513+
exec_file,
514+
uid,
515+
"zzz",
516+
chroot_base,
517+
None,
518+
true,
519+
vec![],
520+
),
456521
0,
457522
0
458523
)

jailer/src/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::io;
2020
use std::path::{Path, PathBuf};
2121
use std::result;
2222

23-
use clap::{App, Arg, ArgMatches};
23+
use clap::{App, AppSettings, Arg, ArgMatches};
2424

2525
use env::Env;
2626
use fc_util::validators;
@@ -222,6 +222,7 @@ pub fn clap_app<'a, 'b>() -> App<'a, 'b> {
222222
.version(crate_version!())
223223
.author(crate_authors!())
224224
.about("Jail a microVM.")
225+
.setting(AppSettings::TrailingVarArg)
225226
.arg(
226227
Arg::with_name("id")
227228
.long("id")
@@ -298,6 +299,13 @@ pub fn clap_app<'a, 'b>() -> App<'a, 'b> {
298299
.default_value("2")
299300
.possible_values(&["0", "1", "2"]),
300301
)
302+
.arg(
303+
Arg::with_name("extra-args")
304+
.help("Arguments that will be passed verbatim to the exec file.")
305+
.required(false)
306+
.takes_value(true)
307+
.multiple(true),
308+
)
301309
}
302310

303311
fn sanitize_process() {

src/main.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ extern crate vmm;
1717
use backtrace::Backtrace;
1818
use clap::{App, Arg};
1919

20+
use std::fs;
2021
use std::io::ErrorKind;
2122
use std::panic;
2223
use std::path::PathBuf;
@@ -108,6 +109,13 @@ fn main() {
108109
.takes_value(true)
109110
.hidden(true),
110111
)
112+
.arg(
113+
Arg::with_name("config-file")
114+
.long("config-file")
115+
.help("Path to a file that contains the microVM configuration in JSON format.")
116+
.takes_value(true)
117+
.required(false),
118+
)
111119
.get_matches();
112120

113121
let bind_path = cmd_arguments
@@ -142,6 +150,11 @@ fn main() {
142150
.expect("'start-time-cpu_us' parameter expected to be of 'u64' type.")
143151
});
144152

153+
let vmm_config_json = cmd_arguments
154+
.value_of("config-file")
155+
.map(fs::read_to_string)
156+
.map(|x| x.expect("Unable to open or read from the configuration file"));
157+
145158
let shared_info = Arc::new(RwLock::new(InstanceInfo {
146159
state: InstanceState::Uninitialized,
147160
id: instance_id,
@@ -156,8 +169,13 @@ fn main() {
156169
.get_event_fd_clone()
157170
.expect("Cannot clone API eventFD.");
158171

159-
let _vmm_thread_handle =
160-
vmm::start_vmm_thread(shared_info, api_event_fd, from_api, seccomp_level);
172+
let _vmm_thread_handle = vmm::start_vmm_thread(
173+
shared_info,
174+
api_event_fd,
175+
from_api,
176+
seccomp_level,
177+
vmm_config_json,
178+
);
161179

162180
match server.bind_and_run(bind_path, start_time_us, start_time_cpu_us, seccomp_level) {
163181
Ok(_) => (),

tests/framework/jailer.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def __del__(self):
6262
"""Cleanup this jailer context."""
6363
self.cleanup()
6464

65-
def construct_param_list(self):
65+
def construct_param_list(self, config_file):
6666
"""Create the list of parameters we want the jailer to start with.
6767
6868
We want to be able to vary any parameter even the required ones as we
@@ -94,6 +94,9 @@ def construct_param_list(self):
9494
jailer_param_list.extend(
9595
['--seccomp-level', str(self.seccomp_level)]
9696
)
97+
if config_file is not None:
98+
jailer_param_list.extend(['--'])
99+
jailer_param_list.extend(['--config-file', str(config_file)])
97100
return jailer_param_list
98101

99102
def chroot_base_with_id(self):
@@ -113,16 +116,17 @@ def chroot_path(self):
113116
"""Return the MicroVM chroot path."""
114117
return os.path.join(self.chroot_base_with_id(), 'root')
115118

116-
def jailed_path(self, file_path, create=False):
119+
def jailed_path(self, file_path, create=False, create_jail=False):
117120
"""Create a hard link owned by uid:gid.
118121
119122
Create a hard link to the specified file, changes the owner to
120123
uid:gid, and returns a path to the link which is valid within the jail.
121124
"""
122125
file_name = os.path.basename(file_path)
123126
global_p = os.path.join(self.chroot_path(), file_name)
127+
if create_jail:
128+
os.makedirs(self.chroot_path(), exist_ok=True)
124129
jailed_p = os.path.join("/", file_name)
125-
126130
if create:
127131
cmd = 'ln -f {} {}'.format(file_path, global_p)
128132
run(cmd, shell=True, check=True)

0 commit comments

Comments
 (0)