Skip to content

Commit

Permalink
Remove the build_time method, use source_date for clamping all packag…
Browse files Browse the repository at this point in the history
…e times (#157)

* Remove the build_time method and rename source_date to source_date_epoch

* Introduce Timestamp type and make chrono dependency optional

* Use source_date_epoch to clamp signature timestamps

* Use impl Trait for arguments consistently

* Add RPMPackage::sign_with_timestamp method
  • Loading branch information
newpavlov authored Jun 14, 2023
1 parent 5941941 commit 9498e02
Show file tree
Hide file tree
Showing 17 changed files with 431 additions and 436 deletions.
13 changes: 8 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## Added
## Breaking Change

- `RPMBuilder::source_date()` method for clamping modification time of files. This allows reproducible builds when included files are re-generated (having the same content but with different mtimes).
- The `RPMBuilder::build_time` method is removed. Package build time is now included by default and can be clamped using the `RPMBuilder::source_date` method.

## Changed
## Added

- Build time is now included into package by default
- `RPMBuilder::source_date` method for clamping modification time of files, build time of the package, and signature time. This functionality is required for reproducible generation of packages.
- `RPMPackage::sign_with_timestamp` method.
- Package build time is now included by default.

### Fixed

- CentOS 7 support by using long sizes only for packages bigger than 4 GiB
- CentOS 7 support by using long sizes only for packages bigger than 4 GiB.
- Avoid a longstanding bug in `rpm --rebuilddb` by adding a package build time by default.

## 0.11.0

Expand Down
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ sha2 = "0.10"
md-5 = "0.10"
sha1 = "0.10"
pgp = { version = "0.10", optional = true }
chrono = "0.4"
chrono = { version = "0.4", optional = true }
log = "0.4"
itertools = "0.10"
hex = { version = "0.4", features = ["std"] }
Expand All @@ -61,11 +61,12 @@ env_logger = "0.10.0"
serial_test = "2.0"
pretty_assertions = "1.3.0"
gethostname = "0.4"
hex-literal = "0.4"

[features]
default = ["signature-pgp"]

signature-pgp = ["signature-meta", "pgp"]
signature-pgp = ["signature-meta", "pgp", "chrono"]
signature-meta = []

# Segregate tests that require podman to be installed
Expand Down
69 changes: 43 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,34 +29,51 @@ This library does not build software like rpmbuild. It is meant for finished art
### Examples

```rust
use rpm;
use rpm::signature::pgp::{Signer,Verifier};
use rpm::signature::pgp::{Signer, Verifier};

let raw_secret_key = std::fs::read("/path/to/gpg.secret.key")?;
let raw_secret_key = std::fs::read("./test_assets/secret_key.asc")?;
// It's recommended to use timestamp of last commit in your VCS
let source_date = 1_600_000_000;
let pkg = rpm::RPMBuilder::new("test", "1.0.0", "MIT", "x86_64", "some awesome package")
.compression(rpm::CompressionType::Zstd)
.with_file(
"./awesome-config.toml",
rpm::RPMFileOptions::new("/etc/awesome/config.toml").is_config(),
)?
// file mode is inherited from source file
.with_file(
"./awesome-bin",
rpm::RPMFileOptions::new("/usr/bin/awesome"),
)?
.with_file(
"./awesome-config.toml",
// you can set a custom mode and custom user too
rpm::RPMFileOptions::new("/etc/awesome/second.toml").mode(0o100744).user("hugo"),
)?
.pre_install_script("echo preinst")
.add_changelog_entry("Max Mustermann <[email protected]>", "- was awesome, eh?", chrono::DateTime::parse_from_rfc2822("Wed, 19 April 2023 23:16:09 GMT"))
.add_changelog_entry("Charlie Yom <[email protected]>", "- yeah, it was", chrono::DateTime::parse_from_rfc3339("1996-12-19T16:39:57-08:00"))
.requires(rpm::Dependency::any("wget"))
.vendor("corporation or individual")
.url("www.github.com/repo")
.vcs("git:repo=example_repo:branch=example_branch:sha=example_sha")
.build_and_sign(Signer::load_from_asc_bytes(&raw_secret_key)?);
.compression(rpm::CompressionType::Gzip)
.with_file(
"./test_assets/awesome.toml",
rpm::RPMFileOptions::new("/etc/awesome/config.toml").is_config(),
)?
// file mode is inherited from source file
.with_file(
"./test_assets/awesome.py",
rpm::RPMFileOptions::new("/usr/bin/awesome"),
)?
.with_file(
"./test_assets/awesome.toml",
// you can set a custom mode and custom user too
rpm::RPMFileOptions::new("/etc/awesome/second.toml")
.mode(rpm::FileMode::regular(0o644))
.user("hugo"),
)?
.pre_install_script("echo preinst")
// If you don't need reproducible builds,
// you can remove the following line
.source_date(source_date)
.build_host(gethostname::gethostname().to_str().unwrap_or("host"))
.add_changelog_entry(
"Max Mustermann <[email protected]> - 0.1-29",
"- was awesome, eh?",
chrono::DateTime::parse_from_rfc2822("Wed, 19 Apr 2023 23:16:09 GMT")
.expect("Date 1 is correct. qed"),
)
.add_changelog_entry(
"Charlie Yom <[email protected]> - 0.1-28",
"- yeah, it was",
// Raw timestamp for 1996-08-14 05:20:00
840_000_000,
)
.requires(rpm::Dependency::any("wget"))
.vendor("corporation or individual")
.url("www.github.com/repo")
.vcs("git:repo=example_repo:branch=example_branch:sha=example_sha")
.build_and_sign(Signer::load_from_asc_bytes(&raw_secret_key)?)?;

pkg.write_file("./awesome.rpm")?;

Expand Down
11 changes: 10 additions & 1 deletion src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::io;

use thiserror::Error;

use crate::DigestAlgorithm;
use crate::{DigestAlgorithm, TimestampError};

#[derive(Error, Debug)]
#[non_exhaustive]
Expand Down Expand Up @@ -82,6 +82,9 @@ pub enum RPMError {

#[error("invalid file mode {raw_mode} - {reason}")]
InvalidFileMode { raw_mode: i32, reason: &'static str },

#[error("timestamp conversion error: {0:?}")]
TimestampConv(TimestampError),
}

impl From<nom::Err<(&[u8], nom::error::ErrorKind)>> for RPMError {
Expand All @@ -94,3 +97,9 @@ impl From<nom::Err<(&[u8], nom::error::ErrorKind)>> for RPMError {
}
}
}

impl From<TimestampError> for RPMError {
fn from(error: TimestampError) -> Self {
RPMError::TimestampConv(error)
}
}
83 changes: 44 additions & 39 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
//! Signer,
//! Verifier
//! },
//! chrono::TimeZone,
//! };
//! use std::str::FromStr;
//!
Expand All @@ -21,48 +20,54 @@
//! # #[cfg(feature = "signature-pgp")]
//! # {
//! let raw_secret_key = std::fs::read("./test_assets/secret_key.asc")?;
//! // Use date of last commit in your VCS for reproducible builds
//! let source_date = chrono::Utc::now();
//! // It's recommended to use timestamp of last commit in your VCS
//! let source_date = 1_600_000_000;
//! let pkg = rpm::RPMBuilder::new("test", "1.0.0", "MIT", "x86_64", "some awesome package")
//! .compression(rpm::CompressionType::Gzip)
//! .with_file(
//! "./test_assets/awesome.toml",
//! rpm::RPMFileOptions::new("/etc/awesome/config.toml")
//! .is_config(),
//! )?
//! // file mode is inherited from source file
//! .with_file(
//! "./test_assets/awesome.py",
//! rpm::RPMFileOptions::new("/usr/bin/awesome"),
//! )?
//! .with_file(
//! "./test_assets/awesome.toml",
//! // you can set a custom mode and custom user too
//! rpm::RPMFileOptions::new("/etc/awesome/second.toml")
//! .mode(rpm::FileMode::regular(0o644))
//! .user("hugo"),
//! )?
//! .pre_install_script("echo preinst")
//! // You can remove the following two methods,
//! // if you don't need reproducible builds
//! .build_time(source_date)
//! .source_date(source_date)
//! .build_host(gethostname::gethostname().to_str().expect("Hostname works. qed").to_string())
//! .add_changelog_entry("Max Mustermann <[email protected]> - 0.1-29", "- was awesome, eh?", chrono::DateTime::parse_from_rfc2822("Wed, 19 Apr 2023 23:16:09 GMT").expect("Date 1 is correct. qed"))
//! .add_changelog_entry("Charlie Yom <[email protected]> - 0.1-28", "- yeah, it was", chrono::DateTime::parse_from_rfc3339("1996-12-19T16:39:57-08:00").expect("Date 2 is corrrect. qed"))
//! .requires(rpm::Dependency::any("wget"))
//! .vendor("corporation or individual")
//! .url("www.github.com/repo")
//! .vcs("git:repo=example_repo:branch=example_branch:sha=example_sha")
//! .build_and_sign(
//! Signer::load_from_asc_bytes(&raw_secret_key)?
//! )?;
//! let mut f = std::fs::File::create("./target/awesome.rpm")?;
//! .compression(rpm::CompressionType::Gzip)
//! .with_file(
//! "./test_assets/awesome.toml",
//! rpm::RPMFileOptions::new("/etc/awesome/config.toml").is_config(),
//! )?
//! // file mode is inherited from source file
//! .with_file(
//! "./test_assets/awesome.py",
//! rpm::RPMFileOptions::new("/usr/bin/awesome"),
//! )?
//! .with_file(
//! "./test_assets/awesome.toml",
//! // you can set a custom mode and custom user too
//! rpm::RPMFileOptions::new("/etc/awesome/second.toml")
//! .mode(rpm::FileMode::regular(0o644))
//! .user("hugo"),
//! )?
//! .pre_install_script("echo preinst")
//! // If you don't need reproducible builds,
//! // you can remove the following line
//! .source_date(source_date)
//! .build_host(gethostname::gethostname().to_str().unwrap_or("host"))
//! .add_changelog_entry(
//! "Max Mustermann <[email protected]> - 0.1-29",
//! "- was awesome, eh?",
//! chrono::DateTime::parse_from_rfc2822("Wed, 19 Apr 2023 23:16:09 GMT")
//! .expect("Date 1 is correct. qed"),
//! )
//! .add_changelog_entry(
//! "Charlie Yom <[email protected]> - 0.1-28",
//! "- yeah, it was",
//! // Raw timestamp for 1996-08-14 05:20:00
//! 840_000_000,
//! )
//! .requires(rpm::Dependency::any("wget"))
//! .vendor("corporation or individual")
//! .url("www.github.com/repo")
//! .vcs("git:repo=example_repo:branch=example_branch:sha=example_sha")
//! .build_and_sign(Signer::load_from_asc_bytes(&raw_secret_key)?)?;
//! let mut f = std::fs::File::create("/tmp/awesome.rpm")?;
//! pkg.write(&mut f)?;
//!
//! // reading
//! let raw_pub_key = std::fs::read("./test_assets/public_key.asc")?;
//! let pkg = rpm::RPMPackage::open("./target/awesome.rpm")?;
//! let raw_pub_key = std::fs::read("test_assets/public_key.asc")?;
//! let pkg = rpm::RPMPackage::open("/tmp/awesome.rpm")?;
//! // verifying
//! pkg.verify_signature(Verifier::load_from_asc_bytes(&raw_pub_key)?)?;
//! # }
Expand Down
Loading

0 comments on commit 9498e02

Please sign in to comment.