1use std::collections::HashMap;
2
3use crate::{
4 error::OciSpecError,
5 runtime::{Arch, LinuxNamespaceType, LinuxSeccompAction},
6};
7use derive_builder::Builder;
8use getset::{Getters, MutGetters, Setters};
9use serde::{Deserialize, Serialize};
10
11#[derive(
16 Builder,
17 Clone,
18 Debug,
19 Default,
20 Deserialize,
21 Eq,
22 MutGetters,
23 Getters,
24 Setters,
25 PartialEq,
26 Serialize,
27)]
28#[serde(rename_all = "camelCase")]
29#[builder(
30 default,
31 pattern = "owned",
32 setter(into, strip_option),
33 build_fn(error = "OciSpecError")
34)]
35#[getset(get_mut = "pub", get = "pub", set = "pub")]
36pub struct Features {
37 oci_version_min: String,
39 oci_version_max: String,
41 hooks: Option<Vec<String>>,
44 mount_options: Option<Vec<String>>,
48 linux: Option<LinuxFeature>,
50 annotations: Option<HashMap<String, String>>,
53 potentially_unsafe_config_annotations: Option<Vec<String>>,
57}
58
59#[derive(
61 Builder,
62 Clone,
63 Debug,
64 Default,
65 Deserialize,
66 Eq,
67 MutGetters,
68 Getters,
69 Setters,
70 PartialEq,
71 Serialize,
72)]
73#[serde(rename_all = "camelCase")]
74#[builder(
75 default,
76 pattern = "owned",
77 setter(into, strip_option),
78 build_fn(error = "OciSpecError")
79)]
80#[getset(get_mut = "pub", get = "pub", set = "pub")]
81pub struct LinuxFeature {
82 namespaces: Option<Vec<LinuxNamespaceType>>,
85 capabilities: Option<Vec<String>>,
88 cgroup: Option<Cgroup>,
90 seccomp: Option<Seccomp>,
92 apparmor: Option<Apparmor>,
94 selinux: Option<Selinux>,
96 intel_rdt: Option<IntelRdt>,
98 mount_extensions: Option<MountExtensions>,
100}
101
102#[derive(
104 Builder,
105 Clone,
106 Debug,
107 Default,
108 Deserialize,
109 Eq,
110 MutGetters,
111 Getters,
112 Setters,
113 PartialEq,
114 Serialize,
115)]
116#[serde(rename_all = "camelCase")]
117#[builder(
118 default,
119 pattern = "owned",
120 setter(into, strip_option),
121 build_fn(error = "OciSpecError")
122)]
123#[getset(get_mut = "pub", get = "pub", set = "pub")]
124pub struct Cgroup {
125 v1: Option<bool>,
129 v2: Option<bool>,
133 systemd: Option<bool>,
137 systemd_user: Option<bool>,
141 rdma: Option<bool>,
145}
146
147#[derive(
149 Builder,
150 Clone,
151 Debug,
152 Default,
153 Deserialize,
154 Eq,
155 MutGetters,
156 Getters,
157 Setters,
158 PartialEq,
159 Serialize,
160)]
161#[serde(rename_all = "camelCase")]
162#[builder(
163 default,
164 pattern = "owned",
165 setter(into, strip_option),
166 build_fn(error = "OciSpecError")
167)]
168#[getset(get_mut = "pub", get = "pub", set = "pub")]
169pub struct Seccomp {
170 enabled: Option<bool>,
173 actions: Option<Vec<LinuxSeccompAction>>,
176 operators: Option<Vec<String>>,
179 archs: Option<Vec<Arch>>,
182 known_flags: Option<Vec<String>>,
185 supported_flags: Option<Vec<String>>,
190}
191
192#[derive(
194 Builder,
195 Clone,
196 Debug,
197 Default,
198 Deserialize,
199 Eq,
200 MutGetters,
201 Getters,
202 Setters,
203 PartialEq,
204 Serialize,
205)]
206#[serde(rename_all = "camelCase")]
207#[builder(
208 default,
209 pattern = "owned",
210 setter(into, strip_option),
211 build_fn(error = "OciSpecError")
212)]
213#[getset(get_mut = "pub", get = "pub", set = "pub")]
214pub struct Apparmor {
215 enabled: Option<bool>,
219}
220
221#[derive(
223 Builder,
224 Clone,
225 Debug,
226 Default,
227 Deserialize,
228 Eq,
229 MutGetters,
230 Getters,
231 Setters,
232 PartialEq,
233 Serialize,
234)]
235#[serde(rename_all = "camelCase")]
236#[builder(
237 default,
238 pattern = "owned",
239 setter(into, strip_option),
240 build_fn(error = "OciSpecError")
241)]
242#[getset(get_mut = "pub", get = "pub", set = "pub")]
243pub struct Selinux {
244 enabled: Option<bool>,
248}
249
250#[derive(
252 Builder,
253 Clone,
254 Debug,
255 Default,
256 Deserialize,
257 Eq,
258 MutGetters,
259 Getters,
260 Setters,
261 PartialEq,
262 Serialize,
263)]
264#[serde(rename_all = "camelCase")]
265#[builder(
266 default,
267 pattern = "owned",
268 setter(into, strip_option),
269 build_fn(error = "OciSpecError")
270)]
271#[getset(get_mut = "pub", get = "pub", set = "pub")]
272pub struct IntelRdt {
273 enabled: Option<bool>,
276}
277
278#[derive(
280 Builder,
281 Clone,
282 Debug,
283 Default,
284 Deserialize,
285 Eq,
286 MutGetters,
287 Getters,
288 Setters,
289 PartialEq,
290 Serialize,
291)]
292#[serde(rename_all = "camelCase")]
293#[builder(
294 default,
295 pattern = "owned",
296 setter(into, strip_option),
297 build_fn(error = "OciSpecError")
298)]
299#[getset(get_mut = "pub", get = "pub", set = "pub")]
300pub struct MountExtensions {
301 idmap: Option<IDMap>,
303}
304
305#[derive(
307 Builder,
308 Clone,
309 Debug,
310 Default,
311 Deserialize,
312 Eq,
313 MutGetters,
314 Getters,
315 Setters,
316 PartialEq,
317 Serialize,
318)]
319#[serde(rename_all = "camelCase")]
320#[builder(
321 default,
322 pattern = "owned",
323 setter(into, strip_option),
324 build_fn(error = "OciSpecError")
325)]
326#[getset(get_mut = "pub", get = "pub", set = "pub")]
327pub struct IDMap {
328 enabled: Option<bool>,
332}
333
334#[cfg(test)]
335mod tests {
336 use std::ops::Deref;
337
338 use super::*;
339
340 #[test]
341 fn test_parse_features() {
342 let example_json = r#"
343{
344 "ociVersionMin": "1.0.0",
345 "ociVersionMax": "1.1.0-rc.2",
346 "hooks": [
347 "prestart",
348 "createRuntime",
349 "createContainer",
350 "startContainer",
351 "poststart",
352 "poststop"
353 ],
354 "mountOptions": [
355 "async",
356 "atime",
357 "bind",
358 "defaults",
359 "dev",
360 "diratime",
361 "dirsync",
362 "exec",
363 "iversion",
364 "lazytime",
365 "loud",
366 "mand",
367 "noatime",
368 "nodev",
369 "nodiratime",
370 "noexec",
371 "noiversion",
372 "nolazytime",
373 "nomand",
374 "norelatime",
375 "nostrictatime",
376 "nosuid",
377 "nosymfollow",
378 "private",
379 "ratime",
380 "rbind",
381 "rdev",
382 "rdiratime",
383 "relatime",
384 "remount",
385 "rexec",
386 "rnoatime",
387 "rnodev",
388 "rnodiratime",
389 "rnoexec",
390 "rnorelatime",
391 "rnostrictatime",
392 "rnosuid",
393 "rnosymfollow",
394 "ro",
395 "rprivate",
396 "rrelatime",
397 "rro",
398 "rrw",
399 "rshared",
400 "rslave",
401 "rstrictatime",
402 "rsuid",
403 "rsymfollow",
404 "runbindable",
405 "rw",
406 "shared",
407 "silent",
408 "slave",
409 "strictatime",
410 "suid",
411 "symfollow",
412 "sync",
413 "tmpcopyup",
414 "unbindable"
415 ],
416 "linux": {
417 "namespaces": [
418 "cgroup",
419 "ipc",
420 "mount",
421 "network",
422 "pid",
423 "user",
424 "uts"
425 ],
426 "capabilities": [
427 "CAP_CHOWN",
428 "CAP_DAC_OVERRIDE",
429 "CAP_DAC_READ_SEARCH",
430 "CAP_FOWNER",
431 "CAP_FSETID",
432 "CAP_KILL",
433 "CAP_SETGID",
434 "CAP_SETUID",
435 "CAP_SETPCAP",
436 "CAP_LINUX_IMMUTABLE",
437 "CAP_NET_BIND_SERVICE",
438 "CAP_NET_BROADCAST",
439 "CAP_NET_ADMIN",
440 "CAP_NET_RAW",
441 "CAP_IPC_LOCK",
442 "CAP_IPC_OWNER",
443 "CAP_SYS_MODULE",
444 "CAP_SYS_RAWIO",
445 "CAP_SYS_CHROOT",
446 "CAP_SYS_PTRACE",
447 "CAP_SYS_PACCT",
448 "CAP_SYS_ADMIN",
449 "CAP_SYS_BOOT",
450 "CAP_SYS_NICE",
451 "CAP_SYS_RESOURCE",
452 "CAP_SYS_TIME",
453 "CAP_SYS_TTY_CONFIG",
454 "CAP_MKNOD",
455 "CAP_LEASE",
456 "CAP_AUDIT_WRITE",
457 "CAP_AUDIT_CONTROL",
458 "CAP_SETFCAP",
459 "CAP_MAC_OVERRIDE",
460 "CAP_MAC_ADMIN",
461 "CAP_SYSLOG",
462 "CAP_WAKE_ALARM",
463 "CAP_BLOCK_SUSPEND",
464 "CAP_AUDIT_READ",
465 "CAP_PERFMON",
466 "CAP_BPF",
467 "CAP_CHECKPOINT_RESTORE"
468 ],
469 "cgroup": {
470 "v1": true,
471 "v2": true,
472 "systemd": true,
473 "systemdUser": true,
474 "rdma": true
475 },
476 "seccomp": {
477 "enabled": true,
478 "actions": [
479 "SCMP_ACT_ALLOW",
480 "SCMP_ACT_ERRNO",
481 "SCMP_ACT_KILL",
482 "SCMP_ACT_KILL_PROCESS",
483 "SCMP_ACT_KILL_THREAD",
484 "SCMP_ACT_LOG",
485 "SCMP_ACT_NOTIFY",
486 "SCMP_ACT_TRACE",
487 "SCMP_ACT_TRAP"
488 ],
489 "operators": [
490 "SCMP_CMP_EQ",
491 "SCMP_CMP_GE",
492 "SCMP_CMP_GT",
493 "SCMP_CMP_LE",
494 "SCMP_CMP_LT",
495 "SCMP_CMP_MASKED_EQ",
496 "SCMP_CMP_NE"
497 ],
498 "archs": [
499 "SCMP_ARCH_AARCH64",
500 "SCMP_ARCH_ARM",
501 "SCMP_ARCH_MIPS",
502 "SCMP_ARCH_MIPS64",
503 "SCMP_ARCH_MIPS64N32",
504 "SCMP_ARCH_MIPSEL",
505 "SCMP_ARCH_MIPSEL64",
506 "SCMP_ARCH_MIPSEL64N32",
507 "SCMP_ARCH_PPC",
508 "SCMP_ARCH_PPC64",
509 "SCMP_ARCH_PPC64LE",
510 "SCMP_ARCH_RISCV64",
511 "SCMP_ARCH_S390",
512 "SCMP_ARCH_S390X",
513 "SCMP_ARCH_X32",
514 "SCMP_ARCH_X86",
515 "SCMP_ARCH_X86_64"
516 ],
517 "knownFlags": [
518 "SECCOMP_FILTER_FLAG_TSYNC",
519 "SECCOMP_FILTER_FLAG_SPEC_ALLOW",
520 "SECCOMP_FILTER_FLAG_LOG"
521 ],
522 "supportedFlags": [
523 "SECCOMP_FILTER_FLAG_TSYNC",
524 "SECCOMP_FILTER_FLAG_SPEC_ALLOW",
525 "SECCOMP_FILTER_FLAG_LOG"
526 ]
527 },
528 "apparmor": {
529 "enabled": true
530 },
531 "selinux": {
532 "enabled": true
533 },
534 "intelRdt": {
535 "enabled": true
536 }
537 },
538 "annotations": {
539 "io.github.seccomp.libseccomp.version": "2.5.4",
540 "org.opencontainers.runc.checkpoint.enabled": "true",
541 "org.opencontainers.runc.commit": "v1.1.0-534-g26851168",
542 "org.opencontainers.runc.version": "1.1.0+dev"
543 }
544}"#;
545
546 let features: Features = serde_json::from_str(example_json).unwrap();
548 assert_eq!(features.oci_version_min().deref(), "1.0.0".to_string());
549 assert_eq!(features.oci_version_max().deref(), "1.1.0-rc.2".to_string());
550
551 assert_eq!(
552 features.hooks.as_ref().unwrap(),
553 &[
554 "prestart",
555 "createRuntime",
556 "createContainer",
557 "startContainer",
558 "poststart",
559 "poststop"
560 ]
561 );
562
563 assert_eq!(
564 features.mount_options.as_ref().unwrap(),
565 &[
566 "async",
567 "atime",
568 "bind",
569 "defaults",
570 "dev",
571 "diratime",
572 "dirsync",
573 "exec",
574 "iversion",
575 "lazytime",
576 "loud",
577 "mand",
578 "noatime",
579 "nodev",
580 "nodiratime",
581 "noexec",
582 "noiversion",
583 "nolazytime",
584 "nomand",
585 "norelatime",
586 "nostrictatime",
587 "nosuid",
588 "nosymfollow",
589 "private",
590 "ratime",
591 "rbind",
592 "rdev",
593 "rdiratime",
594 "relatime",
595 "remount",
596 "rexec",
597 "rnoatime",
598 "rnodev",
599 "rnodiratime",
600 "rnoexec",
601 "rnorelatime",
602 "rnostrictatime",
603 "rnosuid",
604 "rnosymfollow",
605 "ro",
606 "rprivate",
607 "rrelatime",
608 "rro",
609 "rrw",
610 "rshared",
611 "rslave",
612 "rstrictatime",
613 "rsuid",
614 "rsymfollow",
615 "runbindable",
616 "rw",
617 "shared",
618 "silent",
619 "slave",
620 "strictatime",
621 "suid",
622 "symfollow",
623 "sync",
624 "tmpcopyup",
625 "unbindable"
626 ]
627 );
628
629 let linux = features.linux().as_ref().unwrap();
630
631 assert_eq!(
632 linux.namespaces.as_ref().unwrap(),
633 &[
634 LinuxNamespaceType::Cgroup,
635 LinuxNamespaceType::Ipc,
636 LinuxNamespaceType::Mount,
637 LinuxNamespaceType::Network,
638 LinuxNamespaceType::Pid,
639 LinuxNamespaceType::User,
640 LinuxNamespaceType::Uts,
641 ]
642 );
643
644 assert_eq!(
645 linux.capabilities.as_ref().unwrap(),
646 &[
647 "CAP_CHOWN",
648 "CAP_DAC_OVERRIDE",
649 "CAP_DAC_READ_SEARCH",
650 "CAP_FOWNER",
651 "CAP_FSETID",
652 "CAP_KILL",
653 "CAP_SETGID",
654 "CAP_SETUID",
655 "CAP_SETPCAP",
656 "CAP_LINUX_IMMUTABLE",
657 "CAP_NET_BIND_SERVICE",
658 "CAP_NET_BROADCAST",
659 "CAP_NET_ADMIN",
660 "CAP_NET_RAW",
661 "CAP_IPC_LOCK",
662 "CAP_IPC_OWNER",
663 "CAP_SYS_MODULE",
664 "CAP_SYS_RAWIO",
665 "CAP_SYS_CHROOT",
666 "CAP_SYS_PTRACE",
667 "CAP_SYS_PACCT",
668 "CAP_SYS_ADMIN",
669 "CAP_SYS_BOOT",
670 "CAP_SYS_NICE",
671 "CAP_SYS_RESOURCE",
672 "CAP_SYS_TIME",
673 "CAP_SYS_TTY_CONFIG",
674 "CAP_MKNOD",
675 "CAP_LEASE",
676 "CAP_AUDIT_WRITE",
677 "CAP_AUDIT_CONTROL",
678 "CAP_SETFCAP",
679 "CAP_MAC_OVERRIDE",
680 "CAP_MAC_ADMIN",
681 "CAP_SYSLOG",
682 "CAP_WAKE_ALARM",
683 "CAP_BLOCK_SUSPEND",
684 "CAP_AUDIT_READ",
685 "CAP_PERFMON",
686 "CAP_BPF",
687 "CAP_CHECKPOINT_RESTORE"
688 ],
689 );
690
691 assert_eq!(
692 linux.cgroup.as_ref().unwrap(),
693 &Cgroup {
694 v1: Some(true),
695 v2: Some(true),
696 systemd: Some(true),
697 systemd_user: Some(true),
698 rdma: Some(true),
699 }
700 );
701
702 assert_eq!(
703 linux.seccomp.as_ref().unwrap(),
704 &Seccomp {
705 enabled: Some(true),
706 actions: Some(vec![
707 LinuxSeccompAction::ScmpActAllow,
708 LinuxSeccompAction::ScmpActErrno,
709 LinuxSeccompAction::ScmpActKill,
710 LinuxSeccompAction::ScmpActKillProcess,
711 LinuxSeccompAction::ScmpActKillThread,
712 LinuxSeccompAction::ScmpActLog,
713 LinuxSeccompAction::ScmpActNotify,
714 LinuxSeccompAction::ScmpActTrace,
715 LinuxSeccompAction::ScmpActTrap
716 ]),
717 operators: Some(vec![
718 "SCMP_CMP_EQ".to_string(),
719 "SCMP_CMP_GE".to_string(),
720 "SCMP_CMP_GT".to_string(),
721 "SCMP_CMP_LE".to_string(),
722 "SCMP_CMP_LT".to_string(),
723 "SCMP_CMP_MASKED_EQ".to_string(),
724 "SCMP_CMP_NE".to_string()
725 ]),
726 archs: Some(vec![
727 Arch::ScmpArchAarch64,
728 Arch::ScmpArchArm,
729 Arch::ScmpArchMips,
730 Arch::ScmpArchMips64,
731 Arch::ScmpArchMips64n32,
732 Arch::ScmpArchMipsel,
733 Arch::ScmpArchMipsel64,
734 Arch::ScmpArchMipsel64n32,
735 Arch::ScmpArchPpc,
736 Arch::ScmpArchPpc64,
737 Arch::ScmpArchPpc64le,
738 Arch::ScmpArchRiscv64,
739 Arch::ScmpArchS390,
740 Arch::ScmpArchS390x,
741 Arch::ScmpArchX32,
742 Arch::ScmpArchX86,
743 Arch::ScmpArchX86_64,
744 ]),
745 known_flags: Some(vec![
746 "SECCOMP_FILTER_FLAG_TSYNC".to_string(),
747 "SECCOMP_FILTER_FLAG_SPEC_ALLOW".to_string(),
748 "SECCOMP_FILTER_FLAG_LOG".to_string()
749 ]),
750 supported_flags: Some(vec![
751 "SECCOMP_FILTER_FLAG_TSYNC".to_string(),
752 "SECCOMP_FILTER_FLAG_SPEC_ALLOW".to_string(),
753 "SECCOMP_FILTER_FLAG_LOG".to_string()
754 ])
755 },
756 );
757
758 assert_eq!(
759 linux.apparmor.as_ref().unwrap(),
760 &Apparmor {
761 enabled: Some(true)
762 }
763 );
764
765 assert_eq!(
766 linux.selinux.as_ref().unwrap(),
767 &Selinux {
768 enabled: Some(true)
769 }
770 );
771
772 assert_eq!(
773 linux.intel_rdt.as_ref().unwrap(),
774 &IntelRdt {
775 enabled: Some(true)
776 }
777 );
778
779 assert_eq!(
780 features.annotations().as_ref().unwrap(),
781 &[
782 (
783 "io.github.seccomp.libseccomp.version".to_string(),
784 "2.5.4".to_string()
785 ),
786 (
787 "org.opencontainers.runc.checkpoint.enabled".to_string(),
788 "true".to_string()
789 ),
790 (
791 "org.opencontainers.runc.commit".to_string(),
792 "v1.1.0-534-g26851168".to_string()
793 ),
794 (
795 "org.opencontainers.runc.version".to_string(),
796 "1.1.0+dev".to_string()
797 )
798 ]
799 .iter()
800 .cloned()
801 .collect()
802 );
803
804 assert_eq!(
805 features.potentially_unsafe_config_annotations().as_ref(),
806 None,
807 );
808 }
809}