refactor: Use GITHUB_TOKEN instead of REGISTRY_TOKEN (#75)

To keep things as consistent as possible, we're switching to using the
GITHUB_TOKEN env var for login instead. Env vars were also all pulled
out into their own constants to make things more consistent.

This change also includes prioritizing public/private key signing over
OIDC keyless for GitHub for an easier transition. It would require the
user to delete their `cosign.pub` file from their repo in order to start
using the keyless method.
This commit is contained in:
Gerald Pinder 2024-02-19 23:30:39 -05:00 committed by GitHub
parent 411e782dc8
commit 54742ecaa1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 165 additions and 129 deletions

View file

@ -1,6 +1,3 @@
use crate::module_recipe::Recipe;
use crate::shadow;
use askama::Template;
use clap::Args;
use clap_complete::Shell;
@ -14,10 +11,7 @@ use typed_builder::TypedBuilder;
use super::utils::exec_cmd;
use super::BlueBuildCommand;
const UNKNOWN_SHELL: &str = "<unknown shell>";
const UNKNOWN_VERSION: &str = "<unknown version>";
const UNKNOWN_TERMINAL: &str = "<unknown terminal>";
const GITHUB_CHAR_LIMIT: usize = 8100; // Magic number accepted by Github
use crate::{constants::*, module_recipe::Recipe, shadow};
#[derive(Default, Debug, Clone, TypedBuilder, Args)]
pub struct BugReportRecipe {
@ -232,12 +226,12 @@ struct TerminalInfo {
}
fn get_terminal_info() -> TerminalInfo {
let terminal = std::env::var("TERM_PROGRAM")
.or_else(|_| std::env::var("LC_TERMINAL"))
let terminal = std::env::var(TERM_PROGRAM)
.or_else(|_| std::env::var(LC_TERMINAL))
.unwrap_or_else(|_| UNKNOWN_TERMINAL.to_string());
let version = std::env::var("TERM_PROGRAM_VERSION")
.or_else(|_| std::env::var("LC_TERMINAL_VERSION"))
let version = std::env::var(TERM_PROGRAM_VERSION)
.or_else(|_| std::env::var(LC_TERMINAL_VERSION))
.unwrap_or_else(|_| UNKNOWN_VERSION.to_string());
TerminalInfo {

View file

@ -34,12 +34,7 @@ use tokio::{
sync::oneshot::{self, Sender},
};
use crate::{
commands::template::TemplateCommand,
constants::{GITHUB_TOKEN_ISSUER_URL, RECIPE_PATH},
module_recipe::Recipe,
ops::{self, ARCHIVE_SUFFIX},
};
use crate::{commands::template::TemplateCommand, constants::*, module_recipe::Recipe, ops};
use super::BlueBuildCommand;
@ -210,8 +205,6 @@ impl BuildCommand {
use futures_util::StreamExt;
use signal_hook::consts::{SIGINT, SIGQUIT, SIGTERM};
use crate::ops::BUILD_ID_LABEL;
trace!("BuildCommand::build_image({client:#?})");
let credentials = self.get_login_creds();
@ -415,12 +408,12 @@ impl BuildCommand {
)
} else {
match (
env::var("CI_REGISTRY").ok().map(|s| s.to_lowercase()),
env::var("CI_PROJECT_NAMESPACE")
env::var(CI_REGISTRY).ok().map(|s| s.to_lowercase()),
env::var(CI_PROJECT_NAMESPACE)
.ok()
.map(|s| s.to_lowercase()),
env::var("CI_PROJECT_NAME").ok().map(|s| s.to_lowercase()),
env::var("GITHUB_REPOSITORY_OWNER")
env::var(CI_PROJECT_NAME).ok().map(|s| s.to_lowercase()),
env::var(GITHUB_REPOSITORY_OWNER)
.ok()
.map(|s| s.to_lowercase()),
self.registry.as_ref().map(|s| s.to_lowercase()),
@ -530,8 +523,8 @@ impl BuildCommand {
fn get_login_creds(&self) -> Option<Credentials> {
let registry = match (
self.registry.as_ref(),
env::var("CI_REGISTRY").ok(),
env::var("GITHUB_ACTIONS").ok(),
env::var(CI_REGISTRY).ok(),
env::var(GITHUB_ACTIONS).ok(),
) {
(Some(registry), _, _) => registry.to_owned(),
(None, Some(ci_registry), None) => ci_registry,
@ -541,8 +534,8 @@ impl BuildCommand {
let username = match (
self.username.as_ref(),
env::var("CI_REGISTRY_USER").ok(),
env::var("GITHUB_ACTOR").ok(),
env::var(CI_REGISTRY_USER).ok(),
env::var(GITHUB_ACTOR).ok(),
) {
(Some(username), _, _) => username.to_owned(),
(None, Some(ci_registry_user), None) => ci_registry_user,
@ -552,8 +545,8 @@ impl BuildCommand {
let password = match (
self.password.as_ref(),
env::var("CI_REGISTRY_PASSWORD").ok(),
env::var("REGISTRY_TOKEN").ok(),
env::var(CI_REGISTRY_PASSWORD).ok(),
env::var(GITHUB_TOKEN).ok(),
) {
(Some(password), _, _) => password.to_owned(),
(None, Some(ci_registry_password), None) => ci_registry_password,
@ -587,17 +580,17 @@ fn sign_images(image_name: &str, tag: Option<&str>) -> Result<()> {
.unwrap_or_else(|| image_name.to_owned());
match (
env::var("CI_DEFAULT_BRANCH"),
env::var("CI_COMMIT_REF_NAME"),
env::var("CI_PROJECT_URL"),
env::var("CI_SERVER_PROTOCOL"),
env::var("CI_SERVER_HOST"),
env::var("SIGSTORE_ID_TOKEN"),
env::var("REGISTRY_TOKEN"),
env::var("GITHUB_EVENT_NAME"),
env::var("GITHUB_REF_NAME"),
env::var("GITHUB_WORKFLOW_REF"),
env::var("COSIGN_PRIVATE_KEY"),
env::var(CI_DEFAULT_BRANCH),
env::var(CI_COMMIT_REF_NAME),
env::var(CI_PROJECT_URL),
env::var(CI_SERVER_PROTOCOL),
env::var(CI_SERVER_HOST),
env::var(SIGSTORE_ID_TOKEN),
env::var(GITHUB_TOKEN),
env::var(GITHUB_EVENT_NAME),
env::var(GITHUB_REF_NAME),
env::var(GITHUB_WORKFLOW_REF),
env::var(COSIGN_PRIVATE_KEY),
) {
(
Ok(ci_default_branch),
@ -658,7 +651,56 @@ fn sign_images(image_name: &str, tag: Option<&str>) -> Result<()> {
_,
_,
_,
Ok(registry_token),
_,
Ok(github_event_name),
Ok(github_ref_name),
_,
Ok(cosign_private_key),
) if github_event_name != "pull_request"
&& (github_ref_name == "live" || github_ref_name == "main")
&& !cosign_private_key.is_empty()
&& Path::new(COSIGN_PATH).exists() =>
{
trace!("GITHUB_EVENT_NAME={github_event_name}, GITHUB_REF_NAME={github_ref_name}");
debug!("On live branch");
info!("Signing image: {image_digest}");
trace!("cosign sign --key=env://COSIGN_PRIVATE_KEY {image_digest}");
if Command::new("cosign")
.arg("sign")
.arg("--key=env://COSIGN_PRIVATE_KEY")
.arg(&image_digest)
.status()?
.success()
{
info!("Successfully signed image!");
} else {
bail!("Failed to sign image: {image_digest}");
}
trace!("cosign verify --key {COSIGN_PATH} {image_name_tag}");
if !Command::new("cosign")
.arg("verify")
.arg(format!("--key={COSIGN_PATH}"))
.arg(&image_name_tag)
.status()?
.success()
{
bail!("Failed to verify image!");
}
}
(
_,
_,
_,
_,
_,
_,
Ok(_),
Ok(github_event_name),
Ok(github_ref_name),
Ok(github_worflow_ref),
@ -668,8 +710,6 @@ fn sign_images(image_name: &str, tag: Option<&str>) -> Result<()> {
{
trace!("GITHUB_EVENT_NAME={github_event_name}, GITHUB_REF_NAME={github_ref_name}, GITHUB_WORKFLOW_REF={github_worflow_ref}");
env::set_var("GITHUB_TOKEN", registry_token);
debug!("On {github_ref_name} branch");
info!("Signing image {image_digest}");
@ -700,54 +740,6 @@ fn sign_images(image_name: &str, tag: Option<&str>) -> Result<()> {
bail!("Failed to verify image!");
}
}
(
_,
_,
_,
_,
_,
_,
_,
Ok(github_event_name),
Ok(github_ref_name),
_,
Ok(cosign_private_key),
) if github_event_name != "pull_request"
&& (github_ref_name == "live" || github_ref_name == "main")
&& !cosign_private_key.is_empty() =>
{
trace!("GITHUB_EVENT_NAME={github_event_name}, GITHUB_REF_NAME={github_ref_name}");
debug!("On live branch");
info!("Signing image: {image_digest}");
trace!("cosign sign --key=env://COSIGN_PRIVATE_KEY {image_digest}");
if Command::new("cosign")
.arg("sign")
.arg("--key=env://COSIGN_PRIVATE_KEY")
.arg(&image_digest)
.status()?
.success()
{
info!("Successfully signed image!");
} else {
bail!("Failed to sign image: {image_digest}");
}
trace!("cosign verify --key ./cosign.pub {image_name_tag}");
if !Command::new("cosign")
.arg("verify")
.arg("--key=./cosign.pub")
.arg(&image_name_tag)
.status()?
.success()
{
bail!("Failed to verify image!");
}
}
_ => debug!("Not running in CI with cosign variables, not signing"),
}
@ -782,14 +774,14 @@ fn check_cosign_files() -> Result<()> {
trace!("check_for_cosign_files()");
match (
env::var("GITHUB_EVENT_NAME").ok(),
env::var("GITHUB_REF_NAME").ok(),
env::var("COSIGN_PRIVATE_KEY").ok(),
env::var(GITHUB_EVENT_NAME).ok(),
env::var(GITHUB_REF_NAME).ok(),
env::var(COSIGN_PRIVATE_KEY).ok(),
) {
(Some(github_event_name), Some(github_ref_name), Some(_))
if github_event_name != "pull_request"
&& (github_ref_name == "live" || github_ref_name == "main")
&& Path::new("cosign.pub").exists() =>
&& Path::new(COSIGN_PATH).exists() =>
{
env::set_var("COSIGN_PASSWORD", "");
env::set_var("COSIGN_YES", "true");
@ -809,18 +801,18 @@ fn check_cosign_files() -> Result<()> {
}
let calculated_pub_key = String::from_utf8(output.stdout)?;
let found_pub_key = fs::read_to_string("./cosign.pub")?;
let found_pub_key = fs::read_to_string(COSIGN_PATH)?;
trace!("calculated_pub_key={calculated_pub_key},found_pub_key={found_pub_key}");
if calculated_pub_key.trim() == found_pub_key.trim() {
debug!("Cosign files match, continuing build");
Ok(())
} else {
bail!("Public key 'cosign.pub' does not match private key")
bail!("Public key '{COSIGN_PATH}' does not match private key")
}
}
_ => {
debug!("Not building on live branch or cosign.pub doesn't exist, skipping cosign file check");
debug!("Not building on live branch or {COSIGN_PATH} doesn't exist, skipping cosign file check");
Ok(())
}
}
@ -843,8 +835,6 @@ async fn handle_signals(
use signal_hook::consts::{SIGHUP, SIGINT};
use tokio::time::{self, Duration};
use crate::ops::BUILD_ID_LABEL;
trace!("handle_signals(signals, {build_id}, {client:#?})");
while let Some(signal) = signals.next().await {

View file

@ -6,7 +6,7 @@ use std::{
use anyhow::{bail, Result};
use log::trace;
use crate::ops;
use crate::{constants::*, ops};
#[cfg(feature = "podman-api")]
#[derive(Debug, Clone, Default)]
@ -25,10 +25,10 @@ impl BuildStrategy {
Ok(
match (
env::var("XDG_RUNTIME_DIR"),
PathBuf::from("/run/podman/podman.sock"),
PathBuf::from("/var/run/podman/podman.sock"),
PathBuf::from("/var/run/podman.sock"),
env::var(XDG_RUNTIME_DIR),
PathBuf::from(RUN_PODMAN_SOCK),
PathBuf::from(VAR_RUN_PODMAN_PODMAN_SOCK),
PathBuf::from(VAR_RUN_PODMAN_SOCK),
ops::check_command_exists("buildah"),
ops::check_command_exists("podman"),
) {

View file

@ -12,8 +12,9 @@ use users::{Users, UsersCache};
use crate::{
commands::build::BuildCommand,
constants::{ARCHIVE_SUFFIX, LOCAL_BUILD},
module_recipe::Recipe,
ops::{self, ARCHIVE_SUFFIX, LOCAL_BUILD},
ops,
};
use super::BlueBuildCommand;

View file

@ -12,7 +12,7 @@ use typed_builder::TypedBuilder;
use uuid::Uuid;
use crate::{
constants::{self},
constants::*,
module_recipe::{Module, Recipe},
};
@ -60,7 +60,7 @@ impl BlueBuildCommand for TemplateCommand {
"Templating for recipe at {}",
self.recipe
.clone()
.unwrap_or_else(|| PathBuf::from(constants::RECIPE_PATH))
.unwrap_or_else(|| PathBuf::from(RECIPE_PATH))
.display()
);
@ -77,7 +77,7 @@ impl TemplateCommand {
let recipe_path = self
.recipe
.clone()
.unwrap_or_else(|| PathBuf::from(constants::RECIPE_PATH));
.unwrap_or_else(|| PathBuf::from(RECIPE_PATH));
debug!("Deserializing recipe");
let recipe_de = Recipe::parse(&recipe_path)?;
@ -133,7 +133,7 @@ fn print_script(script_contents: &ExportsTemplate) -> String {
fn has_cosign_file() -> bool {
trace!("has_cosign_file()");
std::env::current_dir()
.map(|p| p.join(constants::COSIGN_PATH).exists())
.map(|p| p.join(COSIGN_PATH).exists())
.unwrap_or(false)
}
@ -208,16 +208,16 @@ fn get_files_list(module: &Module) -> Option<Vec<(String, String)>> {
}
fn get_github_repo_owner() -> Option<String> {
Some(env::var("GITHUB_REPOSITORY_OWNER").ok()?.to_lowercase())
Some(env::var(GITHUB_REPOSITORY_OWNER).ok()?.to_lowercase())
}
fn get_gitlab_registry_path() -> Option<String> {
Some(
format!(
"{}/{}/{}",
env::var("CI_REGISTRY").ok()?,
env::var("CI_PROJECT_NAMESPACE").ok()?,
env::var("CI_PROJECT_NAME").ok()?,
env::var(CI_REGISTRY).ok()?,
env::var(CI_PROJECT_NAMESPACE).ok()?,
env::var(CI_PROJECT_NAME).ok()?,
)
.to_lowercase(),
)

View file

@ -1,4 +1,56 @@
// Paths
pub const ARCHIVE_SUFFIX: &str = "tar.gz";
pub const COSIGN_PATH: &str = "./cosign.pub";
pub const LOCAL_BUILD: &str = "/etc/bluebuild";
pub const MODULES_PATH: &str = "./config/modules";
pub const RECIPE_PATH: &str = "./config/recipe.yml";
pub const RUN_PODMAN_SOCK: &str = "/run/podman/podman.sock";
pub const VAR_RUN_PODMAN_PODMAN_SOCK: &str = "/var/run/podman/podman.sock";
pub const VAR_RUN_PODMAN_SOCK: &str = "/var/run/podman.sock";
// Labels
pub const BUILD_ID_LABEL: &str = "org.blue-build.build-id";
// Cosign vars
pub const COSIGN_PRIVATE_KEY: &str = "COSIGN_PRIVATE_KEY";
pub const GITHUB_TOKEN_ISSUER_URL: &str = "https://token.actions.githubusercontent.com";
pub const SIGSTORE_ID_TOKEN: &str = "SIGSTORE_ID_TOKEN";
// GitHub CI vars
pub const GITHUB_ACTIONS: &str = "GITHUB_ACTIONS";
pub const GITHUB_ACTOR: &str = "GITHUB_ACTOR";
pub const GITHUB_EVENT_NAME: &str = "GITHUB_EVENT_NAME";
pub const GITHUB_REF_NAME: &str = "GITHUB_REF_NAME";
pub const GITHUB_REPOSITORY_OWNER: &str = "GITHUB_REPOSITORY_OWNER";
pub const GITHUB_SHA: &str = "GITHUB_SHA";
pub const GITHUB_TOKEN: &str = "GITHUB_TOKEN";
pub const GITHUB_WORKFLOW_REF: &str = "GITHUB_WORKFLOW_REF";
pub const PR_EVENT_NUMBER: &str = "PR_EVENT_NUMBER";
// GitLab CI vars
pub const CI_COMMIT_REF_NAME: &str = "CI_COMMIT_REF_NAME";
pub const CI_COMMIT_SHORT_SHA: &str = "CI_COMMIT_SHORT_SHA";
pub const CI_DEFAULT_BRANCH: &str = "CI_DEFAULT_BRANCH";
pub const CI_MERGE_REQUEST_IID: &str = "CI_MERGE_REQUEST_IID";
pub const CI_PIPELINE_SOURCE: &str = "CI_PIPELINE_SOURCE";
pub const CI_PROJECT_NAME: &str = "CI_PROJECT_NAME";
pub const CI_PROJECT_NAMESPACE: &str = "CI_PROJECT_NAMESPACE";
pub const CI_PROJECT_URL: &str = "CI_PROJECT_URL";
pub const CI_SERVER_HOST: &str = "CI_SERVER_HOST";
pub const CI_SERVER_PROTOCOL: &str = "CI_SERVER_PROTOCOL";
pub const CI_REGISTRY: &str = "CI_REGISTRY";
pub const CI_REGISTRY_PASSWORD: &str = "CI_REGISTRY_PASSWORD";
pub const CI_REGISTRY_USER: &str = "CI_REGISTRY_USER";
// Terminal vars
pub const TERM_PROGRAM: &str = "TERM_PROGRAM";
pub const LC_TERMINAL: &str = "LC_TERMINAL";
pub const TERM_PROGRAM_VERSION: &str = "TERM_PROGRAM_VERSION";
pub const LC_TERMINAL_VERSION: &str = "LC_TERMINAL_VERSION";
pub const XDG_RUNTIME_DIR: &str = "XDG_RUNTIME_DIR";
// Misc
pub const UNKNOWN_SHELL: &str = "<unknown shell>";
pub const UNKNOWN_VERSION: &str = "<unknown version>";
pub const UNKNOWN_TERMINAL: &str = "<unknown terminal>";
pub const GITHUB_CHAR_LIMIT: usize = 8100; // Magic number accepted by Github

View file

@ -16,7 +16,10 @@ use serde_json::Value as JsonValue;
use serde_yaml::Value;
use typed_builder::TypedBuilder;
use crate::ops::{self, check_command_exists};
use crate::{
constants::*,
ops::{self, check_command_exists},
};
#[derive(Default, Serialize, Clone, Deserialize, Debug, TypedBuilder)]
pub struct Recipe<'a> {
@ -57,15 +60,15 @@ impl<'a> Recipe<'a> {
let timestamp = Local::now().format("%Y%m%d").to_string();
if let (Ok(commit_branch), Ok(default_branch), Ok(commit_sha), Ok(pipeline_source)) = (
env::var("CI_COMMIT_REF_NAME"),
env::var("CI_DEFAULT_BRANCH"),
env::var("CI_COMMIT_SHORT_SHA"),
env::var("CI_PIPELINE_SOURCE"),
env::var(CI_COMMIT_REF_NAME),
env::var(CI_DEFAULT_BRANCH),
env::var(CI_COMMIT_SHORT_SHA),
env::var(CI_PIPELINE_SOURCE),
) {
trace!("CI_COMMIT_REF_NAME={commit_branch}, CI_DEFAULT_BRANCH={default_branch},CI_COMMIT_SHORT_SHA={commit_sha}, CI_PIPELINE_SOURCE={pipeline_source}");
warn!("Detected running in Gitlab, pulling information from CI variables");
if let Ok(mr_iid) = env::var("CI_MERGE_REQUEST_IID") {
if let Ok(mr_iid) = env::var(CI_MERGE_REQUEST_IID) {
trace!("CI_MERGE_REQUEST_IID={mr_iid}");
if pipeline_source == "merge_request_event" {
debug!("Running in a MR");
@ -91,10 +94,10 @@ impl<'a> Recipe<'a> {
Ok(github_sha),
Ok(github_ref_name),
) = (
env::var("GITHUB_EVENT_NAME"),
env::var("PR_EVENT_NUMBER"),
env::var("GITHUB_SHA"),
env::var("GITHUB_REF_NAME"),
env::var(GITHUB_EVENT_NAME),
env::var(PR_EVENT_NUMBER),
env::var(GITHUB_SHA),
env::var(GITHUB_REF_NAME),
) {
trace!("GITHUB_EVENT_NAME={github_event_name},PR_EVENT_NUMBER={github_event_number},GITHUB_SHA={github_sha},GITHUB_REF_NAME={github_ref_name}");
warn!("Detected running in Github, pulling information from GITHUB variables");

View file

@ -3,10 +3,6 @@ use format_serde_error::SerdeError;
use log::{debug, trace};
use std::process::Command;
pub const LOCAL_BUILD: &str = "/etc/bluebuild";
pub const ARCHIVE_SUFFIX: &str = "tar.gz";
pub const BUILD_ID_LABEL: &str = "org.blue-build.build-id";
pub fn check_command_exists(command: &str) -> Result<()> {
trace!("check_command_exists({command})");
debug!("Checking if {command} exists");

View file

@ -18,7 +18,7 @@ RUN printf {{ self::print_script(export_script) }} >> /exports.sh && chmod +x /e
FROM {{ recipe.base_image }}:{{ recipe.image_version }}
LABEL {{ crate::ops::BUILD_ID_LABEL }}="{{ build_id }}"
LABEL {{ crate::constants::BUILD_ID_LABEL }}="{{ build_id }}"
LABEL org.opencontainers.image.title="{{ recipe.name }}"
LABEL org.opencontainers.image.description="{{ recipe.description }}"
LABEL io.artifacthub.package.readme-url=https://raw.githubusercontent.com/blue-build/cli/main/README.md