Linters have a long and enduring history in software. From their origins in late 1970s to the present, they’ve caught things like programming errors, confusing formatting, unsafe functions, and everything in between. The static analysis approach lends itself to general purpose templates that make it easy to share between projects making best practices more accessible.
With infrastructure-as-code (IaC) becoming more ubiquitous and mature for cloud infrastructure, linters have made their impact in this area as well. IaC linters range from catching errors like illegal resource types, deprecated syntax, to enforcing best practices like tagging conventions. As they continue to mature, specialized security oriented linters are also becoming more commonplace; preventing the creation of overly permissive IAM policies, firewall rules, and storage bucket policies are getting easier to codify as lint.
The ever changing flux and churn of production infrastructure ensures that there is always a delta between the intended infrastructure and what’s actually deployed. Whether it’s in response to an outage, maintenance, or developer experimentation changes to infrastructure can occur via CLI, the vendor’s UI, or some other out-of-band channel. This gap between IaC’s intent and what’s actually deployed is a reality even with teams reaching the asymptote of 100% IaC controlled infrastructure. We refer to the view of what’s actually live in production as infrastructure-as-deployed and having a view of the world through this prism opens up the door to finding new types of lint.
Our tool Introspector makes it easy to find new categories of lint such as:
- Unused resources (e.g. access keys created but never used)
- Stale resources (e.g. user principals that haven’t logged in some configurable amount of time)
- Orphaned resources (e.g. security groups no longer attached)
The purpose of cleaning up this lint is to reduce surface area—thereby improving your security posture and reducing complexity of your infrastructure.
Finding unused IAM groups:
age(G.createdate) AS age
aws_iam_group AS G
G._id NOT IN (
Finding unused elastic IPs:
aws_ec2_address AS A
A.associationid is null
Finding users that have never logged in:
age(createdate) AS age
password_enabled = true
AND age(createdate) > '3 months'::interval
AND passwordlastused IS NULL
In the next part in this series, we’ll blog about linting your infrastructure for stale resources. Querying resources with a parameter around how long it has been idle makes it easy to find the needles in the haystack.