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 )