App-FargateStack
view release on metacpan or search on metacpan
lib/App/FargateStack/Checker.pm view on Meta::CPAN
my ( $ok, $certs, $err )
= try_aws( sub { $acm->command( 'list-certificates' => [ '--query' => 'CertificateSummaryList[].CertificateArn' ] ) } );
if ( !$ok ) {
return row_fail( 'ACM', 'Cannot list certificates in region' );
}
return row_ok( ACM => 'List OK' );
}
########################################################################
sub check_ecr_access {
########################################################################
my ($opt) = @_;
my $ecr = new_client( 'App::ECR', $opt );
my ( $ok, $repos, $err ) = try_aws( sub { $ecr->command( 'describe-repositories' => [ '--max-results' => '1' ] ) } );
if ( !$ok ) {
return row_warn( 'ECR', 'Cannot describe repositories; image validation may fail' );
}
return row_ok( 'ECR', 'Describe OK' );
}
########################################################################
sub check_secrets_hint {
########################################################################
my ($opt) = @_;
# The deployer does not need GetSecretValue; tasks use task role.
# We only check control-plane reachability here.
my $ec2 = new_client( 'App::EC2', $opt );
my ( $ok_ep, $eps, $err_ep ) = try_aws(
sub {
$ec2->command( 'describe-vpc-endpoints' => [ '--query', 'VpcEndpoints[].ServiceName' ] );
}
);
if ( !$ok_ep ) {
return row_warn( 'Secrets Manager', 'Could not confirm VPC endpoints for Secrets Manager' );
}
my %have = map { $_ => 1 } @{ $eps || [] };
my $svc = sprintf 'com.amazonaws.%s.secretsmanager', ( $opt->{region} || 'us-east-1' );
if ( !$have{$svc} ) {
return row_warn( 'Secrets Manager', 'Missing VPC endpoint for Secrets Manager or rely on NAT' );
}
return row_ok( 'Secrets Manager', 'Endpoint present' );
}
########################################################################
sub check_passrole {
########################################################################
my ($opt) = @_;
my $role_names = $opt->get_role_names;
# role_names: arrayref of the exact role names your framework will create,
# e.g., [ 'FargateStack/my-svc/TaskExecutionRole', 'FargateStack/my-svc/TaskRole',
# 'FargateStack/my-svc/EventsInvokeRole' ]
my $sts = new_client( 'App::STS', $opt );
my $iam = new_client( 'App::IAM', $opt );
my $id = $sts->get_caller_identity();
my $acct = $id->{Account} || q{};
my $caller_arn = $id->{Arn} || q{};
if ( !$acct || !$caller_arn ) {
return row_fail( 'iam:PassRole', 'Cannot resolve caller identity' );
}
my $policy_source_arn = _principal_to_policy_source_arn( $caller_arn, $acct );
if ( !$policy_source_arn ) {
return row_fail( 'iam:PassRole', 'Cannot derive policy-source ARN for simulation' );
}
my $role_arns = _role_arns_from_names( $acct, $role_names || [] );
if ( !$role_arns || !@{$role_arns} ) {
return row_warn( 'iam:PassRole', 'No target roles specified; skipping simulation' );
}
my @services = qw(ecs-tasks.amazonaws.com events.amazonaws.com);
my %decisions_by_service;
foreach my $svc (@services) {
my $result = eval { _simulate_passrole_once( $iam, $policy_source_arn, $role_arns, $svc ) };
if ( $EVAL_ERROR || !$result ) {
# If org blocks simulation, warn rather than fail
return row_warn( 'iam:PassRole', 'Simulation not permitted; verify PassRole manually' );
}
# result is an arrayref of EvalDecision strings: allowed | explicitDeny | implicitDeny
my @dec = @{ $result || [] };
$decisions_by_service{$svc} = \@dec;
}
# Aggregate decisions: any explicitDeny -> FAIL
# otherwise any implicitDeny -> WARN
# otherwise all allowed -> PASS
my $detail = q{};
my $status = 'PASS';
foreach my $svc (@services) {
my $dec = $decisions_by_service{$svc} || [];
my $svc_summary = sprintf '%s: %s', $svc, ( join q{,}, @{$dec} );
if ( grep { $_ eq 'explicitDeny' } @{$dec} ) {
$status = 'FAIL';
}
elsif ( $status ne 'FAIL' && grep { $_ eq 'implicitDeny' } @{$dec} ) {
$status = 'WARN';
}
$detail .= $svc_summary . q{ };
}
( run in 1.249 second using v1.01-cache-2.11-cpan-e1769b4cff6 )