Education › Security › Stage 3: Cloud & pipeline

Cloud security

IAM done right, public buckets and open security groups, logging, encryption at rest, the shared responsibility model.

Intermediate ~35 min read Module 9 of 16

Cloud breaches rarely involve the provider being hacked. They involve a public storage bucket, an access key in a repository, a role that could do everything, or a logging service nobody turned on. The provider secures the data centre and the hypervisor; everything you configure on top is yours to get right, and the configuration surface is enormous. This module gives you the model for splitting that responsibility, the identity and access patterns that prevent most cloud incidents, the handful of misconfigurations that account for most of the rest, and the logging and automated checks that catch drift before an attacker does. Examples use AWS terminology; the ideas map directly to Azure and Google Cloud.

After this module you can
  • Explain the shared responsibility model and list what is yours for compute, managed services and serverless
  • Design cloud IAM around roles, least privilege, and short-lived credentials, with guardrails at the organisation level
  • Recognise and fix the high-impact misconfigurations: public storage, open security groups, unencrypted data, missing MFA
  • Turn on and centralise the logs that every cloud investigation depends on
  • Run continuous configuration checks and treat findings as alerts

Shared responsibility, precisely

The provider is responsible for the security of the cloud: physical facilities, hardware, the hypervisor, the managed service's underlying software. You are responsible for security in the cloud: identities and permissions, network configuration, operating systems on your instances, application code, data classification and encryption choices, and logging. The line moves with the service model: on a virtual machine you patch the OS; on a managed database the provider patches the engine but you configure access and backups; on a serverless function you own only code, configuration and permissions.

LayerVirtual machinesManaged databaseServerless functions
Physical, hypervisorProviderProviderProvider
OS and runtime patchesYouProviderProvider
Network exposure, IAMYouYouYou
Data, encryption keys, backupsYouYou (provider offers tools)You
Application code and dependenciesYoun/aYou

The practical consequence is that cloud security is mostly configuration security. The same three questions from the threat modelling module apply to every resource: who can reach it (network), who can act on it (IAM), and what happens to the data (encryption, backups, logging). Almost every cloud incident is a wrong answer to one of them.

Identity: roles, not keys; guardrails, not hope

Cloud IAM is the control plane for everything else, so it gets the strictest treatment. Humans authenticate through single sign-on with phishing-resistant MFA and assume roles for specific tasks; nobody uses the root or owner account for daily work, and its credentials live in a safe with hardware MFA. Workloads use roles attached to the compute (instance profiles, pod identity, function roles) that issue rotating temporary credentials; long-lived access keys exist only where nothing else is possible, with an expiry date and an owner.

A permission boundary style policy: however broad a developer-created role is, it can never touch IAM or disable logging.
json
{
  "Version": "2012-10-17",
  "Statement": [
    {"Effect": "Allow", "Action": "*", "Resource": "*"},
    {
      "Effect": "Deny",
      "Action": [
        "iam:*", "organizations:*", "cloudtrail:StopLogging", "cloudtrail:DeleteTrail",
        "guardduty:Delete*", "config:Delete*", "kms:ScheduleKeyDeletion"
      ],
      "Resource": "*"
    }
  ]
}

Above individual accounts sit organisation-level guardrails: service control policies (or Azure Policy, GCP organisation policies) that apply to every account regardless of what its administrators do — deny leaving the organisation, deny disabling audit logs, deny public storage, restrict regions. Separate accounts or subscriptions per environment (production, staging, sandbox) so a compromised sandbox key cannot touch production at all. Finally, use the access analysers the provider offers: they list roles and resources reachable from outside your organisation and permissions that have never been used, which is a ready-made least-privilege backlog.

Watch out

"Action": "*", "Resource": "*" on a workload role is the most common serious finding in cloud reviews. Start from what the workload actually calls (the access analyser will tell you) and write that.

The misconfigurations that cause most incidents

A short list accounts for a large share of real cloud breaches. Learn them, then automate checking for them.

  • Public object storage: a bucket or blob container readable by everyone, often holding backups or exports. Turn on the account-level public access block and make exceptions explicit and reviewed.
  • Open security groups: SSH, RDP, databases or the Kubernetes API reachable from 0.0.0.0/0. Covered in the network module; scanners find these within minutes.
  • Leaked credentials: access keys in code, in images, in CI logs. Prefer roles; scan for keys; alert on use from new locations.
  • Over-privileged roles: wildcard policies, cross-account trust to anyone, roles assumable without conditions.
  • No MFA on the root or owner account, or on console users with production access.
  • Unencrypted storage and snapshots: volumes, databases and backups without provider-managed or customer-managed keys; public snapshots.
  • Metadata service v1 on instances, which turns any SSRF into stolen role credentials. Require v2 (token-based) everywhere.
  • Logging off: no audit trail, no flow logs, no object access logs, so there is nothing to investigate with.
Finding three of them from the CLI: public buckets, wide-open security group rules, and instances that still allow metadata v1.
bash
# buckets without the public access block (should be none; also set it at the account level)
for b in $(aws s3api list-buckets --query 'Buckets[].Name' --output text); do
  aws s3api get-public-access-block --bucket "$b" >/dev/null 2>&1 || echo "NO PUBLIC ACCESS BLOCK: $b"
done

# security group rules open to the world on anything other than 80/443
aws ec2 describe-security-group-rules \
  --query "SecurityGroupRules[?CidrIpv4=='0.0.0.0/0' && !IsEgress && FromPort!=\`80\` && FromPort!=\`443\`].[GroupId,FromPort,ToPort]" --output table

# instances not requiring IMDSv2
aws ec2 describe-instances \
  --query "Reservations[].Instances[?MetadataOptions.HttpTokens!='required'].[InstanceId,MetadataOptions.HttpTokens]" --output table

Encryption at rest is cheap and should be the default for every volume, database, queue and bucket, using provider-managed keys at minimum and customer-managed keys where you need to control access and rotation or prove it to an auditor. Encryption in transit is TLS to every endpoint, including the provider's own APIs, which policy can require (aws:SecureTransport).

Logs are the investigation

When something goes wrong in the cloud, the questions are always the same: which identity did what, from where, to which resource, when. The API audit trail (CloudTrail, Azure Activity Log, Cloud Audit Logs) answers them, but only if it is enabled in every region and account, delivered to a central bucket the source account cannot delete from, and retained long enough — a year is a reasonable floor, since intrusions are often discovered months later. Add flow logs for network questions and data access logs for the storage that holds anything sensitive.

An organisation-wide trail: all regions, all accounts, log file validation on, delivered to a central account.
hcl
resource "aws_cloudtrail" "org" {
  name                          = "org-trail"
  s3_bucket_name                = aws_s3_bucket.audit_logs.id
  is_organization_trail         = true
  is_multi_region_trail         = true
  include_global_service_events = true
  enable_log_file_validation    = true
  kms_key_id                    = aws_kms_key.audit.arn

  event_selector {
    read_write_type           = "All"
    include_management_events = true
    data_resource {
      type   = "AWS::S3::Object"
      values = ["arn:aws:s3:::acme-customer-exports/"]
    }
  }
}

Logs you never look at are still valuable in an incident, but logs that raise alerts are valuable before one. The provider's threat detection services (GuardDuty, Defender for Cloud, Security Command Center) consume these logs and flag credential use from unusual locations, crypto-mining patterns, calls from known-bad addresses and privilege escalation sequences. Turning them on is one API call and is usually the best-value security control in the account.

Continuous checks and infrastructure as code

Configuration drifts: a rule is widened during an incident, a bucket is made public for a demo, a role gains a wildcard to make a deploy work. Continuous configuration checks compare the live estate against a baseline (the CIS cloud benchmarks are the usual one) and report or auto-remediate: the provider's config services and open-source tools such as Prowler do this across hundreds of rules. Treat a new critical finding as an alert with an owner, not as a monthly report.

A baseline check with Prowler, and a policy-as-code check on Terraform before it is applied.
bash
# assess an account against the CIS benchmark and write an HTML report
prowler aws --compliance cis_2.0_aws --output-formats html --output-directory ./prowler-out

# catch the misconfiguration before it exists: scan the Terraform plan in CI
checkov -d infra/ --framework terraform --check CKV_AWS_18,CKV_AWS_19,CKV_AWS_23,CKV_AWS_53,CKV_AWS_54,CKV_AWS_55,CKV_AWS_56

The most durable fix is upstream: because your infrastructure is code, the security rules can be tests on that code. Policy checks in the pipeline reject a public bucket or an open port before the plan is applied, the same way unit tests reject broken logic. Combine that with guardrails at the organisation level, roles instead of keys, encryption by default, central logging and threat detection, and the cloud stops being the place where one checkbox takes the company down.

Hands-on practice

Audit an account and fix what you find

  1. In a test account (or the one from the zero-to-production project) run the three CLI checks from the lesson: public access blocks, world-open security group rules, and IMDSv1 instances. Note every finding.
  2. Enable the account-level public access block for object storage and re-run the bucket check.
  3. Set http_tokens = "required" in the Terraform metadata_options of an instance, apply, and confirm from the instance that a plain metadata curl without a token now fails.
  4. Open the IAM access analyser (or its equivalent) and list unused permissions for one workload role; rewrite the role to the actions it actually used in the last 90 days.
  5. Create a multi-region audit trail with log file validation to a bucket in the same account, then use the trail's lookup to find your own console login event.
  6. Run prowler aws --severity critical high and pick three findings: fix two in Terraform and document one as accepted with a reason.
  7. Add checkov -d infra/ to the CI workflow of a Terraform repository and make a deliberately public bucket change to confirm the pipeline fails.
Cheat sheet

Cloud security — at a glance

Main things to focus on

  • Provider secures the cloud; you secure what you configure in it: IAM, network, data, logging
  • Roles and temporary credentials for humans and workloads; root/owner locked with hardware MFA
  • Organisation guardrails: SCPs that nobody in an account can override; separate accounts per environment
  • The usual breaches: public storage, open groups, leaked keys, wildcard roles, no MFA, unencrypted data, IMDSv1, logging off
  • Central, tamper-resistant audit trail in every region plus threat detection turned on
  • Continuous checks against a benchmark; policy-as-code on Terraform before apply

IAM patterns

SSO + phishing-resistant MFA -> assume roleHumans never use long-lived keys
instance profile / pod identity / function roleWorkloads get rotating temporary credentials
permission boundary: Deny iam:*, cloudtrail:StopLoggingCaps what any created role can do
SCP: deny leave org, deny disable logging, deny public S3Guardrails above accounts
access analyser: external access, unused permissionsLeast-privilege backlog, generated
root: no keys, hardware MFA, alarm on useBreak-glass only

Quick checks (AWS CLI)

aws s3api get-public-access-block --bucket BMissing block = candidate for exposure
aws ec2 describe-security-group-rules --query "...CidrIpv4=='0.0.0.0/0'..."World-open ingress rules
describe-instances ... MetadataOptions.HttpTokens!='required'Instances still on IMDSv1
aws iam get-credential-reportUsers with keys, key age, MFA status
aws cloudtrail lookup-events --lookup-attributes AttributeKey=Username,AttributeValue=UWhat an identity did

Data and logging

encryption at rest by default (SSE-KMS / CMK)Volumes, databases, buckets, snapshots
Condition: aws:SecureTransport trueRequire TLS to the API and storage
org trail, multi-region, log file validation, central bucketAudit trail nobody can quietly disable
flow logs + S3 data events on sensitive bucketsNetwork and object access questions
GuardDuty / Defender / SCC onManaged threat detection from the logs
retention >= 1 yearBreaches are found late

Continuous checks

prowler aws --compliance cis_2.0_awsBenchmark the live account
checkov -d infra/ --framework terraformBlock misconfiguration at plan time
config rules / Azure Policy / org policiesProvider-native drift detection and remediation
new critical finding = alert with ownerNot a monthly PDF

Common pitfalls

  • Working from the root or owner account because it was the first one created.
  • A wildcard workload role that was going to be tightened after launch.
  • Making a bucket public for a demo and forgetting; the account-level block prevents the class.
  • Leaving IMDSv1 enabled, so any SSRF in any app hands out instance credentials.
  • Logging in one region only, or to a bucket the same account can delete from.
  • Reading cloud security reports monthly instead of alerting on new critical findings.
Quiz

Check your understanding

5 questions · 4 to pass · answers are explained as you go. Your best score is saved on this device only.

Progress and quiz scores are saved in this browser only. Back up or restore on the hub.

Was this lesson useful? Tell me what to improve →