app/collectors/securityGroup.scala (122 lines of code) (raw):

package collectors import agent._ import conf.AWS import controllers.{Prism, routes} import play.api.mvc.Call import software.amazon.awssdk.services.ec2.Ec2Client import software.amazon.awssdk.services.ec2.model.{ DescribeSecurityGroupsRequest, IpPermission, SecurityGroup => AwsSecurityGroup } import scala.jdk.CollectionConverters._ import scala.language.{existentials, postfixOps} class SecurityGroupCollectorSet(accounts: Accounts, prismController: Prism) extends CollectorSet[SecurityGroup]( ResourceType("security-group"), accounts, Some(Regional) ) { def lookupCollector: PartialFunction[Origin, Collector[SecurityGroup]] = { case aws: AmazonOrigin => AWSSecurityGroupCollector( aws, resource, prismController, aws.crawlRate(resource.name) ) } } case class AWSSecurityGroupCollector( origin: AmazonOrigin, resource: ResourceType, prismController: Prism, crawlRate: CrawlRate ) extends Collector[SecurityGroup] { def fromAWS( secGroup: AwsSecurityGroup, lookup: Map[String, SecurityGroup] ): SecurityGroup = { def groupRefs(rule: IpPermission): Seq[SecurityGroupRef] = { rule.userIdGroupPairs.asScala.map { pair => SecurityGroupRef( pair.groupId, pair.userId, lookup.get(pair.groupId).map(_.arn) ) } }.toSeq val rules = secGroup.ipPermissions.asScala.map { rule => Rule( rule.ipProtocol.replace("-1", "all"), Option(rule.fromPort).map(_.toInt), Option(rule.toPort).map(_.toInt), rule.ipRanges.asScala.toSeq.map(_.cidrIp).sorted.wrap, rule.ipv6Ranges.asScala.toSeq.map(_.cidrIpv6).sorted.wrap, groupRefs(rule).wrap ) } SecurityGroup( s"arn:aws:ec2:${origin.region}:${origin.accountNumber.get}:security-group/${secGroup.groupId}", secGroup.groupId, secGroup.groupName, origin.region, rules.toSeq, Option(secGroup.vpcId), secGroup.tags.asScala.map(t => t.key -> t.value).toMap ) } val client: Ec2Client = Ec2Client.builder .credentialsProvider(origin.credentials.provider) .region(origin.awsRegionV2) .overrideConfiguration(AWS.clientConfig) .build def crawl: Iterable[SecurityGroup] = { // get all existing groups to allow for cross referencing val existingGroups = prismController.securityGroupAgent .get() .flatMap(_.data) .map(sg => sg.groupId -> sg) .toMap val request = DescribeSecurityGroupsRequest.builder.build client .describeSecurityGroupsPaginator(request) .securityGroups .asScala .map(fromAWS(_, existingGroups)) } } case class Rule( protocol: String, fromPort: Option[Int], toPort: Option[Int], sourceIpv4CidrBlocks: Option[Seq[String]], sourceIpv6CidrBlocks: Option[Seq[String]], sourceGroupRefs: Option[Seq[SecurityGroupRef]] ) case class SecurityGroup( arn: String, groupId: String, name: String, location: String, rules: Seq[Rule], vpcId: Option[String], tags: Map[String, String] ) extends IndexedItem { def callFromArn: String => Call = arn => routes.Api.securityGroup(arn) } object SecurityGroup { implicit val arnLookup: ArnLookup[SecurityGroup] = new ArnLookup[SecurityGroup] { override def call(arn: String): Call = routes.Api.securityGroup(arn) override def item( arn: String, prism: Prism ): Option[(Label, SecurityGroup)] = prism.securityGroupAgent.getTuples.find(_._2.arn == arn) } } case class SecurityGroupRef( groupId: String, account: String, arn: Option[String] )