其实文档都在那里,只是我找不到。

TLDR

如何设置阿里云的 RAM 用户只能更改子域名的 DNS 解析?

  1. 把子域名加入云解析DNS-公网权威解析
  2. 添加自定义权限策略如下
  3. 授权 RAM 用户
  4. 如果不成功,需等待第一步产生的 SOA 记录同步给证书颁发机构的 DNS
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
  "Version": "1",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "alidns:*",
      "Resource": "acs:alidns:*:*:domain/a.example.com"
    },
    {
      "Effect": "Allow",
      "Action": [
        "alidns:DescribeDomains",
        "alidns:DescribeDomainRecords"
      ],
      "Resource": "*"
    }
  ]
}

Caddy

最近试用 Caddy, 于是研究自动更新 HTTPS 证书。

这里插播一条广告,Caddy 真好用啊。 连编译插件这种事情都帮你安排好了,直接用 xcaddy 就行。

因为机器在内网,所以一开始的想法是在公网申请证书,然后传到内网; 后来反应过来,用 DNS-01 认证并不需要服务器对公网可见, 于是打算直接在内网申请。 这就需要时不时地去阿里云上改 DNS 解析。

然而,之前设置的阿里云 RAM 账号是有 AliyunDNSFullAccess 的, 也就是说可以更改整个域名的 DNS, 这显然不够安全。 即使阿里云的“系统权限策略参考”是这么说的。

给 Caddy 一个只有子域名权限的 RAM 授权显然是个好主意。可是怎么做呢?

RAM

首先自然是试试就试试,于是创建自定义策略:

1
2
3
4
5
{
    "Effect": "Allow",
    "Action": "alidns:*",
    "Resource": "acs:alidns:*:*:domain/a.example.com, acs:alidns:*:*:domain/*.a.example.com"
}

结果显然不成功。看到这篇博客以及阿里云的说明文档之后才发现:

由于权限只能细分到域名粒度,若希望RAM用户只可以管理某一条解析记录,如demo.example.com而不是整个example.com,则需要将deom.example.com添加为一个独立的子域名,参考子域管理,再通过上面的方式进行授权即可。

博客原文中给出的 alidns:Describe* 部分过于宽泛,经过实验,只需要TLDR里的 alidns:DescribeDomainsalidns:DescribeDomainRecords 即可。

以及 Resource 语法里面不能直接 acs:alidns:::* 了,需要在冒号中间加入星号。 (这个的提示倒是简单明了,在权限策略编辑器里直接报错。)

至于我是如何试错的呢?

自助诊断

在 API 调用阶段,阿里云会返回形如 https://api.aliyun.com/troubleshoot?q=Forbidden.RAM&product=Alidns&requestId=&accounttraceid= 的 URL, 可以直接点击进行排查。

点进去之后会有详细的诊断,包括提议加上 AliyunDNSFullAccess (上面提到,这并不十分安全), 以及请求参数和响应内容。

值得一提的是,响应内容里有一段 EncodedDiagnosticMessage 信息,可以通过相应的 API 进行解密。 比如 DNS 请求的 DomainNameRR, 会被翻译为具体的 AuthResource.

Zone

经过一些试错,发现 acs:alidns:*:*:domain/*.a.example.com 这一段是不需要的,删除之。

于是在客户端软件运行 DNS-01, 还是失败了。为什么呢?

细看请求参数:

1
2
3
4
{
    "DomainName":"example.com",
    "RR": "_acme-challenge.a",
}

这里的 DomainName 并不是子域名,而是域名本身;所以具体的 AuthResource 还是 example.com, 权限被拒绝了。

那怎样把请求的 DomainName 改成子域名呢?

寻找一番,发现客户端软件使用的库是 go-acme/lego. 这里的关键是 FindZoneByFqdn 函数,它会逐渐地请求上一级域名的 DNS 信息,直到找到 SOA 记录的位置。

至于 SOA 记录如何添加,这个在“添加为一个独立的子域名”这一步就由阿里云自动完成了。

于是得出结论,是证书颁发机构的 DNS 服务器还没更新; 1小时后请求证书,果然成功了。

不知道,不清楚

“需要添加独立的子域名”是文档里写了的,是我想当然了;但 Resource 怎么写就很难知道了。

访问控制 RAM 的文档里说,“与服务相关的资源描述部分,其语义由具体云服务指定”。 但在系统策略参考里搜索 DNS 只能找到宽泛的描写,比如 "Resource": "*" 或者 alidns:Query*; 权限策略示例库也没有 DNS 的内容。

通过 DecodeDiagnosticMessage 倒是能看到试图授权的 AuthResource, 但 通配符是否需要写,或者 域名解析里的粒度,以及 需要添加独立的子域名才能更改粒度 这样的信息,就只能自己摸索了。

当然,写这么多也不是抱怨,毕竟 ACL 确实是一个难题。 业务整天变更,各种不同的系统需要统一管理;大企业的 IT 甚至会专门聘请人员管理各种内部和外部的权限。

能让我自己没问客服(博客的作者提到了客服,显然是问了的)就能把事情做完,这本身就是一种成就。

写到这里的时候随便看了一眼文档,哇,还有自动转换大小写

无需关注大小写,系统会自动帮您转化为标准的ARN。

几句之后却又区分了起来:

Principal元素中,ARN的值区分大小写, 请使用系统提供的身份提供商的标准ARN信息。

鸣谢

阿里云如何配置子域名及其对应 RAM 访问权限 - SangriaChant