-
Notifications
You must be signed in to change notification settings - Fork 83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sign EIFs separately #652
Sign EIFs separately #652
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,8 @@ pub mod utils; | |
|
||
use aws_nitro_enclaves_image_format::defs::eif_hasher::EifHasher; | ||
use aws_nitro_enclaves_image_format::utils::eif_reader::EifReader; | ||
use aws_nitro_enclaves_image_format::utils::eif_signer::EifSigner; | ||
use aws_nitro_enclaves_image_format::utils::SignKeyData; | ||
use aws_nitro_enclaves_image_format::{generate_build_info, utils::get_pcrs}; | ||
use log::{debug, info}; | ||
use sha2::{Digest, Sha384}; | ||
|
@@ -25,9 +27,9 @@ use std::convert::TryFrom; | |
use std::fs::{File, OpenOptions}; | ||
use std::io::{self, Read, Write}; | ||
use std::os::unix::net::UnixStream; | ||
use std::path::PathBuf; | ||
use std::path::{Path, PathBuf}; | ||
|
||
use common::commands_parser::{BuildEnclavesArgs, EmptyArgs, RunEnclavesArgs}; | ||
use common::commands_parser::{BuildEnclavesArgs, EmptyArgs, RunEnclavesArgs, SignEifArgs}; | ||
use common::json_output::{ | ||
EifDescribeInfo, EnclaveBuildInfo, EnclaveTerminateInfo, MetadataDescribeInfo, | ||
}; | ||
|
@@ -292,6 +294,62 @@ pub fn describe_eif(eif_path: String) -> NitroCliResult<EifDescribeInfo> { | |
Ok(info) | ||
} | ||
|
||
/// Signs EIF with the given key and certificate. If EIF already has a signature, it will be replaced. | ||
pub fn sign_eif(args: SignEifArgs) -> NitroCliResult<()> { | ||
let sign_info = match (&args.private_key, &args.signing_certificate) { | ||
(Some(key), Some(cert)) => SignKeyData::new(key, Path::new(&cert)).map_or_else( | ||
|e| { | ||
eprintln!("Could not read signing info: {:?}", e); | ||
None | ||
}, | ||
Some, | ||
), | ||
_ => None, | ||
}; | ||
|
||
let signer = EifSigner::new(sign_info).ok_or_else(|| { | ||
new_nitro_cli_failure!( | ||
format!("Failed to create EifSigner"), | ||
NitroCliErrorEnum::EIFSigningError | ||
) | ||
})?; | ||
|
||
signer.sign_image(&args.eif_path).map_err(|e| { | ||
new_nitro_cli_failure!( | ||
format!("Failed to sign image: {}", e), | ||
NitroCliErrorEnum::EIFSigningError | ||
) | ||
})?; | ||
|
||
eprintln!("Enclave Image successfully signed."); | ||
|
||
let mut eif_reader = EifReader::from_eif(args.eif_path).map_err(|e| { | ||
new_nitro_cli_failure!( | ||
&format!("Failed to initialize EIF reader: {:?}", e), | ||
NitroCliErrorEnum::EifParsingError | ||
) | ||
})?; | ||
eif_reader | ||
.get_measurements() | ||
.map_err(|e| { | ||
new_nitro_cli_failure!( | ||
&format!("Failed to get PCR values: {:?}", e), | ||
NitroCliErrorEnum::EifParsingError | ||
) | ||
}) | ||
.and_then(|measurements| { | ||
let info = EnclaveBuildInfo::new(measurements); | ||
let printed_info = serde_json::to_string_pretty(&info).map_err(|err| { | ||
new_nitro_cli_failure!( | ||
&format!("Failed to display EnclaveBuild data: {:?}", err), | ||
NitroCliErrorEnum::SerdeError | ||
) | ||
})?; | ||
println!("{}", printed_info); | ||
Ok(()) | ||
}) | ||
} | ||
|
||
/// Returns the value of the `NITRO_CLI_BLOBS` environment variable. | ||
/// | ||
/// This variable specifies where all the blobs necessary for building | ||
|
@@ -803,5 +861,26 @@ macro_rules! create_app { | |
.required(true), | ||
), | ||
) | ||
.subcommand( | ||
Command::new("sign-eif") | ||
.about("Sign EIF with the given key") | ||
.arg( | ||
Arg::new("eif-path") | ||
.long("eif-path") | ||
.help("Path pointing to a prebuilt Eif image") | ||
) | ||
.arg( | ||
Arg::new("signing-certificate") | ||
.long("signing-certificate") | ||
.help("Local path to developer's X509 signing certificate.") | ||
.requires("private-key"), | ||
) | ||
.arg( | ||
Arg::new("private-key") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have concerns about including this feature in the new command. This approach to handling private keys doesn't follow security best practices. While I understand the desire to maintain consistency with the If there are compelling reasons to keep it, we should:
CC: @eugkoira There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My vision is, we might have customers who build EIFs in one (insecure) environment, and then sign those EIFs separately in a secure environment with a local private key, similar to #639. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't get it, is the proposal to not have the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We all know what could happen if a private key is leaked. :) Given the security risks associated with private key exposure, the users should be encouraged to use KMS as the recommended best practice. (Or using PKCS#11 in the future. This was also mentioned by @dwmw2 here.) I believe there are two options:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Customers can still have a secure isolated environment for signing with a private key like in #639. Why should we restrict their possibilities? We can specify in our documentation the recommended way but I don't understand the urge to remove it completely. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Customers can literally use Nitro Enclaves to receive the signing key from KMS and pass it as parameter here, right? I don't believe the tool should per se assume an insecure environment. To clarify environmental requirements and establish best practices is the task of our documentation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Signing requires a private key. That's kind of fundamental. The question is where that private key is found. The real problem here seems to be that we have two completely separate command line arguments to provide the private key. There's the And then we have the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that having a single parameter which dispatches internally basing on the provided value is the best option in terms of UX, and good thing that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Excellent. So let's fix that to use |
||
.long("private-key") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we decide to retain the local signing option, this should be --private-key-path for clarity. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it will be inconsistent with |
||
.help("KMS key ARN or local path to developer's Eliptic Curve private key.") | ||
.requires("signing-certificate"), | ||
) | ||
) | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we decide to retain the local signing option, this should be --signing-certificate-path for clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How I see it, the parameter names shall be the same as the ones used when signing an image during build. Updating syntax for
build-enclave
is a breaking change unless we keep both the old parameter name and the new one, plus add a deprecation warning.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I vote for consistency with what we already have
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is what I meant. To clarify,
--signing-certificate
or--private-key
should remain in place until the next major release ofnitro-cli
.