网络设计¶
一句话
一个包能不能到达服务器,要过三关:路由(有没有路)→ Security List / NSG(云上的防火墙让不让过)→ OS 的防火墙。 设计从"服务器不要公网 IP"出发:入口只留 LB,运维走 Bastion,出口按用途分开。
本页整理自 Phase 1、Phase 2、Phase 4、Phase 9 的实验。OKE 特有的网络(Pod 网络、API endpoint)在 Kubernetes 页。
1. 先认识这些组件¶
没碰过 OCI 的话,先对照 AWS 理解。名字不同,概念基本一样。
| OCI | AWS | 一句话 |
|---|---|---|
| VCN | VPC | 自己的私有网络,先定一个大的 CIDR(例:10.4.0.0/16) |
| Subnet | Subnet | VCN 切出来的一段。OCI 的 subnet 默认跨整个 region(AWS 是一个 AZ 一个) |
| public / private subnet | 同左 | OCI 用 prohibit_public_ip_on_vnic 区分:private subnet 里的 VM 不能有公网 IP |
| Route Table | Route Table | "去哪个目的地 → 交给哪个 Gateway",每个 subnet 绑一张 |
| Internet Gateway(IGW) | Internet Gateway | 双向连 Internet。只有 public subnet 用 |
| NAT Gateway | NAT Gateway | 只出不进:private 的 VM 可以下载东西,外面连不进来 |
| Service Gateway(SGW) | VPC Endpoint | 不经过 Internet,走 Oracle 内部网络访问 OCI 的服务(Object Storage、ADB、dnf 仓库…) |
| Security List(SL) | NACL(但 OCI 默认是 stateful) | subnet 级的防火墙,subnet 里所有 VM 都受它影响 |
| NSG | Security Group | VNIC(网卡)级的防火墙,只影响加入了这个 NSG 的 VM |
| Bastion | SSM Session Manager(近似) | 托管的跳板,从外面登录没有公网 IP 的 VM |
2. 原理:一个包要过的三关¶
flowchart TB
P["包"] --> R{"① 路由表<br/>有路吗?"}
R -->|没有| D1["丢弃<br/>Flow Logs 里也没有"]
R -->|有| S{"② Security List ∪ NSG<br/>有一个放行吗?"}
S -->|都没放行| D2["拒绝<br/>Flow Logs:REJECT"]
S -->|放行| F{"③ OS 防火墙<br/>端口开了吗?"}
F -->|没开| D3["拒绝"]
F -->|开了| OK["到达进程"]
① 路由决定"能不能到",安全规则决定"让不让进",两者互相独立。 安全规则全放行,没有路由照样不通;有路由,安全规则不放行也不通。
② Security List 和 NSG 是"并集"。 只要其中一个放行就能通,不是"两个都要放行"。
- 后果:想用 NSG 精确限制的端口,SL 里就不能宽泛放行。 SL 放行了整个 VCN 的 22,NSG 再怎么写"只允许 Bastion"也没有用
- AWS 正好相反:NACL 和 Security Group 是"两边都要放行"(交集)。从 AWS 转过来的人特别容易在这里栽跟头
③ 云上放行了,OS 里还有一道。 Oracle Linux 默认开着 firewalld,NSG 放行了 80,OS 不开也连不上。
补充两条规则:
- stateful:OCI 的规则默认是 stateful,进来的连接,返回的包自动放行,不用另写规则。但"出站 / 入站"是从每块网卡的角度看的。例:Bastion 的 private endpoint 也在 VM 的 subnet 里,它 → VM:22 对 endpoint 来说是"出站",SL 的出站规则也要放行
- 最长前缀匹配:一张路由表里
0.0.0.0/0 → NAT和Oracle 服务 → SGW同时存在时,去 Oracle 服务的流量匹配更具体的那条,走 SGW
3. 出口:三种 Gateway 怎么选¶
| 需求 | 用 | 例子 |
|---|---|---|
| 外面要能连进来 | Internet Gateway(IGW,只给 public subnet) | LB 所在的 subnet |
| VM 要下载外部的东西,但不想被连进来 | NAT Gateway(NAT GW) | 装 pip 包、拉 Docker Hub 的镜像 |
| VM 只访问 OCI 自己的服务 | Service Gateway(SGW) | dnf 仓库、Vault、Object Storage、ADB、监控 Agent |
# private subnet 的路由表(Phase 4,简化:实际代码用 dynamic 块按开关生成):两条规则,各走各的 Gateway
route_rules {
destination = "0.0.0.0/0"
network_entity_id = oci_core_nat_gateway.this[0].id # 普通的 Internet → NAT
}
route_rules {
destination = data.oci_core_services.all[0].services[0].cidr_block # "all-kix-services-in-oracle-services-network"
destination_type = "SERVICE_CIDR_BLOCK" # 目的地是"服务",IP 段由 OCI 维护
network_entity_id = oci_core_service_gateway.this[0].id
}
最安全的组合:只有 Service Gateway(SGW)
Phase 9 的私有 VM 只有 Service Gateway,没有 Internet Gateway 和 NAT Gateway:dnf 装包、Vault 取密码、Agent 上报都正常,但完全出不了公网。不需要 Internet 的服务器,就不给它 NAT Gateway。
4. 访问控制:Security List(SL)和 NSG 怎么分工¶
| Security List | NSG | |
|---|---|---|
| 作用范围 | subnet 里的所有 VM | 加入了这个 NSG 的 VM |
| 规则的来源 / 目的地 | 只能写 CIDR | CIDR,或者另一个 NSG |
| 适合放 | 整个 subnet 都需要的基本规则 | 按"层"(LB 层、app 层)写的业务规则 |
本仓库的做法:SL 只放最基本的,业务规则全写在 NSG 里,而且 NSG 之间互相引用。
# app 层只接受"来自 LB 层"的 80(Phase 4 security.tf)
resource "oci_core_network_security_group_security_rule" "app_http_from_lb" {
network_security_group_id = oci_core_network_security_group.app.id
direction = "INGRESS"
protocol = "6" # TCP
source = oci_core_network_security_group.lb.id # 来源写 NSG,不写 IP
source_type = "NETWORK_SECURITY_GROUP"
tcp_options {
destination_port_range {
min = 80
max = 80
}
}
}
来源写成"lb-nsg",LB 的 IP 变了、app VM 增减了,规则都不用改。
SL 里只留这两类:
- ICMP type 3 code 4(Path MTU Discovery):路径上某段 MTU 较小时,靠这个 ICMP 通知发送方把包改小。挡掉的话大包会无声无息地丢掉。OCI 默认的 SL 里也有这一条
- 出站全放行(出站的控制交给路由:没给 NAT 就出不去)
实际踩过的坑:SL 放行了整个 VCN 的 22
Phase 4 当初在 SL 里放行了"VCN 内 → 22",又在 NSG 里写了"22 只接受 Bastion",以为 SSH 只能从 Bastion 进来。 因为是并集,实际上 VCN 里谁都能连 22(例如 app-1 → app-2)。当时只测了"从 Bastion 能连上",没测"从别处连不上",所以没发现。 现在的代码已改成 SL 不放行 22,SSH 只由 NSG 控制(改完没有重新部署验证)。
5. 运维:怎么登录没有公网 IP 的服务器¶
| 方式 | 做法 | 用在 | 评价 |
|---|---|---|---|
| 公网 IP + SSH | SL / NSG 放行本机 IP 的 22 | — | 所在网络屏蔽了 22,用不了;本来也是攻击面 |
| Cloud Shell private network | Cloud Shell 接进 VCN 的 subnet,拿到私有 IP 后直接 SSH | Phase 2、Phase 6 | 简单。但 Cloud Shell 会在 subnet 里留一块网卡,destroy 前要切回 Public |
| OCI Bastion | Cloud Shell → Bastion(session)→ VM:22 | Phase 4、Phase 9 | 推荐。免费、托管(不用自己维护跳板机)、session 有时限、留审计记录 |
flowchart TB
CS["Cloud Shell"] -->|"① 用 IAM 权限建 session<br/>登记 SSH 公钥<br/>最长 3 小时"| B["Bastion"]
B -->|"② 经 private endpoint<br/>VCN 里的私有 IP"| VM["VM :22<br/>NSG 只接受 endpoint 的 IP"]
几个设计点:
- NSG 的 SSH 来源写成 Bastion 的属性,不写死 IP:
source = "${oci_bastion_bastion.this.private_endpoint_ip_address}/32" - session 不放进 Terraform:最长 3 小时就过期,放进去以后每次 plan 都有差异。用的时候在 Cloud Shell 里建
- Bastion 的
client_cidr_block_allow_list只能写0.0.0.0/0:Cloud Shell 的出口 IP 不固定。这是有意识的取舍,安全性靠两样东西:建 session 要 IAM 权限,连接要 session 登记的私钥
为什么不直接放行 Cloud Shell 的公网 IP?
Cloud Shell 的出口 IP 是 Oracle 的共用 IP,而且会变。放行它,等于对其他 OCI 用户也开放了。
6. 设计决策一览¶
| 决策 | 选了 | 没选 | 理由 |
|---|---|---|---|
| 服务器放哪 | private subnet,没有公网 IP | public subnet + 公网 IP | 公网 IP + 开着的端口 = 攻击面 |
| 外部访问的入口 | 只有 LB(在 public subnet) | VM 直接公开 | 入口集中到一处,VM 可以增减、替换 |
| 安全规则的写法 | NSG 按层建,互相引用 | 在规则里写 IP | IP 变了、台数变了都不用改规则 |
| SSH 的来源控制 | 只在 NSG 写 Bastion 的 IP | SL 放行整个 VCN | SL 和 NSG 是并集,SL 一宽泛 NSG 就失效 |
| 运维通道 | OCI Bastion | 自己建跳板 VM | 免费、托管、不用打补丁、有审计记录 |
| 出口 | 按用途:NAT + SGW;不需要 Internet 的只给 SGW | 统一走 NAT | 访问 OCI 服务不经过 Internet;能少开就少开 |
| 访问 Always Free 的 ADB | public endpoint + ACL(只允许本 VCN 和本机 IP),VCN 内经 SGW | private endpoint | Always Free ADB 不支持 private endpoint |
7. 验证:怎么证明设计生效了¶
只验证"能通"不够,"不该通的确实不通"也要实际测一次。
| 要证明的事 | 正确的方法 | 不能当证据的 |
|---|---|---|
| 出 Internet 走的是 NAT | VM 上 curl ifconfig.me → 显示 NAT GW 的公网 IP |
dnf install 成功(dnf 仓库属于 Oracle 服务,走的是 SGW) |
| 完全出不了公网 | curl -m 5 ifconfig.me → timeout |
Flow Logs 里有没有记录(没有路由的包不会出现在 Flow Logs 里,这是实验中观察到的) |
| 访问 ADB 走的是 SGW | 在 ADB 里查 sys_context('USERENV','IP_ADDRESS') → 是 VM 的私有 IP |
能连上(走 NAT 也能连) |
| SSH 只能从 Bastion 进 | 从 Bastion 能登录 + 从 app-1 → app-2:22 会 timeout(本仓库当时没做这个反向测试,所以漏掉了问题) | 只测了从 Bastion 能登录 |
| 规则真的和代码一致 | 用 API / Console 看实际的规则 | plan 没报错 |
8. 坑¶
| 现象 | 原因 | 对策 |
|---|---|---|
| NSG 写了"只允许 X",别的来源也能连 | SL 和 NSG 是并集,SL 放行得太宽 | SL 不放行要精确控制的端口;做反向测试 |
| Bastion session 连不上 VM | SL 只放行了出站 443,endpoint → VM:22 对 endpoint 是出站 | SL 加"subnet 内 22 出站" |
| destroy 时 subnet 一直删不掉 | Cloud Shell private network 的网卡还挂在 subnet 上 | destroy 前把 Cloud Shell 的 Network 切回 Public;用 list_private_ips(subnet_id=...) 查是谁 |
dnf install 成功,以为 NAT 通了 |
dnf 仓库走 SGW | 用 curl ifconfig.me 看出口 IP |
| 从本机 SSH timeout | 所在网络屏蔽出站 22 | 用 Cloud Shell(private network 或 Bastion) |
9. 自测问题¶
1. VM 的 SL 放行了 22,NSG 没放行 22,从 VCN 内能 SSH 吗?
能。SL 和 NSG 是并集,有一个放行就能通。
2. 同样的配置换到 AWS(NACL 放行、Security Group 没放行),能连吗?
不能。AWS 的 NACL 和 Security Group 两边都要放行。
3. private subnet 的 VM 能 dnf install,能说明 NAT GW 配好了吗?
不能。OCI 的 dnf 仓库属于 Oracle 服务,有 SGW 就能装。要用 curl ifconfig.me 看出口 IP 是不是 NAT 的公网 IP。
4. 一台 VM 只需要从 Vault 取密码、往 Object Storage 写日志,出口该怎么配?
只给 SGW,不给 NAT 和 IGW。这样它访问 OCI 服务没问题,但完全出不了公网。
5. app-nsg 的 80 端口,来源为什么写 lb-nsg 而不写 LB 的 IP?
LB 的 IP 可能变,app VM 也可能增减。写 NSG 的话规则不用改,而且规则本身就说明了"只有 LB 层能访问 app 层"。
6. Bastion 的 client_cidr_block_allow_list 是 0.0.0.0/0,安全吗?
这是取舍:Cloud Shell 的出口 IP 不固定,没法限定来源。安全靠建 session 需要 IAM 权限、连接需要 session 登记的私钥,而且 session 最长 3 小时。如果客户端 IP 固定,就应该限定。
7. 怎么证明「SSH 只能从 Bastion 进来」?
两个方向都要测:从 Bastion 能登录(正向),从 VCN 里其他 VM 连 22 会 timeout(反向)。只测正向的话,SL 放行过宽这种问题发现不了。
8. 路由表里同时有 0.0.0.0/0 → NAT 和 Oracle 服务 → SGW,访问 Object Storage 走哪个?
走 SGW。路由按最长前缀匹配,Oracle 服务的地址段比 0.0.0.0/0 更具体。
完整代码¶
正文只摘了关键部分。完整代码(Phase 4 的 3 层结构):
04-3tier/modules/network/main.tf(VCN / Gateway / 路由表 / SL / subnet)
# ------------------------------------------------------------
# network module:VCN 一套(Phase 4 版)
# VCN ─ Internet Gateway / NAT Gateway / Service Gateway
# ├─ Public Subnet(s) ─ rt-public (0.0.0.0/0 → IGW)
# └─ Private Subnet(s) ─ rt-private(0.0.0.0/0 → NAT GW、Oracle Services → Service GW)
# Security List 所有 Subnet 共用一个
# 和 03 的区别:
# - 加了 NAT GW / Service GW(用 bool 开关,count 控制建不建)
# - Private Subnet 不再用 VCN 的默认 Route Table,改用自己建的 rt-private
# ------------------------------------------------------------
locals {
# for_each 的表达式:按 is_public 把 Subnet 分成两组
public_subnets = { for k, s in var.subnets : k => s if s.is_public }
private_subnets = { for k, s in var.subnets : k => s if !s.is_public }
create_nat = var.enable_nat_gateway && length(local.private_subnets) > 0
create_sgw = var.enable_service_gateway && length(local.private_subnets) > 0
}
resource "oci_core_vcn" "this" {
compartment_id = var.compartment_ocid
cidr_blocks = [var.vcn_cidr]
display_name = "${var.name_prefix}-vcn"
dns_label = var.vcn_dns_label
freeform_tags = var.freeform_tags
}
# ---- Gateway ----
# Public Subnet 一个都没有的话就不需要 IGW → count 控制建 0 个还是 1 个
resource "oci_core_internet_gateway" "this" {
count = length(local.public_subnets) > 0 ? 1 : 0
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.this.id
display_name = "${var.name_prefix}-igw"
enabled = true
freeform_tags = var.freeform_tags
}
# NAT GW:Private Subnet 里的 VM 只能"从里往外"连 Internet(外面连不进来)
# 用途:dnf install、OS 更新等
resource "oci_core_nat_gateway" "this" {
count = local.create_nat ? 1 : 0
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.this.id
display_name = "${var.name_prefix}-natgw"
freeform_tags = var.freeform_tags
}
# Service GW 的连接对象:"All <region> Services In Oracle Services Network"
# (另一个选项是只有 Object Storage 的那个。ADB 要用 All 的这个)
data "oci_core_services" "all" {
count = local.create_sgw ? 1 : 0
filter {
name = "name"
values = ["All .* Services In Oracle Services Network"]
regex = true
}
}
# Service GW:不经过 Internet,直接走 Oracle 内部网络访问 OCI 的服务(ADB、Object Storage 等)
resource "oci_core_service_gateway" "this" {
count = local.create_sgw ? 1 : 0
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.this.id
display_name = "${var.name_prefix}-sgw"
services {
service_id = data.oci_core_services.all[0].services[0].id
}
freeform_tags = var.freeform_tags
}
# ---- Route Table ----
resource "oci_core_route_table" "public" {
count = length(local.public_subnets) > 0 ? 1 : 0
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.this.id
display_name = "${var.name_prefix}-rt-public"
route_rules {
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = oci_core_internet_gateway.this[0].id # count 建的资源要用 [0] 引用
}
freeform_tags = var.freeform_tags
}
# Private 用的 Route Table:NAT / SGW 有几个开着就生成几条规则
# Oracle Services 的流量会匹配更具体的 SGW 规则,不会走 NAT(最长前缀匹配)
resource "oci_core_route_table" "private" {
count = length(local.private_subnets) > 0 ? 1 : 0
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.this.id
display_name = "${var.name_prefix}-rt-private"
dynamic "route_rules" {
for_each = local.create_nat ? [1] : []
content {
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = oci_core_nat_gateway.this[0].id
}
}
dynamic "route_rules" {
for_each = local.create_sgw ? [1] : []
content {
# 目的地不是 CIDR,而是"服务"(例:all-kix-services-in-oracle-services-network)
destination = data.oci_core_services.all[0].services[0].cidr_block
destination_type = "SERVICE_CIDR_BLOCK"
network_entity_id = oci_core_service_gateway.this[0].id
}
}
freeform_tags = var.freeform_tags
}
# ---- Security List ----
resource "oci_core_security_list" "this" {
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.this.id
display_name = "${var.name_prefix}-sl"
egress_security_rules {
destination = "0.0.0.0/0"
protocol = "all"
}
# dynamic block:var.ssh_allowed_cidrs 有几个就生成几条规则
dynamic "ingress_security_rules" {
for_each = var.ssh_allowed_cidrs
content {
source = ingress_security_rules.value
protocol = "6"
description = "SSH"
tcp_options {
min = 22
max = 22
}
}
}
# ICMP type 3 code 4(Path MTU Discovery)
ingress_security_rules {
source = "0.0.0.0/0"
protocol = "1"
icmp_options {
type = 3
code = 4
}
}
freeform_tags = var.freeform_tags
}
# ---- Subnet ----
# for_each:var.subnets 的每个条目建一个 Subnet
# state 里是 oci_core_subnet.this["public"] 这样的地址
resource "oci_core_subnet" "this" {
for_each = var.subnets
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.this.id
cidr_block = each.value.cidr
display_name = "${var.name_prefix}-subnet-${each.key}"
dns_label = each.value.dns_label
security_list_ids = [oci_core_security_list.this.id]
route_table_id = each.value.is_public ? oci_core_route_table.public[0].id : oci_core_route_table.private[0].id
prohibit_public_ip_on_vnic = !each.value.is_public
freeform_tags = var.freeform_tags
}
04-3tier/env/security.tf(NSG)
# ------------------------------------------------------------
# NSG:按"层"建,同一层的 VM 共用
# lb-nsg :LB
# app-nsg:app VM ×2
# 规则里可以写"来源/目的地 = 另一个 NSG"(source_type = NETWORK_SECURITY_GROUP)
# → 不用写 IP,VM 增减、IP 变化都不用改规则
#
# my_ip ──:80──→ [lb-nsg] LB ──:80──→ [app-nsg] VM ×2 ←──:22── Bastion
#
# checkov CKV_OCI_21(要求 ingress 规则用 stateless)→ 有意不改,在各规则里用 skip 注释写明理由
# stateless = 不记录连接状态,返回流量要另写规则放行(规则翻倍、易配错);优点是大流量时性能好
# 这里流量很小,用默认的 stateful
# ------------------------------------------------------------
# ---- lb-nsg ----
resource "oci_core_network_security_group" "lb" {
compartment_id = var.compartment_ocid
vcn_id = module.network.vcn_id
display_name = "${local.name_prefix}-lb-nsg"
freeform_tags = local.common_tags
}
# 入:只允许本机的 IP 访问 LB 的 80
resource "oci_core_network_security_group_security_rule" "lb_http_from_my_ip" {
#checkov:skip=CKV_OCI_21:Low traffic lab; stateful rules are simpler and sufficient
network_security_group_id = oci_core_network_security_group.lb.id
direction = "INGRESS"
protocol = "6"
source = "${var.my_ip}/32"
source_type = "CIDR_BLOCK"
description = "HTTP from my IP"
tcp_options {
destination_port_range {
min = 80
max = 80
}
}
}
# 出:LB → app VM 的 80(转发请求 + 健康检查)
# (Subnet 的 Security List 已经允许全部出站,这条不写也能通。写出来是为了让 NSG 自己就能说明"LB 只往 app 发")
resource "oci_core_network_security_group_security_rule" "lb_http_to_app" {
network_security_group_id = oci_core_network_security_group.lb.id
direction = "EGRESS"
protocol = "6"
destination = oci_core_network_security_group.app.id
destination_type = "NETWORK_SECURITY_GROUP"
description = "HTTP to app"
tcp_options {
destination_port_range {
min = 80
max = 80
}
}
}
# ---- app-nsg ----
resource "oci_core_network_security_group" "app" {
compartment_id = var.compartment_ocid
vcn_id = module.network.vcn_id
display_name = "${local.name_prefix}-app-nsg"
freeform_tags = local.common_tags
}
# SSH 只接受 Bastion 来的连接
# Bastion 在 app Subnet 里有一个 private endpoint(私有 IP),SSH 会从这个 IP 进来
resource "oci_core_network_security_group_security_rule" "app_ssh_from_bastion" {
#checkov:skip=CKV_OCI_21:Low traffic lab; stateful rules are simpler and sufficient
network_security_group_id = oci_core_network_security_group.app.id
direction = "INGRESS"
protocol = "6"
source = "${oci_bastion_bastion.this.private_endpoint_ip_address}/32"
source_type = "CIDR_BLOCK"
description = "SSH from Bastion"
tcp_options {
destination_port_range {
min = 22
max = 22
}
}
}
# HTTP 只接受 LB 来的连接(来源写 lb-nsg,不写 IP)
# app VM 之间、Bastion → VM 的 80 仍然不通
resource "oci_core_network_security_group_security_rule" "app_http_from_lb" {
#checkov:skip=CKV_OCI_21:Low traffic lab; stateful rules are simpler and sufficient
network_security_group_id = oci_core_network_security_group.app.id
direction = "INGRESS"
protocol = "6"
source = oci_core_network_security_group.lb.id
source_type = "NETWORK_SECURITY_GROUP"
description = "HTTP from LB"
tcp_options {
destination_port_range {
min = 80
max = 80
}
}
}
04-3tier/env/bastion.tf
# ------------------------------------------------------------
# OCI Bastion Service(Always Free)
# 跳板机由 OCI 托管,不用自己建 VM、打补丁
# Terraform 只建 Bastion 本体;session(有效期最长 3 小时)每次用的时候在 Console / CLI 里建
# → session 放进 Terraform 的话,过期后每次 plan 都会出差异
# ------------------------------------------------------------
resource "oci_bastion_bastion" "this" {
compartment_id = var.compartment_ocid
bastion_type = "STANDARD"
name = "${local.name_prefix}bastion" # 只能用字母和数字(不能有 -)
target_subnet_id = module.network.subnet_ids["app"]
# 允许连 Bastion 的来源
# 所在网络屏蔽 22 → 实际从 Cloud Shell 连,而 Cloud Shell 的公网 IP 不固定 → 只能 0.0.0.0/0
# 安全性靠 session:要有 IAM 权限才能建 session,而且只认建 session 时登记的 SSH 公钥
client_cidr_block_allow_list = ["0.0.0.0/0"]
max_session_ttl_in_seconds = 10800 # 3 小时(上限)
freeform_tags = local.common_tags
}