云环境资产发现(AWS/Azure/GCP)

我最早接触云资产发现时,是在一次多云环境整合项目里。客户把一批核心应用分散在三家云上,团队说「我们都有资产清单」。结果一审计,AWS 上挂着 3 台没人用的红移集群,Azure 那边躺了一堆孤立的函数应用,GCP 的存储桶权限直接对公网开放。

你看,没有统一的资产发现,安全就是个笑话。DSPM 的第一步,就是把云上到底跑着什么、存着什么、怎么连的,全给翻出来。

一、为什么云环境资产发现更棘手?

传统 IDC 里资产是固定的——IP、机柜、端口,你写个 CMDB 就能管个七七八八。云上不一样。一个人用控制台点几下,一台 EC2 就出来了,30 秒后又删了。嗯,这就是云资产的第一个特性:动态性。

我把云资产发现的难点总结成 3 点:

  1. API 驱动:所有操作都靠 API。DSPM 必须通过云厂商 API 去拉数据,不能指望人工录入。
  2. 资源种类爆炸:十年前你只需要管虚机和数据库。现在有 S3 桶、Lambda 函数、托管 K8s、消息队列、数据湖……每类资源都有自己的元数据。
  3. 权限网络复杂:同一个账号下,IAM 角色、策略、VPC 流日志、安全组、KMS 密钥全部纠缠在一起。你发现一个存储桶,不一定能找到「谁通过什么路径在访问它」。

我当时就踩过一个坑:只拉 AWS EC2 列表,没拉安全组和网络 ACL。结果客户的数据泄露事件发生在 S3 上,EC2 那页清单一丁点线索都没有。

二、三大云平台的资产发现策略

AWS、Azure、GCP 虽然都是云,但 API 设计哲学完全不同。我习惯从三个维度去拆:计算、存储、网络。

维度 AWS Azure GCP
计算 EC2、ECS、EKS、Lambda VM、AKS、Functions GCE、GKE、Cloud Run
存储 S3、EBS、RDS、DynamoDB Blob、Disk、SQL DB、Cosmos DB GCS、Persistent Disk、Cloud SQL、Firestore
网络/权限 VPC、Security Group、IAM VNet、NSG、RBAC VPC、Firewall Rules、IAM
发现入口 API ec2:DescribeInstances, s3:ListBuckets az vm list, az storage account list gcloud compute instances list, gsutil ls
分页限制 单次 1000 条,需 NextToken 20 条/页,需 continuation_ 单次 500 条,需 pageToken
跨账号 通过 Organizations + AssumeRole 通过 Management Group + Lighthouse 通过 Resource Manager + 聚合视图

你看,API 名称完全不同,但逻辑是相通的。关键是你得知道每家云的「资源树」怎么遍历。我个人习惯按这张图去规划发现流程:

多云资产发现核心流程 多云资产发现核心流程 定义云账号/项目清单 调用云厂商 API 拉取资源 分页:处理 NextToken 关联 IAM + 网络 + 标签 校验→去重→写入资产库 触发扫描/告警 避坑提示 某些 API 返回资源名 但 URL 不一致, 必须二次调用 API 获取完整元数据。

三、AWS 资产发现实战要点

AWS 的 API 是我用过最全的,几乎可以拉到每一条资源的全部元数据。但有一个问题:API 调用频次限制。我曾在项目中一次性拉 5 个账号的 10 个区域的全量资源,结果触发了 DescribeInstances 的限流(每区域每秒 100 次)。

这么做可以降低风险:

  • 按服务分区拉取,比如先拉 EC2,再拉 S3,不要并发拉所有。
  • 使用 NextToken 循环拉取,单次最多 1000 条。
  • 对 Lambda 和 RDS 等低频资源,降低轮询频率。

我个人的建议:先通过 AWS Organizations 列出所有成员账号,再用 AssumeRole 切换到每个账号。否则你光是在主账号里拉,子账号的 S3 桶一个都看不到。

# 伪代码:AWS 全账号资产发现
accounts = list_accounts()  # 通过 Organizations API
for acct in accounts:
    role = assume_role(acct.id, 'DSPMDiscoveryRole')
    # 拉计算
    ec2_list = ec2_client.describe_instances(MaxResults=1000)
    while True:
        # 处理 NextToken
        ...
    # 拉存储
    buckets = s3_client.list_buckets()
    for b in buckets:
        # 检查公共权限
        acl = s3_client.get_bucket_acl(Bucket=b['Name'])
    # 拉网络
    sgs = ec2_client.describe_security_groups()

我曾经遇到过:某些废弃的 EC2 实例在 DescribeInstances 里返回了,但 DescribeInstanceStatus 返回 terminated。如果只拉列表不校验状态,资产清单里就会多一堆死资源。

四、Azure 资产发现实战要点

Azure 的 API 设计跟 AWS 区别很大。最大的坑是:资源分类层级复杂——订阅(Subscription)→资源组(Resource Group)→资源。拉一个虚拟机,你得先知道它在哪个资源组下面。

我的做法是先用 az account list 拿到所有订阅,然后用 az resource list 拉资源组,最后逐个资源组去拉 VM、Blob 等。

# Azure 资产发现示例(Python SDK)
from azure.identity import DefaultAzureCredential
from azure.mgmt.resource import ResourceManagementClient

credential = DefaultAzureCredential()
subscriptions = list(credential.list_subscriptions())

for sub in subscriptions:
    client = ResourceManagementClient(credential, sub.subscription_id)
    # 列出所有资源
    resources = client.resources.list()
    for r in resources:
        if r.type == 'Microsoft.Compute/virtualMachines':
            # 拉 VM 详情
            vm = client.virtual_machines.get(r.resource_group, r.name)
        elif r.type == 'Microsoft.Storage/storageAccounts':
            # 检查 Blob 公开访问
            ...

另外,Azure 的 RBAC 权限检查不是靠 API 直接返回的。你得调用 role assignments API 才能看到谁有什么角色。嗯,这是跟 AWS IAM 最大的区别。

五、GCP 资产发现实战要点

GCP 的资产发现相对简洁。它的资源模型是:项目(Project)→区域(Region)→资源。API 调用时,通常需要指定 --project 参数。

我建议你用 asset inventory API,它是 GCP 官方的资产清单服务,能一次性拉出项目内的所有资源。不过要注意,这个 API 有 7 天缓存,新创建的资源可能要等几分钟才能被扫到。

# 使用 gcloud CLI 预处理
gcloud asset list \
    --project=my-project \
    --asset-types="compute.googleapis.com/Instance,storage.googleapis.com/Bucket" \
    --content-type=resource \
    --format=json

# API 调用示例(Python)
from google.cloud import asset_v1
client = asset_v1.AssetServiceClient()
request = {
    "parent": "projects/my-project",
    "content_type": "RESOURCE",
    "asset_types": ["compute.googleapis.com/Instance"]
}
response = client.list_assets(request)
for asset in response:
    print(asset.name)

关键差距:GCP 的存储桶默认是公开的?不对。GCP 的权限模型是统一 IAM,没有独立的桶 ACL(除非你开启 FM 模式)。所以拉 GCS 桶时,必须同时拉它绑定的 IAM 策略,否则你判断不了对象是否可公开访问。

六、统一资产发现框架的设计思路

你不可能为每家云写一套完全独立的发现引擎。我接过一个活儿,需要同时扫 AWS 和 Azure,当时我设计了一个统一的 Provider 接口:

  1. 定义抽象类:包含 discover_compute()、discover_storage()、discover_network() 三个方法。
  2. 每个云实现一个具体类:内部处理各自的 API 分页、限流、凭证管理。
  3. 统一输出资产对象:不管来自 AWS 还是 GCP,最终输出相同的 Asset 结构(包括 ID、类型、区域、标签、关联的 IAM、网络策略)。

这样做的好处是,后续的接口测绘、敏感数据扫描都只需要面向这个统一的资产模型,不用再关心底层是哪个云。

<
抽象方法 AWS 实现 GCP 实现
discover_compute() DescribeInstances + DescribeAutoScalingGroups VirtualMachines.list + instance group.list Instances.list
discover_storage() ListBuckets + DescribeRDSInstances BlobAccounts.list + CosmosDB.list Buckets.list + SQLInstances.list
discover_network() DescribeSecurityGroups + DescribeVpcs NetworkSecurityGroups.list + VirtualNetworks.list Firewalls.list + Vpc.list
凭证处理 AssumeRole + IAM User Service Principal + Managed Identity Service Account + Workload Identity

七、避坑指南与经验总结

做了这么多项目,我总结了几条云资产发现必须注意的「事故高发区」:

  • 跨区域遗漏:AWS 有 30+ 区域,Azure 有 60+ 区域。很多团队只扫了 default 区域,结果数据泄漏发生在 ap-southeast-1。
  • 非标准资源:比如 AWS 的 aws:elasticbeanstalk 不是标准 EC2 资源,你用 DescribeInstances 扫不到。得额外调用 Elastic Beanstalk 的 API。
  • 对象级别的权限:拉 S3 桶的 ACL 很容易,但桶内每个对象的 Bucket Policy 不一定能通过一次 API 拿到。你有时需要模拟访问。
  • 频率限制:GCP 的有限配额是每 100 秒 1000 次 API 调用。如果项目很多,必须设计退避算法(exponential backoff)。

我曾经在 Azure 上遇到过:订阅本身没有关联任何资源组,但 Resource API 返回了空列表。我当时以为这个账号没问题,后来发现客户把生产资源藏在另一个订阅下。所以资产管理的第一原则永远是:全量发现,不要相信「默认」。

好了,云环境资产发现的框架和实操要点基本就是这些。你可以基于这个思路,在 DSPM 资产发现引擎 模块里搭一个统一的 Provider 层。代码不用多,关键是搞清楚每种云 API 的逻辑差异,然后设计好重试、去重和关联规则。