Skip to content

Commit a2ecc4b

Browse files
authored
Merge pull request #1028 from jlebon/pr/verify-text
ostree-ext: surface libostree signature verification text
2 parents 04332ca + fdde8c6 commit a2ecc4b

File tree

6 files changed

+49
-58
lines changed

6 files changed

+49
-58
lines changed

lib/src/cli.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use crate::lints;
2828
use crate::progress_jsonl::{ProgressWriter, RawProgressFd};
2929
use crate::spec::Host;
3030
use crate::spec::ImageReference;
31-
use crate::utils::sigpolicy_from_opts;
31+
use crate::utils::sigpolicy_from_opt;
3232

3333
/// Shared progress options
3434
#[derive(Debug, Parser, PartialEq, Eq)]
@@ -111,10 +111,6 @@ pub(crate) struct SwitchOpts {
111111
#[clap(long)]
112112
pub(crate) enforce_container_sigpolicy: bool,
113113

114-
/// Enable verification via an ostree remote
115-
#[clap(long)]
116-
pub(crate) ostree_remote: Option<String>,
117-
118114
/// Don't create a new deployment, but directly mutate the booted state.
119115
/// This is hidden because it's not something we generally expect to be done,
120116
/// but this can be used in e.g. Anaconda %post to fixup
@@ -795,10 +791,7 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
795791
transport,
796792
name: opts.target.to_string(),
797793
};
798-
let sigverify = sigpolicy_from_opts(
799-
!opts.enforce_container_sigpolicy,
800-
opts.ostree_remote.as_deref(),
801-
);
794+
let sigverify = sigpolicy_from_opt(opts.enforce_container_sigpolicy);
802795
let target = ostree_container::OstreeImageReference { sigverify, imgref };
803796
let target = ImageReference::from(target);
804797
let prog: ProgressWriter = opts.progress.try_into()?;

lib/src/install.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ use crate::progress_jsonl::ProgressWriter;
5757
use crate::spec::ImageReference;
5858
use crate::store::Storage;
5959
use crate::task::Task;
60-
use crate::utils::{open_dir_noxdev, sigpolicy_from_opts};
60+
use crate::utils::{open_dir_noxdev, sigpolicy_from_opt};
6161

6262
/// The toplevel boot directory
6363
const BOOT: &str = "boot";
@@ -112,10 +112,6 @@ pub(crate) struct InstallTargetOpts {
112112
#[serde(default)]
113113
pub(crate) enforce_container_sigpolicy: bool,
114114

115-
/// Enable verification via an ostree remote
116-
#[clap(long)]
117-
pub(crate) target_ostree_remote: Option<String>,
118-
119115
/// By default, the accessiblity of the target image will be verified (just the manifest will be fetched).
120116
/// Specifying this option suppresses the check; use this when you know the issues it might find
121117
/// are addressed.
@@ -1216,10 +1212,7 @@ async fn prepare_install(
12161212
"Use of --target-no-signature-verification flag which is enabled by default"
12171213
);
12181214
}
1219-
let target_sigverify = sigpolicy_from_opts(
1220-
!target_opts.enforce_container_sigpolicy,
1221-
target_opts.target_ostree_remote.as_deref(),
1222-
);
1215+
let target_sigverify = sigpolicy_from_opt(target_opts.enforce_container_sigpolicy);
12231216
let target_imgname = target_opts
12241217
.target_imgref
12251218
.as_deref()

lib/src/utils.rs

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -172,16 +172,10 @@ pub(crate) fn spawn_editor(tmpf: &tempfile::NamedTempFile) -> Result<()> {
172172
}
173173

174174
/// Convert a combination of values (likely from CLI parsing) into a signature source
175-
pub(crate) fn sigpolicy_from_opts(
176-
disable_verification: bool,
177-
ostree_remote: Option<&str>,
178-
) -> SignatureSource {
179-
if disable_verification {
180-
SignatureSource::ContainerPolicyAllowInsecure
181-
} else if let Some(remote) = ostree_remote {
182-
SignatureSource::OstreeRemote(remote.to_owned())
183-
} else {
184-
SignatureSource::ContainerPolicy
175+
pub(crate) fn sigpolicy_from_opt(enforce_container_verification: bool) -> SignatureSource {
176+
match enforce_container_verification {
177+
true => SignatureSource::ContainerPolicy,
178+
false => SignatureSource::ContainerPolicyAllowInsecure,
185179
}
186180
}
187181

@@ -273,20 +267,9 @@ mod tests {
273267

274268
#[test]
275269
fn test_sigpolicy_from_opts() {
270+
assert_eq!(sigpolicy_from_opt(true), SignatureSource::ContainerPolicy);
276271
assert_eq!(
277-
sigpolicy_from_opts(false, None),
278-
SignatureSource::ContainerPolicy
279-
);
280-
assert_eq!(
281-
sigpolicy_from_opts(true, None),
282-
SignatureSource::ContainerPolicyAllowInsecure
283-
);
284-
assert_eq!(
285-
sigpolicy_from_opts(false, Some("foo")),
286-
SignatureSource::OstreeRemote("foo".to_owned())
287-
);
288-
assert_eq!(
289-
sigpolicy_from_opts(true, Some("foo")),
272+
sigpolicy_from_opt(false),
290273
SignatureSource::ContainerPolicyAllowInsecure
291274
);
292275
}

ostree-ext/src/cli.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,9 @@ async fn container_store(
896896
{
897897
eprintln!("{msg}")
898898
}
899+
if let Some(ref text) = import.verify_text {
900+
println!("{text}");
901+
}
899902
println!("Wrote: {} => {}", imgref, import.merge_commit);
900903
Ok(())
901904
}

ostree-ext/src/container/store.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,16 @@ pub struct LayeredImageState {
121121
pub merge_commit: String,
122122
/// The digest of the original manifest
123123
pub manifest_digest: Digest,
124-
/// The image manfiest
124+
/// The image manifest
125125
pub manifest: ImageManifest,
126126
/// The image configuration
127127
pub configuration: ImageConfiguration,
128128
/// Metadata for (cached, previously fetched) updates to the image, if any.
129129
pub cached_update: Option<CachedImageUpdate>,
130+
/// The signature verification text from libostree for the base commit;
131+
/// in the future we should probably instead just proxy a signature object
132+
/// instead, but this is sufficient for now.
133+
pub verify_text: Option<String>,
130134
}
131135

132136
impl LayeredImageState {
@@ -230,6 +234,8 @@ pub struct PreparedImport {
230234
pub ostree_commit_layer: Option<ManifestLayerState>,
231235
/// Any further non-ostree (derived) layers.
232236
pub layers: Vec<ManifestLayerState>,
237+
/// OSTree remote signature verification text, if enabled.
238+
pub verify_text: Option<String>,
233239
}
234240

235241
impl PreparedImport {
@@ -635,6 +641,7 @@ impl ImageImporter {
635641
ostree_layers: component_layers,
636642
ostree_commit_layer: commit_layer,
637643
layers: remaining_layers,
644+
verify_text: None,
638645
};
639646
Ok(Box::new(imp))
640647
}
@@ -704,7 +711,7 @@ impl ImageImporter {
704711
/// Extract the base ostree commit.
705712
#[context("Unencapsulating base")]
706713
pub(crate) async fn unencapsulate_base(
707-
&mut self,
714+
&self,
708715
import: &mut store::PreparedImport,
709716
require_ostree: bool,
710717
write_refs: bool,
@@ -804,17 +811,19 @@ impl ImageImporter {
804811
let blob = super::unencapsulate::decompressor(&media_type, blob)?;
805812
let mut archive = tar::Archive::new(blob);
806813
importer.import_commit(&mut archive, Some(cancellable))?;
807-
let commit = importer.finish_import_commit();
814+
let (commit, verify_text) = importer.finish_import_commit();
808815
if write_refs {
809816
repo.transaction_set_ref(None, &target_ref, Some(commit.as_str()));
810817
tracing::debug!("Wrote {} => {}", target_ref, commit);
811818
}
812819
repo.mark_commit_partial(&commit, false)?;
813820
txn.commit(Some(cancellable))?;
814-
Ok::<_, anyhow::Error>(commit)
821+
Ok::<_, anyhow::Error>((commit, verify_text))
815822
});
816-
let commit = super::unencapsulate::join_fetch(import_task, driver).await?;
823+
let (commit, verify_text) =
824+
super::unencapsulate::join_fetch(import_task, driver).await?;
817825
commit_layer.commit = Some(commit);
826+
import.verify_text = verify_text;
818827
if let Some(p) = self.layer_progress.as_ref() {
819828
p.send(ImportProgress::OstreeChunkCompleted(
820829
commit_layer.layer.clone(),
@@ -977,7 +986,7 @@ impl ImageImporter {
977986
.unwrap_or_else(|| chrono::offset::Utc::now().timestamp() as u64);
978987
// Destructure to transfer ownership to thread
979988
let repo = self.repo;
980-
let state = crate::tokio_util::spawn_blocking_cancellable_flatten(
989+
let mut state = crate::tokio_util::spawn_blocking_cancellable_flatten(
981990
move |cancellable| -> Result<Box<LayeredImageState>> {
982991
use rustix::fd::AsRawFd;
983992

@@ -1090,6 +1099,8 @@ impl ImageImporter {
10901099
},
10911100
)
10921101
.await?;
1102+
// We can at least avoid re-verifying the base commit.
1103+
state.verify_text = import.verify_text;
10931104
Ok(state)
10941105
}
10951106
}
@@ -1220,6 +1231,8 @@ pub fn query_image_commit(repo: &ostree::Repo, commit: &str) -> Result<Box<Layer
12201231
manifest,
12211232
configuration,
12221233
cached_update,
1234+
// we can't cross-reference with a remote here
1235+
verify_text: None,
12231236
});
12241237
tracing::debug!("Wrote merge commit {}", state.merge_commit);
12251238
Ok(state)

ostree-ext/src/tar/import.rs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ enum ImporterMode {
4747
pub(crate) struct Importer {
4848
repo: ostree::Repo,
4949
remote: Option<String>,
50+
verify_text: Option<String>,
5051
// Cache of xattrs, keyed by their content checksum.
5152
xattrs: HashMap<String, glib::Variant>,
5253
// Reusable buffer for xattrs references. It maps a file checksum (.0)
@@ -165,6 +166,7 @@ impl Importer {
165166
Self {
166167
repo: repo.clone(),
167168
remote,
169+
verify_text: None,
168170
buf: vec![0u8; 16384],
169171
xattrs: Default::default(),
170172
next_xattrs: None,
@@ -179,6 +181,7 @@ impl Importer {
179181
Self {
180182
repo: repo.clone(),
181183
remote: None,
184+
verify_text: None,
182185
buf: vec![0u8; 16384],
183186
xattrs: Default::default(),
184187
next_xattrs: None,
@@ -676,14 +679,17 @@ impl Importer {
676679

677680
// Now that we have both the commit and detached metadata in memory, verify that
678681
// the signatures in the detached metadata correctly sign the commit.
679-
self.repo
680-
.signature_verify_commit_data(
681-
remote,
682-
&commit.data_as_bytes(),
683-
&commitmeta.data_as_bytes(),
684-
ostree::RepoVerifyFlags::empty(),
685-
)
686-
.context("Verifying ostree commit in tar stream")?;
682+
self.verify_text = Some(
683+
self.repo
684+
.signature_verify_commit_data(
685+
remote,
686+
&commit.data_as_bytes(),
687+
&commitmeta.data_as_bytes(),
688+
ostree::RepoVerifyFlags::empty(),
689+
)
690+
.context("Verifying ostree commit in tar stream")?
691+
.into(),
692+
);
687693

688694
self.repo.mark_commit_partial(&checksum, true)?;
689695

@@ -738,10 +744,10 @@ impl Importer {
738744
Ok(())
739745
}
740746

741-
pub(crate) fn finish_import_commit(self) -> String {
747+
pub(crate) fn finish_import_commit(self) -> (String, Option<String>) {
742748
tracing::debug!("Import stats: {:?}", self.stats);
743749
match self.data {
744-
ImporterMode::Commit(c) => c.unwrap(),
750+
ImporterMode::Commit(c) => (c.unwrap(), self.verify_text),
745751
ImporterMode::ObjectSet(_) => unreachable!(),
746752
}
747753
}
@@ -821,7 +827,7 @@ pub async fn import_tar(
821827
let txn = repo.auto_transaction(Some(cancellable))?;
822828
let mut importer = Importer::new_for_commit(&repo, options.remote);
823829
importer.import_commit(&mut archive, Some(cancellable))?;
824-
let checksum = importer.finish_import_commit();
830+
let (checksum, _) = importer.finish_import_commit();
825831
txn.commit(Some(cancellable))?;
826832
repo.mark_commit_partial(&checksum, false)?;
827833
Ok::<_, anyhow::Error>(checksum)

0 commit comments

Comments
 (0)