MENU

【歪门邪道】在内网环境下使用SSL证书

January 11, 2022 • 瞎折腾

最近开始尝试一些集群的玩意儿了,主要问题有二:如何通过主机名解析到IP;对于默认提供SSL的服务,证书怎么办?

本文将阐述我的解决方法。

最近开始搭建Ceph集群了,过程中遇到了两点问题。其中一点就是如果通过主机名来访问其他结点,那么旧必须要修改hosts文件,如果一次搭建还好,可摸索的过程中谁还不翻个车,我重装了6次系统才搞好Ceph集群。其中有不少弯路是因为装完系统之后没有改hosts而排查了半天。

我:这怎么就连不上呢

主机:有人没配hosts,但我不说是谁

另一个问题就是现在大多数服务在启动后都会产生一个自签证书,对于简单的测试还好,可如果要长期运行,自签证书不光用起来麻烦,而且还是很大一个安全隐患——毕竟不能每次都看那个自签CA的公钥有没有变动,但是不检查的话被中间人攻击了也无从得知。如果决定使用自签证书的话,那么每一个服务的CA都不一样,就得在所有访问的设备上安装这个自签CA,并且如果这个自签CA的私钥泄露了,那么对于所有设备都是一个更大的安全隐患。

经过一番谷歌搜索和一系列尝试,我自认为找到了一个相对不错的解决办法。以下详述:

主机名解析到IP

关于这个问题,最简单的解决办法就是利用域名直接解析成内网ip,这样方便又简单,有效避免了频繁改hosts的问题。但是,怎么设置域名呢?经过一番搜索,StackOverflow上无外乎两种声音:

  • 利用已有域名
  • 自己编造一个域名

下面我分别说说这两种方法的利弊。

利用已有域名

利用已有域名最大的问题就是如何保证内网IP不泄露到公网——假如一个内网主机是192.168.1.1,你把它设置到了某个域名上,那么别人访问这个域名就会访问到对方内网上的主机,如果碰巧的话,可能就是他家的路由器。很明显,这会造成不必要的混乱。

最好的办法就是设立一个单独的二级域名,例如internal.skyblond.info,然后在内网上搭建一个DNS服务器用来解析,这样的话公网上不会知道你的node1.internal.skyblond.info,而本地网络中又可以正常解析。而且这种方法的好处就是可以申请通配符SSL证书。至于坏处嘛,就是你得在本地网络上弄个DNS服务器。这个设施对于长期运行且颇具规模的网络来说必不可少,但是对于测试环境,有点杀鸡焉用牛刀的意思了。

自己编造一个域名

如果你没有域名,或者你的域名太长了,那么最好的办法就是随便编一个域名。一般来说不建议随便编造域名,但是如果一定要编造的话,那么最合理的应该是home.arpa。这个域名最开始被IETF提出,一开始是.home,但是后来改成了home.arpa,因为卖域名的不想把整个.home顶级域名拱手相让。虽然合理,但毕竟是二级域名,用起来的话就变成了三级域名了,何况都已经编造了,干嘛不编一个顶级域名呢?

一般来说并不建议随意编造域名,即便是目前没有使用的顶级域名,在未来一旦分配到某个用途上,那对应的本地网络上的运维可能就要骂街了——鬼知道会不会有人用了你现在用的域名,万一发生冲突,谁知道会引发什么奇怪的bug。不过话是这么说,当你一定需要编造域名的话,最好符合行业标准。

我们来看看AWS和GCP是怎么做的:

Amazon EC2 instance hostname types

IP address-based naming

When you launch an EC2 instance with IP address-based naming (IPBN), the guest OS hostname is configured to use the private IPv4 address

  • Format for an instance in us-east-1: private-ipv4-address.ec2.internal
  • Example: ip-10-24-34-0.ec2.internal
  • Format for an instance in any other AWS Region: private-ipv4-address.region.compute.internal
  • Example: ip-10-24-34-0.us-west-2.compute.internal

Resource-based naming

Resource-based naming (RBN) is used automatically when you launch EC2 instances in IPv6-only subnets. RBN is not selected by default when you launch an instance in dual-stack (IPv4+IPv6) subnets, but it is an option that you can select depending on the subnet settings. After you launch an instance, you can manage the guest OS hostname configuration. For more information, see Modify RBN configurations.

When you launch an EC2 instance with a resource-based hostname type, the guest OS hostname is configured to use the EC2 instance ID.

  • Format for an instance in us-east-1: ec2-instance-id.ec2.internal
  • Example: i-0123456789abcdef.ec2.internal
  • Format for an instance in any other AWS Region: ec2-instance-id.region.compute.internal
  • Example: i-0123456789abcdef.us-west-2.compute.internal

内部 DNS

Google Cloud 上的虚拟私有云网络具有一项内部 DNS 服务,允许同一网络内的实例使用内部 DNS 名称彼此访问。虚拟机实例的内部 A 记录是在 .internal 的 DNS 区域中创建的。虚拟机实例的 PTR 记录是在相应的反向区域中创建的。在您管理实例时,Google Cloud 会自动创建、更新和移除这些 DNS 记录。

看起来巨头们都没有遵守RFC的建议,而是使用了.internal顶级域名在内部网络上使用。所以,当我们一定要编造域名的话,不妨也编造一个.internal域名。

不过这样好像并没有解决问题——还是得在内网环境搭建一个DNS服务器。而且这种情况下由于无法通过CA的流程,所以编造的域名没法使用正常签发的SSL证书,就得用自签CA。

我的解决办法

反正我都有域名了,当然直接用已有的域名了。对于域名分配,像是master-node.skyblond.info或者172-16-71-100.skyblond.info这种域名固然简单,但我还有www.skyblond.info这样需要对公网提供服务的域名,两个混作一团实属不妥。于是我决定使用AWS的Route53托管internal.skyblond.info及其下的三级域名,过程也十分简单:

  1. 创建一个托管区域,域名填写待托管的域名,例如internal.skyblond.info
  2. 类型选择“公有托管区”,因为这个托管域名是面向公网解析的
  3. 确认创建

完成后需要在当前域名的DNS服务商处设置几个NS记录。NS记录的域是internal,指向托管区详细信息中的名称服务器,这一步相当于告诉现有的DNS服务器:internal及其下的域名现在由AWS的DNS服务器来解析。这样一来原有的网站不受影响,而针对内网使用的则单独由AWS托管。

成本核算如下:每个托管区最高可以有一万条记录,超过一万条额外收费。一万条记录以内的托管区,每个每月0.5美元,标准查询每月每一百万个0.4美元。这里的查询是指实际打到AWS服务器的查询,如果查询过程中的DNS服务器有缓存,那么缓存的结果是不收费的。如果一个月要用满一百万次查询,那么就得每秒钟0.38次查询,相当于每3秒查询一次,7x24全年无休的查询。即便如此,每个托管区域每月的费用才0.9美元,不到7块钱。好处是之后所有的内网机器可以不需要配置DNS,直接用FQDN作为主机名即可解析。

钱能解决的问题都不是问题。

内网SSL证书

还是上文的结论,编造的域名没法从商业CA处取得合法的SSL证书,只能使用自签证书。但是对于合法域名来说,这是可能的。但是现在大多数的通配符域名都只针对二级域名。例如我的网站SSL证书颁发给了*.skyblond.info,也就是说node1.skyblond.infointernal.skyblond.infowww.skyblond.info是可以被证书保护的,而node1.internal.skyblond.info却不能。如果为此单独买一个针对二级域名的证书,考虑到价格,好像有些犯不上。好在像Let's Encrypt这样的免费SSL提供商允许通过DNS-01的方式自动签发免费的通配符SSL证书,虽然有效期只有90天,但可以使用ACME Bot自动续期。使用过LNMP一键脚本的人可能有印象——如果没有SSL证书的话,选择Let's Encrypt就可以得到一个为期90天的免费证书,并且能够自动续期。

DNS-01是ACME协议的一种验证方式,这种验证方式通过对域名添加一条指定内容的TXT记录来验证申请者对于域名的所有权,而ACME协议则做到了自动化。我这里使用的是acme.sh,这是一个基于shell的ACME机器人,只要填好信息,他就可以自动申请并续期证书,稍加配置还可以实现自动安装证书。

如果采用DNS-01方式验证,它还支持一系列DNS服务商的API用以自动完成验证,而AWS的Route53刚好就是它支持的一个。

首先需要在AWS IAM上创建一个API KEY,其中权限/策略如下(How to use Amazon Route53 API):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "route53:GetHostedZone",
                "route53:ListHostedZones",
                "route53:ListHostedZonesByName",
                "route53:GetHostedZoneCount",
                "route53:ChangeResourceRecordSets",
                "route53:ListResourceRecordSets"
            ],
            "Resource": "*"
        }
    ]
}

这个IAM角色的配置可以用于所有Route53上的托管区域。随后安装acme.sh,过程不再赘述。安装完成后需要临时配一下环境变量:

export AWS_ACCESS_KEY_ID=YOUR_ACCESS_KEY
export AWS_SECRET_ACCESS_KEY=YOUT_SECRET_ACCESS_KEY

然后就可以申请证书了:

acme.sh --issue --dns dns_aws --keylength 4096 -d internal.skyblond.info -d '*.internal.skyblond.info'

这行命令将申请一个密钥长度为4096位的证书,颁发给internal.skyblond.info*.internal.skyblond.info,验证的方式为DNS,并且DNS服务商是AWS,通过预先配置的ACCESS KEY,他便能自动获取挑战,自动设计Route53记录,然后自动申请证书了。生成的证书在~/.acme.sh/internal.skyblond.info目录下。

至此这个证书就可以配合先前的DNS解析一起使用了。


知识共享许可协议
【歪门邪道】在内网环境下使用SSL证书天空 Blond 采用 知识共享 署名 - 非商业性使用 - 相同方式共享 4.0 国际 许可协议进行许可。
本许可协议授权之外的使用权限可以从 https://skyblond.info/about.html 处获得。

Archives QR Code
QR Code for this page
Tipping QR Code
Leave a Comment

已有 1 条评论
  1. > 虽然合理,但毕竟是二级域名,用起来的话就变成了三级域名了

    这里对于`home.arpa`的解释,是如果你有不同的网络,例如`server.home.arpa`和`vmware.home.arpa`,那么这个就是二级域名,对应下属的设备就成了三级域名,例如`node1.vmware.home.arpa`,用起来一样不方便。