myc

Self-custodial remote signer for Radroots apps
git clone https://radroots.dev/git/myc.git
Log | Files | Refs | README | LICENSE

services_hardening_native_release.rs (12756B)


      1 #![forbid(unsafe_code)]
      2 
      3 use std::collections::BTreeSet;
      4 
      5 use serde_json::json;
      6 
      7 const CONTRACT: &str = include_str!("../contracts/services_hardening/native_release.v2.json");
      8 const ACTIVE_CONTRACT: &str =
      9     include_str!("../contracts/services_hardening/native_release.v3.json");
     10 const MANIFEST: &str = include_str!("../Cargo.toml");
     11 const LOCK: &str = include_str!("../Cargo.lock");
     12 const FLAKE: &str = include_str!("../flake.nix");
     13 const FLAKE_LOCK: &str = include_str!("../flake.lock");
     14 const CARGO_CONFIG: &str = include_str!("../.cargo/config.toml");
     15 const SYSTEMD_UNIT: &str = include_str!("../packaging/systemd/myc@.service");
     16 
     17 const LIB_REVISION: &str = "055096853fca95e15d0f813d33a14aca13be3881";
     18 const LIB_REPOSITORY: &str = "https://github.com/radrootslabs/lib";
     19 
     20 #[test]
     21 fn native_release_contract_and_manifest_metadata_are_exact() {
     22     let contract: serde_json::Value = serde_json::from_str(CONTRACT).expect("release contract");
     23     assert_eq!(
     24         contract,
     25         json!({
     26             "schema": "radroots.myc.native-release",
     27             "schema_version": 2,
     28             "contract_version": 2,
     29             "predecessor": {
     30                 "schema_version": 1,
     31                 "filename": "native_release.v1.json",
     32                 "transition": "forward_only_replace"
     33             },
     34             "service": "myc",
     35             "package": {
     36                 "name": "myc",
     37                 "binary": "myc",
     38                 "version": "0.1.0",
     39                 "repository": "https://github.com/radrootslabs/myc",
     40                 "publish_to_crates_io": false
     41             },
     42             "generator": {
     43                 "command": "cargo xtask native-release",
     44                 "modes": ["check", "write"],
     45                 "required_arguments": [
     46                     "mode", "target", "binary", "output", "source_date_epoch"
     47                 ],
     48                 "source_date_epoch_range": "1..=4294967295",
     49                 "clean_exact_head": true,
     50                 "target_binary_validation": "executable_elf64_little_endian_exact_machine",
     51                 "canonical_json": "compact_utf8_json_with_one_final_lf",
     52                 "deterministic_archives": true,
     53                 "output_directory_mode": "0755",
     54                 "output_file_mode": "0644",
     55                 "durability": "sync_files_then_output_directory_then_parent"
     56             },
     57             "toolchain": {
     58                 "rust_version": "1.97.1",
     59                 "edition": "2024",
     60                 "resolver": "3",
     61                 "host_feature_profile": "service-host"
     62             },
     63             "release_profile": {
     64                 "lto": "thin",
     65                 "codegen_units": 1,
     66                 "overflow_checks": true,
     67                 "strip": "symbols",
     68                 "panic": "unwind"
     69             },
     70             "source_lock": {
     71                 "filename": "radroots.service.source-lock.v2.toml",
     72                 "schema": "radroots.service.source-lock.v2",
     73                 "lib_repository": LIB_REPOSITORY,
     74                 "architecture": "radroots.crates.release.v2"
     75             },
     76             "contract_versions": {
     77                 "config": 1,
     78                 "state": 12,
     79                 "admin": 1,
     80                 "status": 1,
     81                 "provider": 1
     82             },
     83             "native_targets": [
     84                 { "target": "aarch64-unknown-linux-gnu", "posture": "target" },
     85                 { "target": "x86_64-unknown-linux-gnu", "posture": "target" }
     86             ],
     87             "output_inventory": [
     88                 "LICENSE",
     89                 "SHA256SUMS",
     90                 "THIRD-PARTY-NOTICES.txt",
     91                 "artifact-manifest.v1.json",
     92                 "binary.tar.gz",
     93                 "config.example.toml",
     94                 "config.schema.json",
     95                 "provenance-input.v1.json",
     96                 "radroots.service.source-lock.v2.toml",
     97                 "sbom.cdx.json",
     98                 "service-source.tar.gz",
     99                 "systemd.service"
    100             ],
    101             "signing_inputs": [
    102                 "SHA256SUMS",
    103                 "artifact-manifest.v1.json",
    104                 "provenance-input.v1.json"
    105             ],
    106             "provenance_posture": "deterministic_unsigned_slsa_v1_input_external_keys_only",
    107             "sbom_format": "cyclonedx_json_1_5_locked_cargo_graph",
    108             "source_archive": "locked_offline_cargo_build_with_vendored_dependencies",
    109             "checksum_format": "sha256_lower_hex_two_spaces_path_lf_sorted_by_path",
    110             "protected_material_included": false,
    111             "maximums": {
    112                 "text_input_bytes": 1048576,
    113                 "generated_document_bytes": 16777216,
    114                 "cargo_metadata_bytes": 33554432,
    115                 "binary_bytes": 536870912,
    116                 "source_archive_bytes": 1073741824,
    117                 "packages": 8192,
    118                 "tracked_files": 4096
    119             },
    120             "deferred_through_rcld_rshr_170": [
    121                 "nix_evaluation",
    122                 "nix_build",
    123                 "nixos_module_qualification",
    124                 "oci_artifact"
    125             ],
    126             "forbidden": [
    127                 "nix_input",
    128                 "nixos_module_output",
    129                 "oci_input",
    130                 "oci_output",
    131                 "protected_material",
    132                 "parent_owned_human_docs",
    133                 "private_harness",
    134                 "local_or_path_lib_dependency",
    135                 "floating_or_branch_lib_dependency",
    136                 "mixed_lib_revision",
    137                 "crates_io_publication",
    138                 "signing",
    139                 "tagging",
    140                 "release_publication",
    141                 "deployment"
    142             ]
    143         })
    144     );
    145 
    146     let manifest: toml::Value = toml::from_str(MANIFEST).expect("Cargo manifest");
    147     let package = manifest["package"].as_table().expect("package");
    148     assert_eq!(
    149         package["repository"].as_str(),
    150         Some("https://github.com/radrootslabs/myc")
    151     );
    152     assert_eq!(package["readme"].as_str(), Some("README"));
    153     assert_eq!(package["publish"].as_bool(), Some(false));
    154 
    155     let metadata = &manifest["workspace"]["metadata"]["radroots"];
    156     assert_eq!(
    157         metadata["service_source_lock"],
    158         toml::Value::Table(toml::toml! {
    159             service = "myc"
    160             host_feature_profile = "service-host"
    161             nix_material = "qualified"
    162             config_contract_version = 1
    163             state_contract_version = 12
    164             admin_contract_version = 1
    165             status_contract_version = 1
    166             provider_contract_version = 1
    167         })
    168     );
    169     assert_eq!(
    170         metadata["service_release"],
    171         toml::Value::Table(toml::toml! {
    172             service = "myc"
    173             service_package = "myc"
    174             binary_name = "myc"
    175             version = "0.1.0"
    176         })
    177     );
    178     assert_eq!(
    179         manifest["profile"]["release"],
    180         toml::Value::Table(toml::toml! {
    181             lto = "thin"
    182             codegen-units = 1
    183             overflow-checks = true
    184             strip = "symbols"
    185             panic = "unwind"
    186         })
    187     );
    188     assert_eq!(
    189         CARGO_CONFIG,
    190         "[alias]\nxtask = \"run --locked -p myc_xtask --\"\n"
    191     );
    192     for required in [
    193         "ExecStart=/usr/bin/myc --profile service-host --instance %i run",
    194         "ConfigurationDirectory=radroots/services/myc/%i",
    195         "StateDirectory=radroots/services/myc/%i",
    196         "RuntimeDirectory=radroots/services/myc/%i",
    197         "UMask=0077",
    198         "NoNewPrivileges=yes",
    199         "ProtectSystem=strict",
    200         "CapabilityBoundingSet=",
    201     ] {
    202         assert!(SYSTEMD_UNIT.contains(required), "missing `{required}`");
    203     }
    204 }
    205 
    206 #[test]
    207 fn every_radroots_dependency_is_exactly_source_locked() {
    208     let manifest: toml::Value = toml::from_str(MANIFEST).expect("Cargo manifest");
    209     let dependencies = manifest["dependencies"].as_table().expect("dependencies");
    210     let radroots = dependencies
    211         .iter()
    212         .filter(|(name, _)| name.starts_with("radroots_"))
    213         .collect::<Vec<_>>();
    214     assert_eq!(radroots.len(), 11);
    215     for (name, dependency) in radroots {
    216         let dependency = dependency.as_table().expect("detailed dependency");
    217         assert_eq!(
    218             dependency.get("git").and_then(toml::Value::as_str),
    219             Some(LIB_REPOSITORY),
    220             "{name}"
    221         );
    222         assert_eq!(
    223             dependency.get("rev").and_then(toml::Value::as_str),
    224             Some(LIB_REVISION),
    225             "{name}"
    226         );
    227         assert_eq!(
    228             dependency.get("version").and_then(toml::Value::as_str),
    229             Some("=0.1.0-alpha"),
    230             "{name}"
    231         );
    232         for forbidden in ["path", "branch", "tag"] {
    233             assert!(
    234                 !dependency.contains_key(forbidden),
    235                 "{name} contains `{forbidden}`"
    236             );
    237         }
    238     }
    239     assert!(!MANIFEST.contains("[patch."));
    240 
    241     let sources = LOCK
    242         .lines()
    243         .filter_map(|line| line.strip_prefix("source = \"git+"))
    244         .filter_map(|line| line.strip_suffix('"'))
    245         .filter(|source| source.contains("radrootslabs/lib"))
    246         .collect::<BTreeSet<_>>();
    247     assert_eq!(sources.len(), 1);
    248     let source = sources.into_iter().next().expect("Lib source");
    249     assert!(source.contains(&format!("?rev={LIB_REVISION}#{LIB_REVISION}")));
    250 
    251     for required in [
    252         "lib = {",
    253         "url = \"github:radrootslabs/lib/055096853fca95e15d0f813d33a14aca13be3881\";",
    254         "inputs.crane.follows = \"crane\";",
    255         "systems = lib.lib.supportedSystems;",
    256         "nixosModules.default",
    257     ] {
    258         assert!(
    259             FLAKE.contains(required),
    260             "flake source data is missing `{required}`"
    261         );
    262     }
    263     let flake_lock: serde_json::Value =
    264         serde_json::from_str(FLAKE_LOCK).expect("flake source lock");
    265     assert_eq!(flake_lock["version"], 7);
    266     assert_eq!(flake_lock["root"], "root");
    267     assert_eq!(flake_lock["nodes"]["root"]["inputs"]["lib"], "lib");
    268     assert_eq!(
    269         flake_lock["nodes"]["lib"],
    270         json!({
    271             "inputs": {
    272                 "crane": ["crane"],
    273                 "flake-parts": "flake-parts",
    274                 "nixpkgs": "nixpkgs",
    275                 "rust-overlay": "rust-overlay",
    276                 "treefmt-nix": "treefmt-nix"
    277             },
    278             "locked": {
    279                 "lastModified": 1788739124_u64,
    280                 "narHash": "sha256-Aw8qbU1DrtxSYJKg0js6kexnKgVGFCjR34bgq+ZVAVo=",
    281                 "owner": "radrootslabs",
    282                 "repo": "lib",
    283                 "rev": LIB_REVISION,
    284                 "type": "github"
    285             },
    286             "original": {
    287                 "owner": "radrootslabs",
    288                 "repo": "lib",
    289                 "rev": LIB_REVISION,
    290                 "type": "github"
    291             }
    292         })
    293     );
    294 }
    295 
    296 #[test]
    297 fn native_release_surfaces_remain_generated_outside_the_source_tree() {
    298     let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
    299     assert!(!root.join("radroots.lib.source-lock.v1.toml").exists());
    300     assert!(!root.join("radroots.service.source-lock.v1.toml").exists());
    301     assert!(!root.join("radroots.service.source-lock.v2.toml").exists());
    302     assert!(root.join("radroots.service.source-lock.v3.toml").is_file());
    303     assert!(
    304         !root
    305             .join("contracts/services_hardening/native_release.v1.json")
    306             .exists()
    307     );
    308     assert!(
    309         root.join("contracts/services_hardening/native_release.v2.json")
    310             .is_file()
    311     );
    312     assert!(
    313         root.join("contracts/services_hardening/native_release.v3.json")
    314             .is_file()
    315     );
    316     for forbidden in [
    317         ".github",
    318         "target",
    319         "result",
    320         "artifacts",
    321         "dist",
    322         "sbom.cdx.json",
    323         "provenance-input.v1.json",
    324         "oci-image.tar.gz",
    325     ] {
    326         assert!(
    327             !root.join(forbidden).exists(),
    328             "forbidden generated surface `{forbidden}` exists"
    329         );
    330     }
    331     assert!(!CONTRACT.contains("qualified"));
    332     assert!(!CONTRACT.contains("production_ready"));
    333     assert!(!CONTRACT.contains("oci-image"));
    334     assert!(!CONTRACT.contains("nixos-module"));
    335     let active: serde_json::Value =
    336         serde_json::from_str(ACTIVE_CONTRACT).expect("active release contract");
    337     assert_eq!(active["schema_version"], 3);
    338     assert_eq!(active["contract_version"], 3);
    339     assert_eq!(active["predecessor"]["filename"], "native_release.v2.json");
    340     assert_eq!(
    341         active["source_lock"]["filename"],
    342         "radroots.service.source-lock.v3.toml"
    343     );
    344     assert_eq!(active["source_lock"]["nix_material"], "qualified");
    345     assert_eq!(active["nix_outputs"]["bundled_sqlite"], true);
    346     assert_eq!(active["nix_outputs"]["native_linkage_count"], 1);
    347 }