将阿里云 DNS 权限验证限制为六秒
This commit is contained in:
parent
3c1fce99be
commit
0e4ed9b2d9
@ -85,7 +85,7 @@ func TestDNSCheckReportsLiveResultWithoutPersistingConfiguration(t *testing.T) {
|
||||
t.Fatal("权限请求必须设置超时")
|
||||
}
|
||||
remaining := time.Until(deadline)
|
||||
if remaining < 9*time.Second || remaining > 11*time.Second {
|
||||
if remaining < 5*time.Second || remaining > 7*time.Second {
|
||||
t.Fatalf("阿里云 CLI 权限验证时限错误:%s", remaining)
|
||||
}
|
||||
report := dnsprovider.AuthorizationReport{Provider: "aliyun", RequestedDomain: name, Verified: outcome == "allowed"}
|
||||
|
||||
@ -20,7 +20,7 @@ const (
|
||||
aliyunDefaultRegion = "cn-hangzhou"
|
||||
aliyunAuthorizationTTL = 600
|
||||
aliyunAuthorizationTimeout = 30 * time.Second
|
||||
aliyunAPICallTimeout = 3 * time.Second
|
||||
aliyunAPICallTimeout = 2 * time.Second
|
||||
)
|
||||
|
||||
// AuthorizationDomain 描述一个阿里云 DNS 根域名的授权探测结果。
|
||||
@ -54,7 +54,7 @@ type AliyunAuthorizationChecker struct {
|
||||
}
|
||||
|
||||
func NewAliyunAuthorizationChecker() *AliyunAuthorizationChecker {
|
||||
return &AliyunAuthorizationChecker{now: time.Now, recordAPI: newAliyunRecordAPI}
|
||||
return &AliyunAuthorizationChecker{now: time.Now, recordAPI: newAliyunAuthorizationRecordAPI}
|
||||
}
|
||||
|
||||
func (c *AliyunAuthorizationChecker) Supports(providerID string) bool {
|
||||
@ -270,6 +270,19 @@ func (c *AliyunAuthorizationChecker) currentTime() time.Time {
|
||||
}
|
||||
|
||||
func newAliyunClient(credentials map[string]string) (*client.Client, error) {
|
||||
return newAliyunClientWithRetryOptions(credentials, nil)
|
||||
}
|
||||
|
||||
// newAliyunAuthorizationClient 为权限探测建立独立的单次调用客户端。
|
||||
// 探测流程有严格的阶段时限,不能因 SDK 重试或退避拉长用户等待;证书签发仍使用普通客户端策略。
|
||||
func newAliyunAuthorizationClient(credentials map[string]string) (*client.Client, error) {
|
||||
return newAliyunClientWithRetryOptions(credentials, &dara.RetryOptions{
|
||||
Retryable: false,
|
||||
MaxAttempts: 1,
|
||||
})
|
||||
}
|
||||
|
||||
func newAliyunClientWithRetryOptions(credentials map[string]string, retryOptions *dara.RetryOptions) (*client.Client, error) {
|
||||
cleaned, err := ValidateCredentials(Aliyun, credentials)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -280,6 +293,9 @@ func newAliyunClient(credentials map[string]string) (*client.Client, error) {
|
||||
if token := cleaned["security_token"]; token != "" {
|
||||
config.SetSecurityToken(token)
|
||||
}
|
||||
if retryOptions != nil {
|
||||
config.SetRetryOptions(retryOptions)
|
||||
}
|
||||
result, err := client.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建阿里云 DNS 客户端失败:%w", err)
|
||||
|
||||
@ -7,6 +7,34 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestAliyunAuthorizationRecordAPIUsesSingleAttemptWithoutRetry(t *testing.T) {
|
||||
api, err := newAliyunAuthorizationRecordAPI(scopeCredentials())
|
||||
if err != nil {
|
||||
t.Fatalf("创建阿里云权限探测客户端失败:%v", err)
|
||||
}
|
||||
records, ok := api.(aliyunRecords)
|
||||
if !ok || records.client == nil || records.client.RetryOptions == nil {
|
||||
t.Fatalf("权限探测必须使用带显式重试策略的专用客户端:%T", api)
|
||||
}
|
||||
if records.client.RetryOptions.Retryable || records.client.RetryOptions.MaxAttempts != 1 {
|
||||
t.Fatalf("权限探测客户端不得自动重试:%+v", records.client.RetryOptions)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAliyunACMERecordAPIDoesNotUseAuthorizationRetryPolicy(t *testing.T) {
|
||||
api, err := newAliyunRecordAPI(scopeCredentials())
|
||||
if err != nil {
|
||||
t.Fatalf("创建阿里云 ACME 客户端失败:%v", err)
|
||||
}
|
||||
records, ok := api.(aliyunRecords)
|
||||
if !ok || records.client == nil {
|
||||
t.Fatalf("ACME 客户端类型错误:%T", api)
|
||||
}
|
||||
if records.client.RetryOptions != nil {
|
||||
t.Fatalf("ACME 客户端不应复用权限探测专用重试策略:%+v", records.client.RetryOptions)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAliyunAuthorizationCheckerRechecksEveryRequestAndDetectsRevocation(t *testing.T) {
|
||||
now := time.Date(2026, 9, 14, 12, 0, 0, 0, time.UTC)
|
||||
calls := 0
|
||||
|
||||
@ -207,7 +207,7 @@ func TestAliyunAuthorizationVerifyCleansUpAfterRequestCancellation(t *testing.T)
|
||||
}
|
||||
|
||||
func TestPermissionVerificationTimeoutForAliyunIsIndependentFromPropagation(t *testing.T) {
|
||||
if got := PermissionVerificationTimeoutFor(Aliyun); got != 10*time.Second {
|
||||
if got := PermissionVerificationTimeoutFor(Aliyun); got != 6*time.Second {
|
||||
t.Fatalf("阿里云权限验证时限错误:%s", got)
|
||||
}
|
||||
if got := PermissionVerificationTimeoutFor(Cloudflare); got != 60*time.Second {
|
||||
|
||||
@ -78,6 +78,15 @@ func newAliyunRecordAPI(credentials map[string]string) (aliyunRecordAPI, error)
|
||||
return aliyunRecords{client: dnsClient}, nil
|
||||
}
|
||||
|
||||
// newAliyunAuthorizationRecordAPI 仅供短时权限探测使用,不改变 ACME DNS-01 的客户端策略。
|
||||
func newAliyunAuthorizationRecordAPI(credentials map[string]string) (aliyunRecordAPI, error) {
|
||||
dnsClient, err := newAliyunAuthorizationClient(credentials)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return aliyunRecords{client: dnsClient}, nil
|
||||
}
|
||||
|
||||
// 不使用自动发现托管区的上游适配器,也不沿 CNAME 自动扩大写入范围。
|
||||
type scopedAliyunProvider struct {
|
||||
scope AliyunScope
|
||||
|
||||
@ -40,7 +40,7 @@ const (
|
||||
|
||||
// AliyunPermissionVerificationTimeout 限制阿里云账号内的创建、查询和清理闭环。
|
||||
// 该流程不等待公网 DNS 传播。
|
||||
AliyunPermissionVerificationTimeout = 10 * time.Second
|
||||
AliyunPermissionVerificationTimeout = 6 * time.Second
|
||||
|
||||
dnsCleanupTimeout = 30 * time.Second
|
||||
defaultDNSPermissionTXTLookupTimeout = 30 * time.Second
|
||||
|
||||
@ -81,8 +81,8 @@ func TestAdminAliyunDNSTestReportsFastVerificationTimeout(t *testing.T) {
|
||||
}`))
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
serveAdminHTTP(server, response, request)
|
||||
if response.Code != http.StatusOK || !strings.Contains(response.Body.String(), `"verification_timeout_secs":10`) {
|
||||
t.Fatalf("管理端阿里云 DNS 在线测试未返回 10 秒等待上限:%d %s", response.Code, response.Body.String())
|
||||
if response.Code != http.StatusOK || !strings.Contains(response.Body.String(), `"verification_timeout_secs":6`) {
|
||||
t.Fatalf("管理端阿里云 DNS 在线测试未返回 6 秒等待上限:%d %s", response.Code, response.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -298,14 +298,14 @@ func TestSetupAliyunDNSTestUsesFastAccountAPITimeout(t *testing.T) {
|
||||
if response.Code != http.StatusUnprocessableEntity {
|
||||
t.Fatalf("模拟阿里云 DNS 验证失败应返回 422,实际为 %d:%s", response.Code, response.Body.String())
|
||||
}
|
||||
if !strings.Contains(response.Body.String(), `"verification_timeout_secs":10`) {
|
||||
t.Fatalf("阿里云 DNS 验证失败响应应包含 10 秒等待上限:%s", response.Body.String())
|
||||
if !strings.Contains(response.Body.String(), `"verification_timeout_secs":6`) {
|
||||
t.Fatalf("阿里云 DNS 验证失败响应应包含 6 秒等待上限:%s", response.Body.String())
|
||||
}
|
||||
if !verifier.verifyHasDeadline {
|
||||
t.Fatal("阿里云 DNS 在线测试应收到带截止时间的上下文")
|
||||
}
|
||||
remaining := verifier.verifyDeadline.Sub(started)
|
||||
if remaining < 9*time.Second || remaining > 11*time.Second {
|
||||
if remaining < 5*time.Second || remaining > 7*time.Second {
|
||||
t.Fatalf("阿里云权限验证不应继承 ACME 传播配置:剩余时间=%s", remaining)
|
||||
}
|
||||
}
|
||||
@ -580,7 +580,7 @@ func TestSetupDNSTestReturnsNotImplementedForUnavailableProvider(t *testing.T) {
|
||||
if strings.Contains(response.Body.String(), "access_key_secret") || strings.Contains(response.Body.String(), "secret") {
|
||||
t.Fatal("未开放 Provider 响应不应包含提交的凭据")
|
||||
}
|
||||
if !strings.Contains(response.Body.String(), `"verification_timeout_secs":10`) {
|
||||
if !strings.Contains(response.Body.String(), `"verification_timeout_secs":6`) {
|
||||
t.Fatalf("未开放 Provider 响应应包含当前最大等待时间:%s", response.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||
|
||||
const defaultDNSVerificationTimeoutSeconds = 60;
|
||||
const aliyunDNSVerificationTimeoutSeconds = 10;
|
||||
const aliyunDNSVerificationTimeoutSeconds = 6;
|
||||
|
||||
function dnsVerificationTimeoutFor(providerID) {
|
||||
return String(providerID || "").trim().toLowerCase() === "aliyun"
|
||||
|
||||
@ -103,7 +103,7 @@ test("管理设置 DNS 验证显示等待进度,并在结束后停止计时",
|
||||
|
||||
assert.equal(state.dnsTesting.value, true);
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
assert.equal(state.dnsVerificationTimeoutSeconds.value, 10);
|
||||
assert.equal(state.dnsVerificationTimeoutSeconds.value, 6);
|
||||
advanceTime(3200);
|
||||
runIntervals();
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 3);
|
||||
@ -113,12 +113,12 @@ test("管理设置 DNS 验证显示等待进度,并在结束后停止计时",
|
||||
credential_ref: "secret://test/only",
|
||||
verification_token: "test-token",
|
||||
expires_at: new Date(Date.now() + 60000).toISOString(),
|
||||
verification_timeout_secs: 10,
|
||||
verification_timeout_secs: 6,
|
||||
}));
|
||||
await task;
|
||||
assert.equal(state.dnsTesting.value, false);
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
assert.equal(state.dnsVerificationTimeoutSeconds.value, 10);
|
||||
assert.equal(state.dnsVerificationTimeoutSeconds.value, 6);
|
||||
advanceTime(1000);
|
||||
runIntervals();
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
|
||||
|
||||
const defaultDNSVerificationTimeoutSeconds = 60;
|
||||
const aliyunDNSVerificationTimeoutSeconds = 10;
|
||||
const aliyunDNSVerificationTimeoutSeconds = 6;
|
||||
|
||||
function dnsVerificationTimeoutFor(providerID) {
|
||||
return String(providerID || "").trim().toLowerCase() === "aliyun"
|
||||
|
||||
@ -34,7 +34,7 @@ test("初始化 DNS 验证显示等待进度,并采用服务端返回的最长
|
||||
|
||||
assert.equal(state.dnsTesting.value, true);
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
assert.equal(state.dnsVerificationTimeoutSeconds.value, 10);
|
||||
assert.equal(state.dnsVerificationTimeoutSeconds.value, 6);
|
||||
|
||||
advanceTime(2500);
|
||||
runIntervals();
|
||||
@ -45,12 +45,12 @@ test("初始化 DNS 验证显示等待进度,并采用服务端返回的最长
|
||||
credential_ref: "secret://test/only",
|
||||
verification_token: "test-token",
|
||||
expires_at: new Date(Date.now() + 60000).toISOString(),
|
||||
verification_timeout_secs: 10,
|
||||
verification_timeout_secs: 6,
|
||||
}));
|
||||
await task;
|
||||
assert.equal(state.dnsTesting.value, false);
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
assert.equal(state.dnsVerificationTimeoutSeconds.value, 10);
|
||||
assert.equal(state.dnsVerificationTimeoutSeconds.value, 6);
|
||||
|
||||
advanceTime(1000);
|
||||
runIntervals();
|
||||
|
||||
@ -706,7 +706,7 @@ const {
|
||||
:max="form.propagation_timeout_secs"
|
||||
required
|
||||
/>
|
||||
<small>默认 10 秒,不能大于最大等待时间。</small>
|
||||
<small>默认 6 秒,不能大于最大等待时间。</small>
|
||||
</label>
|
||||
</div>
|
||||
<p v-if="dnsCatalogError" class="inline-error">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user