3、数据资产发现:云上数据源自动发现

做DSPM第一步是什么?我个人习惯先搞定“资产清单”。连自己有多少数据桶、数据库、集群都不知道,后续的安全策略就是盲打。这一节我们就聊聊怎么在混合云里自动摸清家底,而且不装Agent——也就是所谓的无代理扫描。

核心一句话: 无代理扫描 = 通过云厂商API + 网络探测 + 元数据解析,拿到数据源的“身份证”。

3.1 云上数据源有哪些?

混合云环境里,数据源通常分三类:

  • 对象存储 —— AWS S3、阿里云OSS、华为OBS、腾讯COS,还有MinIO这种私有化部署的。
  • 关系型/非关系型数据库 —— RDS(MySQL、PostgreSQL)、自建数据库、Redis、MongoDB、DynamoDB等。
  • 大数据平台 —— EMR、Hive、Spark、ClickHouse、Snowflake……还有云原生数据湖(如AWS Lake Formation)。

你想想看,如果每个云账号下有几十个桶、上百个DB实例,手工点一遍根本不可能。我在一个金融客户项目里遇到过,对方有8个AWS账号、3个阿里云账号,对象存储总数超过2000个桶。没有自动发现,光资产登记就做了两个月。

3.2 无代理扫描是怎么工作的?

说白了,无代理扫描就是“只看不碰”。它不会在你的虚拟机或容器里装任何agent,而是换个思路:用云平台的API把元数据拉回来。

  1. 身份与权限对接 —— 用只读角色或服务账号对接云厂商的IAM/STS。
  2. 调用列举接口 —— 例如ListBuckets、DescribeDBInstances、ListClusters。
  3. 解析返回的元数据 —— 获取名称、区域、大小、ACL、标签、创建时间等。
  4. 补充网络探测 —— 有些自建实例没有暴露API,可以通过端口扫描、DNS解析、云VPC流日志来发现。
  5. 归一化资产指纹 —— 把不同云的字段统一成标准资产模型。
曾经踩过的坑: 我曾以为只要API权限够就行,结果在某个客户环境里,AWS S3的ListBuckets只能列出桶名,但桶内对象的ACL和加密信息需要额外调用GetBucketPolicy。后来我改成递归调用Describe*接口,才补全了敏感字段。
无代理扫描资产发现流程 混合云/多云 API适配器层 对象存储 数据库 大数据平台 元数据采集引擎 统一资产指纹库 风险与策略引擎 只读API调用 无需安装Agent 支持增量扫描

3.3 对象存储自动发现实操

不同云的对象存储API长得像,但细节不同。我们拿AWS S3和阿里云OSS做个对比。

能力 AWS S3 阿里云 OSS
列举所有桶 s3.list_buckets() oss.list_buckets()
获取桶ACL s3.get_bucket_acl(Bucket=...) oss.get_bucket_acl(bucket_name=...)
桶内对象列表 s3.list_objects_v2(Bucket=...) oss.list_objects(bucket_name=...)
版本控制状态 s3.get_bucket_versioning(Bucket=...) 通过BucketPolicy判断
小技巧: 我习惯把不同云厂商的API调用封装成统一接口类,输出一个标准的“资产记录”:{cloud_provider, region, name, type, created_at, owner, access_level, encryption_status}。这样后续分析策略就不用关心底层云了。

3.4 数据库与大数据平台发现

数据库的发现稍微复杂些,因为有些是云托管实例(RDS),有些是自己部署在ECS上的。无代理扫描没法连进数据库执行SQL,而是靠以下方式:

  • RDS类实例 —— 调用DescribeDBInstances,拿VPC、端口、引擎类型、存储大小、备份状态。
  • 自建数据库 —— 先通过云VPC的ip scan探测端口开放(3306, 5432, 6379等),再用DNS反向解析或标签推测归属。
  • 大数据组件 —— EMR调用ListClusters,Hive通过MetaStore API获取Table列表,Spark通过History Server暴露的REST接口。
# 伪代码示例:统一资产发现引擎
def discover_all(cloud_accounts):
    assets = []
    for account in cloud_accounts:
        # 对象存储
        for bucket in account.s3.list_buckets():
            assets.append(Asset(
                type='object_storage',
                name=bucket.name,
                region=account.region,
                tags=bucket.tags
            ))
        # RDS
        for instance in account.rds.describe_instances():
            assets.append(Asset(
                type='rds',
                engine=instance.engine,
                endpoint=instance.endpoint,
                ...
            ))
        # EMR集群
        for cluster in account.emr.list_clusters():
            assets.append(Asset(
                type='emr',
                cluster_id=cluster.id,
                applications=cluster.applications
            ))
    return assets

3.5 资产指纹的归一与去重

你会发现,同一个资产可能在不同API里重复出现。比如RDS实例既有EC2标签,又有RDS标签。我记得有一次调试时,一个MySQL实例被扫出两份记录,因为从EC2和RDS两个维度都发现了它。解决办法是用ARN或抽象资源ID作为唯一键。

  • 对象存储:arn:aws:s3:::bucket-name 或 oss://bucket-name
  • 数据库实例:arn:aws:rds:region:account:db:instance-name
  • EMR集群:arn:aws:elasticmapreduce:region:account:cluster/j-xxxx
曾经踩过的坑: 有个客户用多个云账号,不同region的资产名恰巧一样。我用名称+Region作为主键,后来发现跨region复制会生成同名对象。最终改用云账号ID + Region + 内部ID才搞定去重。

3.6 扫描频率与增量更新

全量扫描很费时(尤其是上千个桶),所以我建议这样:

  1. 首次部署先全量扫一次,生成基线资产库。
  2. 之后用事件驱动增量更新 —— 监听CloudTrail/ActionTrail日志,发现CreateBucket、CreateDBInstance就自动触发扫。
  3. 如果没有event支持,那就每天1~2次增量扫描(只扫最近24小时内创建或修改过的资源)。
避坑: 别把全量扫描直接放生产业务高峰期。我曾经让定时任务在每天凌晨1点跑,结果某个客户刚好在那个时间做备份,API限流了。后来改成错峰执行。

3.7 无代理扫描的权限最小化

你给的只读角色应该只包含必要的权限。下面是一个最小权限示例(AWS IAM):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListAllMyBuckets",
        "s3:GetBucketLocation",
        "s3:GetBucketAcl",
        "s3:GetBucketVersioning",
        "rds:DescribeDBInstances",
        "rds:DescribeDBClusters",
        "elasticmapreduce:ListClusters",
        "elasticmapreduce:DescribeCluster"
      ],
      "Resource": "*"
    }
  ]
}

嗯,这里要注意ListAllMyBuckets必须放*,其他可以通过条件限制region。我在交付时都会跟客户过一遍这个json,确保不会误给写权限。

核心观点: 资产发现不是一次性工作,它是一个持续不断地“拉” + “听” + “查”的闭环。无代理扫描是起点,但后面的分级、标记、风险分析全靠这张资产地图。

有了准确的数据资产清单,后面我们才能做敏感数据分类、权限分析和风险评级。别着急,一步步来。