Skip to content
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

host-ctr: support FIPS ECR service endpoints #204

Merged
merged 2 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions sources/api/schnauzer/src/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use serde_plain::derive_fromstr_from_deserialize;
use settings_extension_oci_defaults::OciDefaultsResourceLimitV1;
use snafu::{OptionExt, ResultExt};
use std::borrow::Borrow;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::convert::TryFrom;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
Expand Down Expand Up @@ -70,6 +70,19 @@ lazy_static! {
m.insert("us-west-2", "328549459982");
m
};

/// A set to tell us which regions have FIPS ECR endpoints.
/// https://docs.aws.amazon.com/general/latest/gr/ecr.html
static ref ECR_FIPS_REGIONS: HashSet<&'static str> = {
let mut h = HashSet::new();
h.insert("us-east-1");
h.insert("us-east-2");
h.insert("us-gov-east-1");
h.insert("us-gov-west-1");
h.insert("us-west-1");
h.insert("us-west-2");
h
};
}

/// But if there is a region that does not exist in our map (for example a new
Expand All @@ -78,6 +91,9 @@ lazy_static! {
const ECR_FALLBACK_REGION: &str = "us-east-1";
const ECR_FALLBACK_REGISTRY: &str = "328549459982";

/// Path to FIPS sysctl file.
const FIPS_ENABLED_SYSCTL_PATH: &str = "/proc/sys/crypto/fips_enabled";

lazy_static! {
/// A map to tell us which endpoint to pull updates from for a given region.
static ref TUF_ENDPOINT_MAP: HashMap<&'static str, &'static str> = {
Expand Down Expand Up @@ -519,6 +535,14 @@ pub fn tuf_prefix(
Ok(())
}

/// Utility function to determine if a variant is in FIPS mode based
/// on /proc/sys/crypto/fips_enabled.
fn fips_enabled() -> bool {
std::fs::read_to_string(FIPS_ENABLED_SYSCTL_PATH)
.map(|s| s.trim() == "1")
.unwrap_or(false)
}

/// The `metadata-prefix` helper is used to map an AWS region to the correct
/// metadata location inside of the TUF repository.
///
Expand Down Expand Up @@ -1426,7 +1450,16 @@ fn ecr_registry<S: AsRef<str>>(region: S) -> String {
match partition {
"aws-cn" => format!("{}.dkr.ecr.{}.amazonaws.com.cn", registry_id, region),
"aws-iso-e" => format!("{}.dkr.ecr.{}.cloud.adc-e.uk", registry_id, region),
_ => format!("{}.dkr.ecr.{}.amazonaws.com", registry_id, region),
_ => {
// Only inject the FIPS service endpoint if the variant is in FIPS mode and the
// region supports FIPS.
let suffix = if fips_enabled() && ECR_FIPS_REGIONS.contains(region) {
"-fips"
} else {
""
};
format!("{}.dkr.ecr{}.{}.amazonaws.com", registry_id, suffix, region)
}
}
}

Expand Down
33 changes: 29 additions & 4 deletions sources/host-ctr/cmd/host-ctr/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ import (
// Example 1: 777777777777.dkr.ecr.us-west-2.amazonaws.com/my_image:latest
// Example 2: 777777777777.dkr.ecr.cn-north-1.amazonaws.com.cn/my_image:latest
// Example 3: 777777777777.dkr.ecr.eu-isoe-west-1.cloud.adc-e.uk/my_image:latest
var ecrRegex = regexp.MustCompile(`(^[a-zA-Z0-9][a-zA-Z0-9-_]*)\.dkr\.ecr\.([a-zA-Z0-9][a-zA-Z0-9-_]*)\.(amazonaws\.com(\.cn)?|cloud\.adc-e\.uk).*`)
// Example 4: 777777777777.dkr.ecr-fips.us-west-2.amazonaws.com/my_image:latest
var ecrRegex = regexp.MustCompile(`(^[a-zA-Z0-9][a-zA-Z0-9-_]*)\.dkr\.ecr(-fips)?\.([a-zA-Z0-9][a-zA-Z0-9-_]*)\.(amazonaws\.com(\.cn)?|cloud\.adc-e\.uk).*`)

const (
// The maximum size of an image label.
Expand Down Expand Up @@ -594,21 +595,28 @@ func cleanUp(containerdSocket string, namespace string, containerID string) erro

// parseImageURISpecialRegions mimics the parsing in ecr.ParseImageURI but
// constructs the canonical ECR references while skipping certain checks.
// We only do this for special regions that are not yet supported by the aws-go-sdk.
// We only do this for special regions that are not yet supported by the aws-go-sdk and for ECR FIPS endpoints.
// Referenced source: https://github.com/awslabs/amazon-ecr-containerd-resolver/blob/a5058cf091f4fc573813a032db37a9820952f1f9/ecr/ref.go#L70-L71
func parseImageURISpecialRegions(input string) (ecr.ECRSpec, error) {
ecrRefPrefixMapping := map[string]string{
"ap-southeast-5": "ecr.aws/arn:aws:ecr:ap-southeast-5:",
"eu-isoe-west-1": "ecr.aws/arn:aws-iso-e:ecr:eu-isoe-west-1:",
}
// A set of the currently supported FIPS regions for ECR: https://docs.aws.amazon.com/general/latest/gr/ecr.html
fipsSupportedEcrRegionSet := map[string]bool{
"us-east-1": true,
"us-east-2": true,
"us-west-1": true,
"us-west-2": true,
"us-gov-east-1": true,
"us-gov-west-1": true,
}
// Matching on account, region
matches := ecrRegex.FindStringSubmatch(input)
if len(matches) < 3 {
return ecr.ECRSpec{}, fmt.Errorf("invalid image URI: %s", input)
}
account := matches[1]
region := matches[2]

// Need to include the full repository path and the imageID (e.g. /eks/image-name:tag)
tokens := strings.SplitN(input, "/", 2)
if len(tokens) != 2 {
Expand All @@ -627,6 +635,23 @@ func parseImageURISpecialRegions(input string) (ecr.ECRSpec, error) {
return ecr.ECRSpec{}, errors.New("incomplete reference provided")
}

// Return early if the FIPS endpoint is being used. amazon-ecr-containerd-resolver doesn't yet support FIPS urls:
// https://github.com/awslabs/amazon-ecr-containerd-resolver/blob/7b72333e780f5a5168936eae79fb89448e2f2a8f/ecr/ref.go#L43
// The ecr-prefix helper for admin and control host containers will have already accounted for setting this endpoint
// if the region has FIPS support.
if matches[2] == "-fips" {
region := matches[3]
_, isFips := fipsSupportedEcrRegionSet[region]
if !isFips {
return ecr.ECRSpec{}, fmt.Errorf("%s: %s", "invalid FIPS region", region)
}
ecrRefPrefix := fmt.Sprintf("ecr.aws/arn:aws:ecr-fips:%s:", region)
return ecr.ParseRef(fmt.Sprintf("%s%s:repository/%s", ecrRefPrefix, account, fullRepoPath))
}

// The provided URI does not specify the FIPS endpoint, and the second match is the region.
region := matches[2]

// Get the ECR image reference prefix from the AWS region
ecrRefPrefix, ok := ecrRefPrefixMapping[region]
if !ok {
Expand Down
12 changes: 12 additions & 0 deletions sources/host-ctr/cmd/host-ctr/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,18 @@ func TestFetchECRRef(t *testing.T) {
false,
"ecr.aws/arn:aws-us-gov:ecr:us-gov-west-1:111111111111:repository/bottlerocket/container:1.2.3",
},
{
"Parse FIPS region for normal use-cases",
"111111111111.dkr.ecr-fips.us-west-2.amazonaws.com/bottlerocket/container:1.2.3",
false,
"ecr.aws/arn:aws:ecr-fips:us-west-2:111111111111:repository/bottlerocket/container:1.2.3",
},
{
"Fail for region that does not have FIPS support",
"111111111111.dkr.ecr-fips.ca-central-1.amazonaws.com/bottlerocket/container:1.2.3",
true,
"",
},
{
"Fail for invalid region",
"111111111111.dkr.ecr.outer-space.amazonaws.com/bottlerocket/container:1.2.3",
Expand Down
Loading