

# 为网关终端节点设置自定义域名
<a name="gateway-custom-domains"></a>

默认情况下，网关终端节点 AWS的格式`<gateway-id>.gateway.bedrock-agentcore.<region>.amazonaws.com`为托管域名。对于生产环境或为了创建更加用户友好的体验，您可能需要为网关终端节点使用自定义域名。本节将指导您使用亚马逊 CloudFront 作为反向代理来设置自定义域名。

## 先决条件
<a name="gateway-custom-domains-prereq"></a>

在开始之前，请确保您满足以下条件：
+ 有效的网关终端节点
+ DNS 委托（如果您的 Route 53 域名需要可供公众访问）
+  AWS 已安装并配置 CDK（如果采用 CDK 方法）
+ 创建和管理 CloudFront 分配、Route 53 托管区域和 ACM 证书的相应 IAM 权限

## 解决方案概述
<a name="gateway-custom-domains-overview"></a>

此解决方案包含以下组件：
+  **Route 53 托管区域**：管理自定义域名的 DNS 记录
+  **ACM 证书**：为您的自定义 SSL/TLS 域提供加密
+  **CloudFront 分发**：充当反向代理，将来自自定义域的请求转发到网关终端节点
+  **Route 53 A 记录**：将您的自定义域名映射到 CloudFront 分配

以下步骤将指导您使用 AWS CDK 设置这些组件。

## 实现步骤
<a name="gateway-custom-domains-steps"></a>

### 步骤 1：创建 Route 53 托管区域
<a name="gateway-custom-domains-hosted-zone"></a>

首先，为您的自定义域创建一个 Route 53 托管区域：

```
import { RemovalPolicy } from 'aws-cdk-lib';
import { PublicHostedZone } from 'aws-cdk-lib/aws-route53';

const domainName = 'my.example.com';

const hostedZone = new PublicHostedZone(this, 'HostedZone', {
    zoneName: domainName,
});
this.hostedZone.applyRemovalPolicy(RemovalPolicy.RETAIN);
```

**注意**  
我们采用的移除政策是`RETAIN`为了防止在堆栈更新或删除期间意外删除托管区域。

### 步骤 2：创建 DNS-validated 证书
<a name="gateway-custom-domains-certificate"></a>

接下来，使用带有 DNS 验证功能的 Certifice Manager (ACM) 为您的自定义域创建 AWS 证书： SSL/TLS 

```
import { RemovalPolicy } from 'aws-cdk-lib';
import { Certificate, CertificateValidation } from 'aws-cdk-lib/aws-certificatemanager';

const certificate = new Certificate(this, 'SSLCertificate', {
    domainName: domainName, // route53 hosted zone domain name from step 1
    validation: CertificateValidation.fromDns(hostedZone), // route53 hosted zone from step 1
});
this.certificate.applyRemovalPolicy(RemovalPolicy.RETAIN);
```

DNS 验证会自动在您的 Route 53 托管区域中创建必要的验证记录。

### 步骤 3：创建分 CloudFront 配
<a name="gateway-custom-domains-cloudfront"></a>

创建 CloudFront 分配以充当网关终端节点的反向代理：

```
import {
  AllowedMethods,
  CachePolicy,
  Distribution,
  OriginProtocolPolicy,
  ViewerProtocolPolicy
} from 'aws-cdk-lib/aws-cloudfront';
import { HttpOrigin } from 'aws-cdk-lib/aws-cloudfront-origins';

const bedrockAgentCoreGatewayHostName = '<mymcpserver>.gateway.bedrock-agentcore.<region>.amazonaws.com'
const bedrockAgentCoreGatewayPath = '/mcp' // can also be left undefined, depending on your requirement

const distribution = new Distribution(this, 'Distribution', {
    defaultBehavior: {
        origin: new HttpOrigin(bedrockAgentCoreGatewayHostName, {
            protocolPolicy: OriginProtocolPolicy.HTTPS_ONLY,
            originPath: bedrockAgentCoreGatewayPath,
        }),
        viewerProtocolPolicy: ViewerProtocolPolicy.HTTPS_ONLY,
        cachePolicy: CachePolicy.CACHING_DISABLED, // important since caching is enabled by default and hence is not suitable for a reverse proxy
        allowedMethods: AllowedMethods.ALLOW_ALL,
    },
    domainNames: [domainName], // route53 hosted zone domain name from step 1
    certificate: certificate, // ssl certificate for the route53 domain from step 2
});
```

**重要**  
设置`cachePolicy: CachePolicy.CACHING_DISABLED`为确保 CloudFront 不会缓存来自网关终端节点的响应，这对于动态 API 交互非常重要。

`<mymcpserver>`替换为您的网关 ID `<region>` 和您的 AWS 区域（例如`us-east-1`）。

### 步骤 4：创建 Route 53 A 记录
<a name="gateway-custom-domains-dns-record"></a>

创建 Route 53 一条将您的自定义域指向 CloudFront 分布的记录：

```
import { ARecord, RecordTarget } from 'aws-cdk-lib/aws-route53';
import { CloudFrontTarget } from 'aws-cdk-lib/aws-route53-targets';

const aRecord = new ARecord(this, 'AliasRecord', {
    zone: hostedZone, // route53 hosted zone from step 1
    recordName: domainName, // route53 hosted zone domain name from step 1
    target: RecordTarget.fromAlias(new CloudFrontTarget(distribution)), // cloudfront distribution from step 3
});
```

这将创建将您的自定义域名映射到 CloudFront 分配的别名记录。

### 步骤 5：部署您的基础架构
<a name="gateway-custom-domains-deploy"></a>

部署 CDK 堆栈来创建资源：

```
cdk deploy
```

部署过程可能需要一些时间，尤其是在证书验证和 CloudFront 分发创建方面。

## 测试您的自定义域名
<a name="gateway-custom-domains-testing"></a>

部署基础设施后，请验证您的自定义域名配置是否正确：

### 验证 DNS 解析度
<a name="gateway-custom-domains-testing-dns"></a>

使用`dig`命令验证您的自定义域名是否已解析为 CloudFront 分发：

```
dig my.example.com
```

输出应显示您的域名解析为 CloudFront的 IP 地址。

### 验证 SSL 证书
<a name="gateway-custom-domains-testing-ssl"></a>

`curl`用于验证 SSL 证书的配置是否正确：

```
curl -v https://my.example.com
```

输出应显示成功的 SSL 握手且没有证书错误。

## 配置 MCP 客户端
<a name="gateway-custom-domains-client-config"></a>

设置并验证自定义域名后，您可以将 MCP 客户端配置为使用它：

### 游标配置
<a name="gateway-custom-domains-client-config-cursor"></a>

对于 Cursor，请更新您的配置文件：

```
{
  "mcpServers": {
    "my-mcp-server": {
      "url": "https://my.example.com"
    }
  }
}
```

### 其他 MCP 客户
<a name="gateway-custom-domains-client-config-other"></a>

对于原生不支持可流式传输 HTTP 的 MCP 客户端：

```
{
  "mcpServers": {
    "my-mcp-server": {
      "command": "/path/to/uvx",
        "args": [
            "mcp-proxy",
            "--transport",
            "streamablehttp",
            "https://my.example.com"
        ]
    }
  }
}
```

## 其他注意事项
<a name="gateway-custom-domains-considerations"></a>

 **所涉费用问题**   
 CloudFront 用作反向代理会产生额外的数据传输和请求处理成本。查看定 CloudFront 价模型，了解您的特定用例的成本影响。

 **安全注意事项**   
考虑实施其他安全措施，例如：  
+ 保护您的终端节点免受常见 Web 漏洞攻击的 WAF 规则
+ Geo-restrictions 限制对特定地理区域的访问
+ 自定义标头或请求签名以添加额外的身份验证层

 **监控和日志记录**   
启用 CloudFront 访问日志并配置 CloudWatch 警报，以监控自定义域设置的运行状况和性能。

 **证书续订**   
只要 DNS 记录保持不变，通过 DNS 验证颁发的 ACM 证书就会自动续订。确保不要删除验证记录。

 **带有自定义域的 OAuth 保护资源端点**   
默认情况下，`/.well-known/oauth-protected-resource`终端节点返回的资源网址包含网关域而不是您的自定义域。这可能会导致 OAuth 客户端在使用自定义域名时无法进行身份验证。  
要解决此问题，您可以实现一个 Lambda @Edge 函数，该函数可拦截 OAuth 发现响应并生成带有正确自定义域 URL 的新响应。方法如下：  
+  **将 Lambda @Edge 与 ORIGIN\_RESPONSE 事件类型配合使用：创建一个在源响应**上触发的函数，以拦截 OAuth 保护的资源端点响应。
+  **生成新响应**：Lambda @Edge 无法读取源响应正文，因此，与其修改现有响应，不如使用自定义域生成全新的 JSON 响应。
+  **与 CloudFront 行为关联**：将 Lambda @Edge 函数配置为专门针对`/.well-known/oauth-protected-resource`路径模式触发。

  实施此解决方案后，受 OAuth 保护的资源端点将返回正确的自定义域：

  ```
  curl https://my-custom-domain.com/.well-known/oauth-protected-resource
  {
    "authorization_servers": ["https://my-org.okta.com/oauth2/default"],
    "resource": "https://my-custom-domain.com/mcp"
  }
  ```
**注意**  
虽然 Lambda @Edge 为这个问题提供了解决方案，但在没有内置支持的情况下为 AgentCore Gateway 实现自定义域需要额外的复杂性，这可能不是所有客户的最佳选择。在使用自定义域名对 OAuth 发现提供原生支持之前，可以考虑将此方法作为一种解决方法。

## 问题排查
<a name="gateway-custom-domains-troubleshooting"></a>

 **DNS 解析问题**   
如果您的自定义域名无法正确解析：  
+ 验证您的 Route 53 托管区域中是否正确配置了 A 记录
+ 在域名注册商处检查您的域名服务器是否设置正确
+ 为 DNS 传播留出时间（在某些情况下最长可达 48 小时）

 **SSL 证书问题**   
如果您遇到 SSL 证书错误：  
+ 在 ACM 控制台中验证证书是否已颁发并处于活动状态
+ 检查证书是否与您的 CloudFront 发行版关联正确
+ 确保证书涵盖您正在使用的确切域名

 **网关连接问题**   
如果您的自定义域名未连接到您的网关：  
+ 验证 CloudFront 分配中的源域和路径是否正确
+ 检查您的网关终端节点是否可以直接访问
+ 查看 CloudFront 分发日志中是否存在任何错误

## 结论
<a name="gateway-custom-domains-conclusion"></a>

为 Gateway 终端节点设置自定义域名可以增强应用程序的专业外观，并提供管理 API 终端节点的灵活性。按照本指南中概述的步骤，您可以使用 CloudFront 作为反向代理来创建安全可靠的自定义域配置。

有关网关特性和功能的更多信息，请参阅 [Amazon Bedrock AgentCore Gateway：将工具和其他资源安全地连接到您的网关](gateway.md)。