因为疫情的原因,在家里实在是无聊,外加最近公司里的事情不是很忙,于是我就开始研究捡垃圾事宜。而且之前在学校薅的 vps 羊毛也快到期了,基本上各大平台都薅过了,没法继续薅了,也使我决定了继续捡垃圾去搞一套家庭服务器。

开门见山,直接说我捡垃圾的结果,总价 3000 左右

  • 主板:华硕 z10pa-u8 10G-2S 12 ¥1250

  • CPU:e5 2660 v3 10 核 20 线程 ¥510

  • 内存:2 16 ECC DDR4 2133 ¥250 2

  • 电源:海韵 550W 全模组金牌电源 ¥450

  • 散热器:超微 E5 2011 服务器专用散热器 ¥155

  • 机箱:航嘉 S400 4u 工控机箱 ¥239

  • 系统:Unraid,暂时是试用版,所以不计入总价。等后面磨合好了会购买正版

  • 硬盘:家里淘汰下来的 500G 垃圾机械,不计入总价

整机装好 unraid 系统后空盘待机功耗 35W 左右,CPU 温度 40 度;系统满载在 130W 左右,温度 75 度左右。整体来说我是非常满意的,因为我另外一台 j1900 的 nas 待机也要 15W,虽然高了 20W 但是带来的性能提升可不止 20W 这么一点。

可能你会有很多疑惑,为什么要搞这个,为什么选用这样的配置,那么接下来让我一一来解释下我为啥选用这套配置,也给想要相同想法的朋友一个选择方案。

目标

在具体讲选择配件过程中,我们现在对齐目标,只有我们的目标相同,才能更好的理解我为什么选择这套配置:

  • 需要一台家庭强性能服务器,用于跑我个人的项目以及一些常用的 app,要求 CPU 核心数足够多,方便跑多任务

  • 服务器单核性能也要足够的强,因为会用来游戏开服,比如 minecraft,这个比较吃单核性能

  • 需要能够较好的以虚拟化的方式运行群辉,并且最好能够支持万兆网络,方便有时候心血来潮拷贝素材剪辑视频等

  • 偶尔要做家庭影音啥的,所以最好能够硬件解码的能力

  • 因为要跑群辉,所以要能够有较多的 SATA 接口,或者足够的 PCI 接口

  • 待机功耗要足够的低,毕竟我还是租房子住,不希望电费太贵

  • 服务器体积尽可能不要太大,同时要保证静音,而且家里有宠物,所以会考虑走线,避免宠物触碰到,所以机箱的选择可能不是很适合所有人

  • 最后的最后,价钱要便宜,挑选起来可就简单多了

CPU

一个服务器的核心就是他的 CPU,只要 CPU 定好之后,其他的配件都可以围绕着它展开。

先说一句,因为我是要做高性能服务器,所以什么 j1900 j3455 奔腾啊这些低功耗的 CPU 全部 pass。

其实挑选 CPU 是我最纠结的地方,因为我有两个自相矛盾的,是核心数的数量和单核性能之间的矛盾,众所周知,服务器级别 CPU 核心数多但单核性能羸弱,而消费级 CPU 核心数少但单核性能强。于是我在服务器和消费级之间来回摇摆,虽然消费级一般不支持 ECC,但是核心强更吸引我。我也一直不能下定决心。我目标的是至少 8 核 16 线程,并且单核性能与现有的消费级别处理器差不多。

说道这里,可能就有人会说 AMD 线程撕裂者不香么。确实,当初看到觉得特别符合我的要求,核心多单核强,但问题就在于这玩意上万块啊,就算是线程撕裂者一代,也要 1w,这对于我来说太难以接受了。

逛了一圈,实在是找不到,于是我不得不降低要求,就是放弃消费级别 CPU。一是因为没有核心数合适的,在锐龙以前的时代,intel 一直在四核心徘徊,就算是在锐龙之后,intel 的核心数也少。而锐龙核心够,但这又牵扯出另外一个问题,就是消费级别的 U 实在是贵啊,7700K 都还 1800 块呢,想要搞个便宜的,只能去找 4 代 3 代的 intel U,但这个时代的 U 和 E5 洋垃圾也差不多。所以最后将目光投向了服务器 E5 洋垃圾

而 E5 最难选择的其实就是 v2 系列还是 v3 系列了。v2 系列意味着可以用 DDR3 内存以及更便宜的主板,但是他的待机功耗要大不少。但 v3 系列相比要用更贵的 DDR4 内存和主板,但他的性能更强,待机功耗更低。具体对比可以看图

可以发现,同样是 2660,v3 比 v2 的性能提升了 20% 还要多,单核心性能比 r5 1600 来说才低了 20% 左右,比我想象中的好多了,一般来说同代的服务器都要比同代的消费级性能至少低 30% 多,如果是更高端的消费级可能要低 50%。而且总分更是比万元的 1900x 一代线程撕裂者还要高。

当然了,这里应该拿 intel 的做对比,拿 AMD 不太恰当,AMD 本身同代单核就比 intel 低不少,不过我手上只有 AMD 的 u,所以就拿 AMD 的来对比了。

那我为啥选择 2660 而不是 2650 或者 2678 呢?其实原因很简单,2650 以上基本就符合我的需求了,但是我发现 2660 竟然比 2650 还要便宜,那为啥不用 2660 呢?如果等以后我对性能有更高要求的时候,再换也不迟。

准系统?

在考虑的过程中,我也曾经看过一些准系统,二手服务器 dell r620 r730xd 准系统、二手的塔式服务器准系统,但都被我 pass 掉了,主要原因是:

  • 二手塔式服务器太贵了,光一个准系统就要 3000+ 了,而且还是 v2 的 u。

  • 机架式的服务器虽然便宜,但是噪音功耗都太大,而且体积也很大,放到哪里都不合适,因为租的房子没有专门的机房或者书房。

  • r620 是 v2 的 u,功耗太大。而 r730xd 又太贵了,最后也 pass 了

主板

既然将准系统 pass 掉之后,我不得不开始自选主板的道路。因为我不会用来做把服务器用来做视频渲染,需要核心多,但不需要那么多,所以这里我主要挑选的是单路主板,而且单路的便宜啊。如果小伙伴需要服务器拿来做视频渲染,建议直接上双路主板。PS:其实自从三代锐龙出现之后,建议视频渲染啥的还是直接上 3900x 3950x 这类吧,E5 做视频渲染已经不香了。

支持 V3 的主板基本有两种,一种是国产的寨板,另外一种就是拆机的服务器主板。

寨板有一个最大的好处,就是便宜,基本上五六百就可以搞定,但是缺点就是可扩展性太差了,内存插槽少,SATA 少,PCI-E 更少,而且还容易 BOOM,最终我放弃了寨板

那就只有拆机服务器主板可以选了,这其中就有微星、华硕的可以选,我最后选定了华硕 z10pa-u8 10g-2s 只有一个原因,便宜。微星的拆机件某宝基本上要 2000 左右,而话说的这个只需要 1400 多,运气好的话还能找到 1200 多的,就比如我下单的这个,而且还是湖北店铺,就当支持湖北朋友了。

简答介绍一下我这个主板,大家来感受下这 1200 块到底值不值:

  • 双板载千兆网卡,双板载万兆网卡,一个 IPMI 管理端口(板载万兆啊,普通的万兆扩展卡都要三四百呢,注意,不是所有的板子都有万兆网卡的,不带 10g-2s 的就没有)

  • 8 条内存插槽

  • 10 SATA 接口(足够我的硬盘使用了,而且 4 个侧插,6 个直插,还是比较丰富的)

  • 板载 m.2 NGFF 接口(因为是上年纪的板子,没有 nvme,不过也很不错了)

  • 双 PCI-Ex16,3 个 PCI-Ex8,一个 PCI-Ex1,不过其中一个 x16 是一个 x8 是共用的,当插了一个 x8 之后,x16 会自动变成 x8。

  • 板上搭载一个 USB,方便直接做启动盘

总的来讲,在单路主板里面,我觉得这个算是比较值的,尤其是板载万兆网卡。

机箱

前面也说了,我不想有一个太大的机箱,所以当时就没想直接买个 2u 机架服务器的机箱。而比较符合的是各种 nas 机箱,比如 8 盘位的,但问题依旧是太贵。8 盘位的要上千了,4 盘位的基本也在五百左右。

于是我就去看了看普通的塔式机箱,基本上比较符合我的心意,最多有 10 盘位的,支持 E-ATX 主板,而且价钱也才 300 多块,最主要是能够支持普通的机箱配件,而且还有一定的热插拔能力。简直太完美了,唯一的缺点就是外观不够有范

直到有一天无意间看到 4u 的工控机箱,发现这玩意好帅气,很符合我对一个服务器的定位。虽然只有 7 盘位,但是配合光驱位也能有 10 盘位。最主要的这个带钥匙,就不用怕我家里的猫一不小心碰到开关就给我关机了。而且体积比塔式的还要小巧一点,毕竟是租的方式,能小一点是一点,不过就是损失了热插拔的能力。好在价格更便宜,而且还躺着,于是心血来潮的我就定了这款机箱。

PS:在我实际装机之后,我觉得奉劝大家,还是塔式的好啊,工控机内部走线实在是太难了,没有热插拔能力测试的时候太难了。不过样子很好看,很有感觉,一次装机之后只要是不加硬盘基本不会动他了,也算是能接受吧。

其他配件

其他的配件基本上就是随便买的,内存选了 2133 频率的,为了保证兼容性。

有个好玩的事情就是电源,原本想买个金牌的 450W 直出电源就够了,毕竟就几个硬盘,最多可能外加一个计算卡,其他的也不会需要了。但正好赶上 618 活动,550W 金牌全模比 450W 金牌直出还便宜,于是我就买了 550W 了。但后来经过朋友提醒,想起来有个最佳转换效率区间,如果负载太低的话,就算是金牌,转换效率也不会太高的,理论上搞个 200W 就够了。

哎,就这样吧,买都买了。

使用

一切装好之后,我就安装了 unraid 作为宿主系统,原因很简单:

  • U 盘就能启动

  • 界面友好,EXSI 实在是有点丑

  • Docker 友好,这点太重要了,作为一个开发,深知 Docker 有多好用

  • 插件丰富,很多东西都能安装

  • 虚拟机太好用了,直通啥的一点问题都没有,而且还支持 XML 编辑,真棒

  • 基于 Linux 系统,直接提供了命令行工具,作为一个开发,能搞的东西太多了,太喜欢了

话不多说,直接一个群辉,一个 debian 虚拟机就搞起来了,把我之前在群辉里面跑的那个 Docker 转移到了 unraid 的 Docker 上。

就此,我心心念的服务器算是告一段落了,接下来就是把云服务器上的业务逐渐迁移到本地来,另外还要折腾下本地域名映射,让泛域名直接解析到内网的网关服务器上,这样就可以通过内网域名直接访问服务器上的业务了。就是内网的域名证书不好搞,用自签名的话需要每一台机器上都要安装根证书,用 CA 签名的吧,泛域名证书太贵了。

注释和共享

作为一个喜欢折腾的人,个人搞了很多东西放在自己的服务器上,但是为了方便,能够在世界各地随时随地的打开查看和使用,我将服务器暴露到了公网中,当然了有些在公有云上的本来就暴露出来的。

那么这里就有一个问题,我如何保护我的信息只能我来查看呢?

  • 最简单的方法就是通过 HTTP Basic Auth + HTTPS。记住一定要上 https,否则你的密码也是会泄漏的。为什么说简单呢?因为只需要在 Nginx 或 Traefik 上配置下就可以了。但是这个方案有一个非常麻烦的问题,就是过一段时间之后就要输入用户名和密码。时间短了,到无所谓,时间一长就会觉得很烦。

  • 构建一套 token 验证体系,不管是使用 oauth 也好还是 jwt 也好,都是可以的。安全性也是可以保证的,而且设置好 token 的时间长度,也能保证避免频繁的输入密码。但是这有一个问题就是实现起来太过于复杂,都快赶上公司的一套系统了。而且还要有各种登录页面,想想都烦。

  • 与上面类似,不过验证方式使用 Two Auth,也就是基于时间的 6 位数组。但是依旧比较复杂。

  • 使用 OpenVPN 的方式。这在一定程度上也能使用,但是对于我来说,OpenVPN 的限制还是比较大的。首先安卓手机无法开启两个 VPN,而且我也不能一直连着 VPN,因为我会部署一些经常用的服务。而且我不是为了能够连接到内网,而是想对外网使用的服务添加验证。

我想了许久,有没有一种不需要输入密码,就可以验证安全的呢?因为是我一个人使用的,所以我根本不需要多用户系统,也就是说验证方式只需要一个密码就可以了。这我突然想起了之前在写 gRPC 的时候有一个双向验证的参数,也可以验证客户端可以不可以。当时觉得只是他们基于 h2 改的协议,结果我一查发现这原来就包含在 https 里面,准确说是 SSL 规范里面。(怪自己当初上计算机网络的时候没好好学这部分,竟然连这个都不知道)

那么至此,思路就很清晰了,给我的所有个人服务都添加 https 客户端校验。只要我的证书够安全,我的页面就是安全的(反正都是我个人的东西,直接拿着 U 盘到处拷贝,手机 Pad 用数据线发送,我就不信这样谁还能盗走我的证书,傲娇脸)

关于 SSL 证书的一些知识

  • 生成证书我们主要采用 openssl 具体的安装教程我就不讲解了,有兴趣的小伙伴自行查阅,主要有下面几个步骤:

    • openssl genrsa:生成 Private Key,用于生成请求文件使用,这里用 .key 后缀。

    • openssl req:依赖上面生成的 Key 去生成 CSR,也就是证书请求文件。使用 .csr 后缀。这期间要填写一些信息,前面的几个大写字母是缩写,后面在命令行使用的时候会用到。

      • C(Country) 国家

      • ST(State/Province) 州或者省

      • L(Locality) 地区,国内写区即可

      • O(Organization) 组织

      • OU(Organization) 组织单位

      • CN(Common Name) 通用名,这个是非常重要的,影响了证书的显示名称和 HTTPS 的域名。

    • openssl x509:根据 x509 规范,利用 CA 的证书和私钥将 CSR 文件加密成真正可以使用到的证书。使用 .crt 后缀

  • SSL 证书必须要采用 sha-2 加密算法。2015 年 12 月 31 日前,CA 机构还会颁发 SHA-1 签名的证书,但是之后只会签发 SHA-2 签名的证书了。Chrome 也会对 SHA-1 签名的证书提示不安全。在 openssl 中用的是 -sha-256 参数。

  • CRTPEM 的关系,大家可以简单的认为 PEM 是将证书 base64 之后的文件,而 CRT 是既能 base64 也能 binary 的一种文件格式。但是通常 openssl 产出的是 base64 的文件,你可以通过 -outform 参数控制产出的类型。

CA 的生成

有了 CA 我们才能去给其他的证书签名,生成 CA 的过程很简单

创建根钥

💡 这个秘钥非常重要,任何获得了这个秘钥的人在知道密码的情况下都可以生成证书。所以请小心保存

1
openssl genrsa -des3 -out root.key 4096
  • -des3 标明了私钥的加密方式,也就是带有密码。建议添加密码保护,这样即使私钥被窃取了,依旧无法对其他证书签名。你也可以更换其他的加密方式,具体的请自行 help。

  • 4096 表示秘钥的长度。

创建自签名证书

因为是 CA 证书,所以没法让别人去签名,只能自签名。这里可以认为是生成 CSR 和签名两部合成一步走。

1
openssl req -x509 -sha256 -new -key root.key -sha256 -days 1024 -out root.crt

服务端证书生成

生成证书私钥

1
openssl genrsa -out your-domain.com.key 2048

和 CA 证书不同,这个私钥一般不需要加密,长度也可以短一些。

生成证书请求文件

1
openssl req -new -key your-domain.com.key -out your-domain.com.csr

这期间要填入一些信息,注意 CN 的名字一定要是你的域名。

使用 CA 对 CSR 签名

在 Chrome 58 之前,Chrome 会根据 CN 来检查访问的域名是不是和证书的域名一致,但是在 Chrome 58 之后,改为使用 SAN(Subject Alternative Name) 而不是 CN 检查域名的一致性。

而 SAN 属于 x509 扩展里面的内容,所以我们需要通过 -extfile 参数来指定存放扩展内容的文件。

所以我们需要额外创建一个 your-domain.com.ext 文件用来保存 SAN 信息,通过指定多个 DNS 从而可以实现多域名证书。

1
2
3
4
5
6
subjectAltName = @alt_names

[alt_names]
DNS.1 = your-domain.com
DNS.2 = *.your-domain.com
DNS.3 = *.api.your-domain.com

以此类推,如果域名较少,还可以用另外一种简写方案。

1
subjectAltName = DNS: your-domain.com, DNS: *.your-domain.com

关于语法的更多内容请查看官方文档。在有了 ext 文件之后就直接可以开始签名了。

1
openssl x509 -req -sha256 -in your-domain.com.csr -CA root.crt -CAkey root.key -CAcreateserial -out your-domain.com.crt -days 365 -extfile your-domain.com.ext

CAcreateserial 这个参数是比较有意思的,意思是如果证书没有 serial number 就创建一个,因为我们是签名,所以肯定会创建一个。序列号在这里的作用就是唯一标识一个证书,当有两个证书的时候,只有给这两个证书签名的 CA 和序列号都一样的情况下,我们才认为这两个证书是一致的。除了自定生成,还可以通过 -set_serial 手动指定一个序列号。

当使用 -CAcreateserial 参数之后,会自动创建一个和 CA 文件名相同的,但是后缀是 .srl 的文件。这里存储了上一次生成的序列号,每次调用的时候都会读取并 +1 。也就是说每一次生成的证书的序列号都比上一次的加了一。

现在,只需要将 your-domain.com.crtyour-domain.com.key 放到服务端就可以使用了。别忘了将 CA 添加系统当中,要不然浏览器访问会出现问题。

客户端证书生成

服务端有了之后,就需要生成客户端的证书,步骤和服务端基本一致,但是不需要 SAN 信息了。

1
2
3
4
5
6
7
openssl genrsa -out client.key 2048
# 这里也可以采用非交互形式,方便制作成命令行工具
openssl req -new \
-key client.key \
-subj "/C=CN/ST=Zhejiang/O=X/CN=*.your-domain.com" \ # 这里的缩写就是文章一开始所说的那些缩写
-out client.csr
openssl x509 -req -in client.csr -CA root.crt -CAkey root.key -out client.crt -days 365

只不过客户端验证需要的是 PKCS#12 格式,这种格式是将证书和私钥打包在了一起。因为系统需要知道一个证书的私钥和公钥,而证书只包含公钥和签名,不包含私钥,所以需要这种格式的温江将私钥和公钥都包含进来。

1
openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12

这期间会提示你输入密码,用于安装的时候使用。也就是说不是什么人都可以安装客户端证书的,要有密码才行,这无疑又增加了一定的安全性。当然了,我试过不输入密码,但是好像有点问题,有兴趣的同学可以自己尝试下。

客户端校验证书的使用

这里以 Node.js 举例。使用 https 模块,在创建的时候和普通的创建方式基本一致,但是需要额外指定 requestCertca 参数来开启客户端校验。

1
2
3
4
5
6
7
8
https.createServer({
key: fs.readFileSync('your-domain.com.key'),
cert: fs.readFileSync('your-domain.com.crt'),
requestCert: true,
ca: [fs.readFileSync('root.crt')], // 校验客户端证书的 CA
}, (req, resp) => {
// blahblah
})

这样只要客户端没有安装合法的证书,那么整个请求就是失败的。而且根本不会进入请求处理的回调函数中,这也意味着显示的错误是浏览器的默认错误。那么这对用户来讲其实不太友好。

那么我们可以通过在参数中添加 rejectUnauthorized: false 来关闭这个功能,也就是说不管客户端证书校验是正确还是失败,都可以进入正常的回调流程。此时我们只需要通过 req.client.authorized 来判断这个请求是否通过了客户端证书的校验,可以给予用户更详尽的错误提示。

另外我们还可以通过 resp.connection.getPeerCertificate() 获取客户端证书的信息,甚至可以根据不同的信息选择给予不同的用户权限。

这里有一个 DEMO: https://www.xgheaven.net.cn:3443,大家打开之后应该会看到一个证书校验失败的提示。这里要说下,我这里的 DEMO 没有使用自签名的服务端证书,只是使用了自签名的 CA 去检查客户端证书。因为用自己签名的服务端证书的话,浏览器会提示不安全,因为用户么有安装自签名的 CA。

可以点击下载客户端证书按钮,安装客户端证书。因为客户端证书是有密码保护的,请输入页面上提示的密码。

再次刷新,如果是 Mac 系统,会提示你要使用哪个客户端证书登录,此时就说明安装成功了。

点击确认,可能还要输入一个系统密码允许 Chrome 访问 Keychain,一劳永逸的话在输入密码之后选择 Always Allow,从此就不需要再输入密码了。

按照道理,你就可以看到这个页面了。

结语

有了这个功能,我就可以将我的所有内容全盘私有化而且还能直接暴露在公网中。配合之前毕设搞的微服务化,简直不要美滋滋。如果之前是使用账号密码登录的,也可以接入这个方案。就是将登录页面替换成证书校验就可以了。

Refs

注释和共享

  • 第 1 页 共 1 页

XGHeaven

一个弱弱的码农


杭州电子科技大学学生一枚


Weifang Shandong, China