Skip to content

Commit f20abfd

Browse files
committed
Allow signing EIFs with KMS while building
This commit updates `nitro-cli` to use `aws-nitro-enclaves-image-format` v0.4 which allows to sign EIF during build using a key from KMS. In order to do so, user needs to specify KMS key ARN and region instead of the local private key file. Signed-off-by: Mark Kirichenko <[email protected]>
1 parent 59beb80 commit f20abfd

File tree

10 files changed

+179
-22
lines changed

10 files changed

+179
-22
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ page_size = "0.6"
2020
signal-hook = "0.3"
2121
serde_cbor = "0.11"
2222
driver-bindings = { path = "./driver-bindings" }
23-
aws-nitro-enclaves-image-format = "0.2"
23+
aws-nitro-enclaves-image-format = "0.4"
2424
eif_loader = { path = "./eif_loader" }
2525
enclave_build = { path = "./enclave_build" }
2626
openssl = "0.10.66"

eif_loader/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ rust-version = "1.68"
88
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
99

1010
[dependencies]
11-
aws-nitro-enclaves-image-format = "0.2"
11+
aws-nitro-enclaves-image-format = "0.4"
1212
nix = "0.26"
1313
libc = "0.2"
1414
vsock = "0.3"

enclave_build/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ url = "2.4"
2121
sha2 = "0.9.5"
2222
futures = "0.3.28"
2323

24-
aws-nitro-enclaves-image-format = "0.2"
24+
aws-nitro-enclaves-image-format = "0.4"
2525
tar = "0.4.40"
2626
flate2 = "1.0.28"

enclave_build/src/lib.rs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ mod yaml_generator;
1212

1313
use aws_nitro_enclaves_image_format::defs::{EifBuildInfo, EifIdentityInfo, EIF_HDR_ARCH_ARM64};
1414
use aws_nitro_enclaves_image_format::utils::identity::parse_custom_metadata;
15-
use aws_nitro_enclaves_image_format::utils::{EifBuilder, SignEnclaveInfo};
15+
use aws_nitro_enclaves_image_format::utils::{
16+
EifBuilder, SignKeyData, SignKeyDataInfo, SignKeyInfo,
17+
};
1618
use docker::DockerUtil;
1719
use serde_json::json;
1820
use sha2::Digest;
@@ -31,7 +33,7 @@ pub struct Docker2Eif<'a> {
3133
linuxkit_path: String,
3234
artifacts_prefix: String,
3335
output: &'a mut File,
34-
sign_info: Option<SignEnclaveInfo>,
36+
sign_info: Option<SignKeyData>,
3537
img_name: Option<String>,
3638
img_version: Option<String>,
3739
metadata_path: Option<String>,
@@ -70,6 +72,8 @@ impl<'a> Docker2Eif<'a> {
7072
artifacts_prefix: String,
7173
certificate_path: &Option<String>,
7274
key_path: &Option<String>,
75+
kms_key_id: &Option<String>,
76+
kms_key_region: &Option<String>,
7377
img_name: Option<String>,
7478
img_version: Option<String>,
7579
metadata_path: Option<String>,
@@ -98,15 +102,31 @@ impl<'a> Docker2Eif<'a> {
98102
}
99103
}
100104

101-
let sign_info = match (certificate_path, key_path) {
105+
let sign_key_info = match (kms_key_id, key_path) {
102106
(None, None) => None,
103-
(Some(cert_path), Some(key_path)) => Some(
104-
SignEnclaveInfo::new(cert_path, key_path)
105-
.map_err(|err| Docker2EifError::SignImageError(format!("{err:?}")))?,
106-
),
107+
(Some(kms_id), None) => Some(SignKeyInfo::KmsKeyInfo {
108+
id: kms_id.into(),
109+
region: kms_key_region.clone(),
110+
}),
111+
(None, Some(key_path)) => Some(SignKeyInfo::LocalPrivateKeyInfo {
112+
path: key_path.into(),
113+
}),
107114
_ => return Err(Docker2EifError::SignArgsError),
108115
};
109116

117+
let sign_info = sign_key_info
118+
.map(|key_info| {
119+
SignKeyData::new(&SignKeyDataInfo {
120+
cert_path: certificate_path
121+
.as_ref()
122+
.ok_or(Docker2EifError::SignArgsError)?
123+
.into(),
124+
key_info,
125+
})
126+
.map_err(|_| Docker2EifError::SignArgsError)
127+
})
128+
.transpose()?;
129+
110130
Ok(Docker2Eif {
111131
docker_image,
112132
docker,
@@ -275,10 +295,15 @@ impl<'a> Docker2Eif<'a> {
275295
_ => return Err(Docker2EifError::UnsupportedArchError),
276296
};
277297

298+
// We cannot clone `sign_info` because it might contain a KmsKey object
299+
// which is not copyable. Since `create` is the last method called, we can
300+
// move it out of the struct.
301+
let sign_info = self.sign_info.take();
302+
278303
let mut build = EifBuilder::new(
279304
Path::new(&self.kernel_img_path),
280305
self.cmdline.clone(),
281-
self.sign_info.clone(),
306+
sign_info,
282307
sha2::Sha384::new(),
283308
flags,
284309
self.generate_identity_info()?,

enclave_build/src/main.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
use clap::{App, AppSettings, Arg};
4+
use clap::{App, AppSettings, Arg, ArgGroup};
55
use std::fs::OpenOptions;
66

77
use aws_nitro_enclaves_image_format::generate_build_info;
@@ -86,6 +86,25 @@ fn main() {
8686
.help("Specify the path to the private-key")
8787
.takes_value(true),
8888
)
89+
.arg(
90+
Arg::with_name("kms-key-id")
91+
.long("kms-key-id")
92+
.help("Specify unique id of the KMS key")
93+
.takes_value(true),
94+
)
95+
.arg(
96+
Arg::with_name("kms-key-region")
97+
.long("kms-key-region")
98+
.help("Specify region in which the KMS key resides")
99+
.takes_value(true)
100+
.requires("kms-key-id")
101+
)
102+
.group(
103+
ArgGroup::new("signing-key")
104+
.args(&["kms-key-id", "private-key"])
105+
.multiple(false)
106+
.requires("signing-certificate")
107+
)
89108
.arg(
90109
Arg::with_name("build")
91110
.short('b')
@@ -136,6 +155,10 @@ fn main() {
136155
let private_key = matches
137156
.value_of("private_certificate")
138157
.map(|val| val.to_string());
158+
let kms_key_id = matches.value_of("kms-key-id").map(|val| val.to_string());
159+
let kms_key_region = matches
160+
.value_of("kms-key-region")
161+
.map(|val| val.to_string());
139162
let img_name = matches.value_of("image_name").map(|val| val.to_string());
140163
let img_version = matches.value_of("image_version").map(|val| val.to_string());
141164
let metadata = matches.value_of("metadata").map(|val| val.to_string());
@@ -159,6 +182,8 @@ fn main() {
159182
".".to_string(),
160183
&signing_certificate,
161184
&private_key,
185+
&kms_key_id,
186+
&kms_key_region,
162187
img_name,
163188
img_version,
164189
metadata,

src/common/commands_parser.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ pub struct BuildEnclavesArgs {
108108
pub signing_certificate: Option<String>,
109109
/// The path to the private key for signed enclaves.
110110
pub private_key: Option<String>,
111+
/// ID of the KMS key for signed enclaves.
112+
pub kms_key_id: Option<String>,
113+
/// Region of the KMS key for signed enclaves.
114+
pub kms_key_region: Option<String>,
111115
/// The name of the enclave image.
112116
pub img_name: Option<String>,
113117
/// The version of the enclave image.
@@ -121,16 +125,18 @@ impl BuildEnclavesArgs {
121125
pub fn new_with(args: &ArgMatches) -> NitroCliResult<Self> {
122126
let signing_certificate = parse_signing_certificate(args);
123127
let private_key = parse_private_key(args);
128+
let kms_key_id = parse_kms_key_id(args);
129+
let kms_key_region = parse_kms_key_region(args);
124130

125-
match (&signing_certificate, &private_key) {
126-
(Some(_), None) => {
131+
match (&signing_certificate, &private_key, &kms_key_id) {
132+
(_, Some(_), Some(_)) => {
127133
return Err(new_nitro_cli_failure!(
128-
"`private-key` argument not found",
129-
NitroCliErrorEnum::MissingArgument
130-
)
131-
.add_info(vec!["private-key"]))
134+
"Cannot use both `private-key` and `kms-key-id`",
135+
NitroCliErrorEnum::ConflictingArgument
136+
))
132137
}
133-
(None, Some(_)) => {
138+
(None, None, None) => (),
139+
(None, _, _) => {
134140
return Err(new_nitro_cli_failure!(
135141
"`signing-certificate` argument not found",
136142
NitroCliErrorEnum::MissingArgument
@@ -158,6 +164,8 @@ impl BuildEnclavesArgs {
158164
})?,
159165
signing_certificate,
160166
private_key,
167+
kms_key_id,
168+
kms_key_region,
161169
img_name: parse_image_name(args),
162170
img_version: parse_image_version(args),
163171
metadata: parse_metadata(args),
@@ -550,6 +558,14 @@ fn parse_private_key(args: &ArgMatches) -> Option<String> {
550558
args.value_of("private-key").map(|val| val.to_string())
551559
}
552560

561+
fn parse_kms_key_id(args: &ArgMatches) -> Option<String> {
562+
args.value_of("kms-key-id").map(|val| val.to_string())
563+
}
564+
565+
fn parse_kms_key_region(args: &ArgMatches) -> Option<String> {
566+
args.value_of("kms-key-region").map(|val| val.to_string())
567+
}
568+
553569
fn parse_image_name(args: &ArgMatches) -> Option<String> {
554570
args.value_of("image_name").map(|val| val.to_string())
555571
}
@@ -579,7 +595,7 @@ mod tests {
579595
use crate::common::construct_error_message;
580596
use crate::create_app;
581597

582-
use clap::{App, AppSettings, Arg, SubCommand};
598+
use clap::{App, AppSettings, Arg, ArgGroup, SubCommand};
583599

584600
/// Parse the path of the JSON config file
585601
fn parse_config_file(args: &ArgMatches) -> NitroCliResult<String> {

src/lib.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ pub fn build_enclaves(args: BuildEnclavesArgs) -> NitroCliResult<()> {
5858
&args.output,
5959
&args.signing_certificate,
6060
&args.private_key,
61+
&args.kms_key_id,
62+
&args.kms_key_region,
6163
&args.img_name,
6264
&args.img_version,
6365
&args.metadata,
@@ -73,6 +75,8 @@ pub fn build_from_docker(
7375
output_path: &str,
7476
signing_certificate: &Option<String>,
7577
private_key: &Option<String>,
78+
kms_key_id: &Option<String>,
79+
kms_key_region: &Option<String>,
7680
img_name: &Option<String>,
7781
img_version: &Option<String>,
7882
metadata_path: &Option<String>,
@@ -136,6 +140,8 @@ pub fn build_from_docker(
136140
artifacts_path()?,
137141
signing_certificate,
138142
private_key,
143+
kms_key_id,
144+
kms_key_region,
139145
img_name.clone(),
140146
img_version.clone(),
141147
metadata_path.clone(),
@@ -742,6 +748,25 @@ macro_rules! create_app {
742748
.help("Local path to developer's Eliptic Curve private key.")
743749
.takes_value(true),
744750
)
751+
.arg(
752+
Arg::with_name("kms-key-id")
753+
.long("kms-key-id")
754+
.help("Specify unique id of the KMS key")
755+
.takes_value(true),
756+
)
757+
.arg(
758+
Arg::with_name("kms-key-region")
759+
.long("kms-key-region")
760+
.help("Specify region in which the KMS key resides")
761+
.takes_value(true)
762+
.requires("kms-key-id")
763+
)
764+
.group(
765+
ArgGroup::new("signing-key")
766+
.args(&["kms-key-id", "private-key"])
767+
.multiple(false)
768+
.requires("signing-certificate")
769+
)
745770
.arg(
746771
Arg::with_name("image_name")
747772
.long("name")

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
99
extern crate lazy_static;
1010

11-
use clap::{App, AppSettings, Arg, SubCommand};
11+
use clap::{App, AppSettings, Arg, ArgGroup, SubCommand};
1212
use log::info;
1313
use std::os::unix::net::UnixStream;
1414

tests/test_nitro_cli_args.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
#[cfg(test)]
66
mod test_nitro_cli_args {
7-
use clap::{App, AppSettings, Arg, SubCommand};
7+
use clap::{App, AppSettings, Arg, ArgGroup, SubCommand};
88
use nitro_cli::create_app;
99

1010
#[test]

0 commit comments

Comments
 (0)