ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

自建企业内网私有CA全流程:openEuler+OpenSSL证书签发部署指南

自建企业内网私有CA全流程:openEuler+OpenSSL证书签发部署指南 做企业内网HTTPS最烦的就是证书那点事。买公网CA证书一年几百上千块钱还得走审核流程;用自签名证书浏览器直接给你甩个大红叉更别提多台web服务器各自搞一套维护起来简直噩梦。前阵子刚好在openEuler上把整套CA根证书服务器搭起来了顺手把web服务器证书申请、签发、部署的流程也梳理清楚了。这篇东西把从零到一的过程都记下来包括我踩过的那些坑有需要的朋友可以直接照着抄作业。1. 项目背景与方案选型1.1 自建CA到底解决了什么痛点先说个实际场景。公司内部有十几个业务系统统统一天到晚要访问内部web服务器。之前图省事给每台服务器都整了个自签名证书结果就是每台电脑的浏览器里躺着一堆不受信任的证书警告。开发环境还能忍但业务系统这么搞运维那边天天收到怎么又提示证书不安全的工单烦都烦死。另一种做法是全部买商业证书一年下来几千块倒是其次关键是内网域名、IP地址的证书很多正规CA不给签发走证书验证流程也折腾。自建CA的意义就在这我只要在内网搭一台CA服务器生成根证书然后把根证书通过域策略推送到所有终端的受信任的根证书颁发机构里。之后所有web服务器都用这把根证书去签发终端就自动信任了一次投入后续所有服务器都能用成本为零管理还方便。核心逻辑不复杂用一把根证书作为信任锚点由它来签发各web服务器的证书终端信任了根证书就等于信任了所有由它签发的子证书。1.2 私有CA与商业CA的取舍分析做方案的时候我列了个对比表方便自己决策对比项私有CA商业CA成本零成本只需一台服务器每张证书几百到上千元/年签发效率签完就能用全程自动化需要机构验证周期长内网IP/域名支持完全支持大部分不签发纯IP证书终端信任需手动/域策略下发根证书浏览器/系统内置信任适合场景内网系统、测试环境、开发环境公网站点、面向外部用户结论很明确公网业务买商业CA内网业务自建CA两者互补。注意一点如果内网系统要对接外部用户那还是老老实实上商业证书别指望客户会去装你的私有根证书。1.3 技术选型为什么选openEuler OpenSSLopenEuler是开源的企业级Linux服务器系统社区活跃、兼容性不错作为CA服务器使用没有任何问题。底层用的是OpenSSL这是事实上的标准加密库几乎所有Linux发行版都在用文档多、踩坑资料也全。我在openEuler上用的是系统自带的OpenSSL版本。如果是老系统比如CentOS 7自带的OpenSSL 1.0.x配置格式上会有些差异操作时注意区分。openEuler默认带的OpenSSL 3.x配置文件路径和指令集有更新照着文章写没问题。2. 服务器准备与证书目录规划2.1 openEuler系统安装与初始化配置CA服务器对硬件要求很低虚拟机给了2核4G完全够用。系统装在虚拟机里centos一样的操作习惯安装方式不用多说重点说安装完之后的初始化操作。装好后先更新系统确保openssl是较新的版本dnf update -y openssl version检查版本的同时确认了openssl已经安装。openEuler默认是带openssl的没装的话执行dnf install -y openssl然后是hostname和IP规划。建议给CA服务器设一个固定的内网IPhostname也要设置好因为证书里的Common Name会用到。我这边设置的是hostnamectl set-hostname ca-server # 写/etc/hosts固定IP映射建议一个机器别名注意CA服务器建议做离线或半离线管理尽量不要让它随意访问外网减少私钥泄露和被攻击的可能。我在生产环境就把这台机器的外网访问权限收掉了只开放内网管理端口。2.2 CA目录结构与关键文件规划为了保证管理规范我采用了系统自带的目录规划即/etc/pki/CA这也是开箱即用推荐布局后续写openssl配置文件时能少踩很多坑。执行命令创建标准目录结构mkdir -p /etc/pki/CA/{certs,crl,newcerts,private} echo 1000 /etc/pki/CA/serial touch /etc/pki/CA/index.txt目录说明目录/文件作用/etc/pki/CA/certs存放已签发的证书/etc/pki/CA/crl存放证书吊销列表/etc/pki/CA/newcerts存放新签发的证书副本/etc/pki/CA/private存放CA私钥权限必须锁死serial记录证书序列号初始值填1000index.txt证书签发数据库记录了所有签发的证书信息serial初始值用1000而不是0001是我的个人习惯这样序列号永远是4位不会遇到从999跳1000位数变化的尴尬。index.txt是空的等签发证书时它会自动追加记录。2.3 openssl配置文件修改注意事项CA签发时用的配置在/etc/pki/CA/openssl.cnf但更推荐的做法是复制一份到独立位置避免影响系统默认配置cp /etc/pki/tls/openssl.cnf /etc/pki/CA/openssl_ca.cnf然后重点修改以下几个参数。打开配置文件先改CA默认配置块[ CA_default ] dir /etc/pki/CA certs $dir/certs crl_dir $dir/crl database $dir/index.txt new_certs_dir $dir/newcerts certificate $dir/cacert.pem serial $dir/serial private_key $dir/private/cakey.pem default_days 1095 default_md sha256 policy policy_loose其中policy_loose是我特意加的。OpenSSL默认的policy_loose允许签发证书时不强制匹配CSR里的所有字段这在内网环境中非常实用。如果你用的是policy_matchCSR里的Organization必须和CA一致否则签不了。我建议直接用policy_loose省去一堆匹配报错的烦恼。重点关注default_md必须写sha256。老配置默认sha1现在浏览器和系统基本不认了签出来的证书会提示证书签名算法不安全。3. 根CA部署与自签根证书生成3.1 生成CA根私钥算法与密钥长度选择这是最核心的一步根证书私钥一旦泄露整个信任链直接崩塌。所以私钥生成必须讲究。我选择RSA 4096位执行下面命令cd /etc/pki/CA openssl genrsa -out private/cakey.pem 4096 chmod 600 private/cakey.pem为什么不选2048位虽然2048位是目前安全的最低标准速度也快但CA私钥毕竟是信任锚点使用周期长达10年以上。以摩尔定律来看谁能保证五年后2048位的安全性还够用所以我用4096位签发速度慢那么几十毫秒完全可以接受安全储备则大了非常多。另外强调一点私钥文件权限必须600属主root。我见过有同事把私钥文件改成644等于把家门钥匙放门槛底下。OpenSSL从3.0开始对权限过宽还会直接报错拒绝操作这点设计挺好逼着你养成好习惯。3.2 根证书自签字段填写与有效期设计私钥生成后用req指令自签根证书openssl req -new -x509 -days 3650 -key private/cakey.pem -out cacert.pem -config /etc/pki/CA/openssl_ca.cnf执行后会交互式问你一堆字段我填的参考字段填写内容解释Country NameCN国家代码两位大写字母State or ProvinceBeijing省份Locality NameBeijing城市Organization NameInternal Root CA组织名Organizational UnitIT Security Dept部门Common NameInternal CA Root证书的标识名必填Email Address留空或运维邮箱选填注意Common Name一定要认真想好了再填因为之后服务端证书申请、终端信任规则可能会基于这个名称做校验不建议频繁修改。不是每个字段都必须填选填的可以直接留空跳过。域名部分不支持IP地址作为根证书Common Name但服务端证书可以这部分到后面说。x509参数没必要每次都敲命令行我后来直接写了一个脚本把交互内容写死在命令里可以一键完成签发。这个放到后面4.4节统一说。3.3 验证根证书是否生成成功生成完成后检查一下openssl x509 -in cacert.pem -text -noout重点看一下Signature Algorithm是不是sha256WithRSAEncryption是的话说明签名算法没问题。再看Validity的起止时间按我上面设置的3650天差不多十年有效期。到这里根CA就已经可以对外签证书了。先按耐一下别急着签发最低限度先把openssl的默认配置目录里也放一份根证书指纹。不对其实不急真正要处理的是把根证书推送到终端放到第5节部署时一起说。4. Web服务器证书申请与签发全流程4.1 Web服务器侧生成私钥与CSR请求文件CA搭建好了接下来给web服务器签证书。第一步不是在CA上操作而是在目标web服务器上生成私钥和CSR证书签名请求。这里有个概念必须先理清楚CSR是web服务器自己生成的包含web服务器的公钥和身份信息CA拿到CSR后用根证书的私钥去签名最终得到web服务器的证书。以内部web服务器为例假设它的域名是web.intranet.internalIP是10.0.20.10。先登录到这台web服务器生成私钥mkdir -p /etc/ssl/web cd /etc/ssl/web openssl genrsa -out web.key 2048服务端证书用2048位就够了没必要上4096。因为服务端证书有效期短一般1~2年且每次TLS握手都要用到私钥做签名2048位性能更好安全性也达标。然后生成CSRopenssl req -new -key web.key -out web.csr -config /etc/pki/tls/openssl.cnf字段填法和3.2节类似区别在于Common Name这里一定要填web服务器的域名或者IP。我填的是web.intranet.internal。服务端证书建议把常用名称(Common Name)填域名然后通过subjectAltName扩展去加IP。因为现在很多浏览器和客户端对IP直连以及证书匹配的校验逻辑很严格只填CN容易出问题。这个在后面4.3节有具体操作。4.2 使用CA签发服务端证书把web.csr通过内网传到CA服务器路径就放在/etc/pki/CA/newcerts目录下边好区分。然后执行签发cd /etc/pki/CA openssl ca -in /path/web.csr -out /path/web.crt -days 730 -config /etc/pki/CA/openssl_ca.cnf出问题在这是高发区。最常遇到的情况就是我前面特意为什么改policy_loose的原因——策略不匹配直接报错提示CSR里的字段跟CA不一致。第二个坑是目录权限如果没把index.txt和serial文件放到位也会直接拒绝服务。第三个坑是openssl.cnf里指向的位置和实际目录不一致。真遇到问题先别慌大部分都能通过对配置文件的排查解决。签发成功会显示一些指纹信息、有效期确认无误后CA还会在index.txt里自动追加一条证书记录。此时CA的newcerts目录下会多出一个以序列号命名的证书副本这就是规律。4.3 配置subjectAltName支持域名和IP前面挖了个坑这里专门填上。早期版本签服务端证书只填CN字段就能用但现在的OpenSSL和浏览器版本都严格执行SANsubjectAltName校验。也就是说证书的CN写了域名浏览器不认必须SAN里有对应条目才放行。解决办法是在openssl配置里加上扩展段。以web服务器为例[ server_cert ] keyUsage critical, digitalSignature, keyEncipherment extendedKeyUsage serverAuth subjectAltName DNS:web.intranet.internal, IP:10.0.20.10然后通过-extensions参数指定扩展openssl ca -in web.csr -out web.crt -days 730 -extensions server_cert -config /etc/pki/CA/openssl_ca.cnf这里keyUsage和extendedKeyUsage是对证书用途的定义。作为web服务器证书digitalSignature数字签名、keyEncipherment密钥加密是标配extendedKeyUsage里只给serverAuth防止证书被拿去做其它用途。我之前一直把IP直连加进SAN就是因为内部有些应用用域名设计和测试环境直接IP访问两种方式都要覆盖不然同一张证书在不同场景下总有一个地方会报错。4.4 证书申请签发自动化脚本人工交互太繁琐了尤其是CSR生成这种步骤每次都要输入一堆信息既容易打错也不方便批量。我在CA和web服务器上分别准备了一个小脚本直接复用。CA侧签发脚本#!/bin/bash # 使用方法: ./issue_cert.sh CSR文件路径 证书输出路径 有效期天数 [域名] [IP] CSR$1 OUT$2 DAYS$3 DNS$4 IP$5 # 动态生成扩展配置把域名和IP写入SAN EXT_FILE$(mktemp) echo [ v3_req ] keyUsage critical, digitalSignature, keyEncipherment extendedKeyUsage serverAuth subjectAltName DNS:$DNS, IP:$IP $EXT_FILE openssl ca -in $CSR -out $OUT -days $DAYS \ -extensions v3_req -config /etc/pki/CA/openssl_ca.cnf -batch rm -f $EXT_FILE echo 证书已签发$OUT加了-batch参数免去是否确认继续的交互自动化流程会舒服非常多。实际执行也就一行命令./issue_cert.sh web.csr web.crt 730 web.intranet.internal 10.0.20.10签出来的手感和手动执行一模一样证书格式、有效期那些都正常。注意脚本开头最好加上判断防止参数遗漏这个就不过度展开了大家根据自己的环境去补强就行。4.5 证书格式转换从PEM到PFX/其他格式OpenSSL默认生成的证书是PEM格式也就是Base64编码的文本。Nginx可以直接用PEM但Windows服务器上的IIS经常需要PFX格式Java环境会用JKS格式所以转换这步几乎总是碰到。PEM转PFX的命令用的是web服务器的私钥和证书openssl pkcs12 -export -out web.pfx -inkey web.key -in web.crt -certfile cacert.pem提示输入导出密码设一个足够复杂的强密码再输一遍。转换完成后把web.pfx、web.key、web.crt三个文件打包好传到对应web服务器即大功告成。Java环境需要的JKS能用keytool命令从PFX转换keytool -importkeystore -srckeystore web.pfx -srcstoretype PKCS12 -destkeystore web.jks -deststoretype JKS格式转换通常只是文件格式变了证书内容、算法、有效期不会改变。转换完成后再用openssl x509命令核对一下证书信息是完整无损的这个习惯比什么都值钱。5. 证书部署与客户端信任配置5.1 Nginx和Apache下配置证书的完整示例其实部署证书的模式高度一致Nginx还算友好。以Nginx为例配置最核心的几行server { listen 443 ssl; server_name web.intranet.internal; ssl_certificate /etc/ssl/web/web.crt; ssl_certificate_key /etc/ssl/web/web.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; }配置完成后先执行nginx -t检查语法再reloadnginx -t systemctl reload nginxApache的配置类似VirtualHost *:443 ServerName web.intranet.internal SSLCertificateFile /etc/ssl/web/web.crt SSLCertificateKeyFile /etc/ssl/web/web.key /VirtualHost同样先执行apachectl -t检查再reload。5.2 根证书在终端的信任安装方法这一步不给web服务器配置漂不漂亮下结论因为web服务器配置完只是服务器侧完工了。真正的核心动作是让所有终端信任自家CA的根证书。Linux终端安装根证书cp cacert.pem /etc/pki/ca-trust/source/anchors/ update-ca-trust extract原理Linux系统通过ca-certificates这个机制集中管理信任的根证书把根证书放入anchors目录并运行update-ca-trust后系统内置的所有应用都会信任这张根证书。Windows域环境最推荐的方式是组策略自动下发把cacert.pem转成cacert.crt放到域控制器的组策略里。计算机配置 → 策略 → Windows设置 → 安全设置 → 公钥策略 → 受信任的根证书颁发机构 → 右键导入。域内终端下一次组策略刷新即自动信任这块在Windows环境省心。macOS/Linux桌面macOS可以通过钥匙串访问工具把证书拖入系统钥匙串并设为始终信任就行。Linux桌面按上面说的update-ca-trust即可。5.3 证书链完整性验证证书部署完先别急着结束考前验一下。方法是从web服务器证书开始往回追溯它由谁签发openssl verify -CAfile /etc/pki/CA/cacert.pem web.crt输出OK就表示证书链完整。如果报unable to get issuer certificate通常原因是没把根证书加到CAfile里或者证书本身不是这张根签的。另外web服务器返回证书链的时候尽量把服务端证书和中间证书一起配进去Nginx里可以直接把两者合在一个crt文件里cat web.crt cacert.pem web_chain.crt然后nginx的ssl_certificate直接指向web_chain.crt即可。浏览器端快速验证就是直接用无痕窗口访问https://web.intranet.internal点击地址栏锁头图标查看证书路径确认证书状态为有效、由内部根CA签发、没有被浏览器拦截。6. 常见问题与排查技巧实录6.1 证书无法验证时间不同步这个坑证书验证机制依赖时间戳。终端和服务器的时间差如果超过证书有效期容差一般5分钟就会报错CERt_HAS_EXPIRED之类的。我遇到过一次特别逗的情况CA服务器虚拟机重启后系统时间跑偏了签出来的证书生效时间变成了当前之前的某个点客户端访问时候好时坏。排查半天最后发现是时间不同步捣的鬼。内网环境装一下chrony做时间同步dnf install -y chrony systemctl enable --now chronyd chronyc makestepCA服务器的系统时间必须常年在线、精准因为所有证书的有效期计算都建立在信任这台服务器的时间是准确的这个基座上。6.2 浏览器提示证书不受信任的若干种原因拿着证书问浏览器为什么不信任90%的情况出在根证书没正确安装到终端。排查思路如下第一看证书链。浏览器里打开受影响的页面点击锁图标查证书路径。如果根证书那一段显示该证书不受信任就是根证书没送达到终端受信任区。检查组策略是否覆盖到了这台机器或者手动装一次根证书验证一下。第二看服务器证书和根证书是否匹配。如果服务器给的证书是另一把根签发的终端装了旧根也白搭。用openssl x509 -in web.crt -issuer和openssl x509 -in cacert.pem -subject对比一下issuer和subject要完全对上。第三看证书有效期是否已过。证书过期是最容易忽略但排查起来最快的一类问题直接看Validity。6.3 证书过期与续期机制设计证书签发时设置的有效期到了必须重新申请新证书。CA对服务端证书的默认有效期我通常给730天和商业CA一致。做过一次自动续期方案比较省事一是在CA服务器上写一个定时脚本每天检查证书剩余天数不足30天自动告警。二是在web服务器上用certbot或内置的mkcert这类工具但这只适合公网内网我重点依赖第一种方式。我这边用cron跑了一个简单的检查脚本#!/bin/bash # 检查证书剩余天数若小于30天则告警 DAYS_LEFT$(openssl x509 -in /etc/ssl/web/web.crt -enddate -noout | cut -d -f2 | awk {print $2, $1} | date -f - %s | { read ts_epoch; now_epoch$(date %s); echo $(( (ts_epoch - now_epoch) / 86400 )); }) if [ $DAYS_LEFT -lt 30 ]; then echo 证书剩余 $DAYS_LEFT 天请及时续期 | mail -s 证书到期提醒 opsinternal.com fi续期流程非常简单重新生成CSR → CA签发新证书 → 覆盖部署 → reload web服务。6.4 OpenSSL报错速查表报错信息原因解决办法unable to get issuer certificate没有把根证书加入验证链用-CAfile参数指定CA根证书或者把根证书合入web_chain.crtcertificate is not yet valid系统时间错误检查CA和web服务器的时区与时间用chrony校准unsupported certificate purpose证书扩展用途配置不正确检查extendedKeyUsage服务端证书必须含serverAuthdatabase not opened没创建index.txt或文件权限有问题touch /etc/pki/CA/index.txt确保CA目录可写signature algorithm is not allowed签发算法不对更新配置文件default_md为sha256不要用sha1self-signed certificate服务端证书配成了自签名而不是CA签发的确认web.crt确实是CA签发的不是openssl req -x509直接生成的6.5 私钥泄露后的吊销处理真到这一步CA私钥疑似泄露第一件事就是去CA服务器把CA证书和私钥全部重新生成一遍同时把旧根证书在终端全部移除。理论上证书吊销列表CRL可以做但实际操作上重签CA更彻底因为根证书一旦泄露整个信任链都无法保证安全。如果只是某个服务端证书泄露那简单把它在index.txt里的状态从V翻转为R:openssl ca -revoke /etc/pki/CA/newcerts/1001.pem -config /etc/pki/CA/openssl_ca.cnf吊销后生成CRL文件分发给客户端校验。不过内网环境我一般直接重签来的干脆。7. 运维经验与扩展思考搭完这套私有CA之后记账本上几个字省心。但随之也有一些注意事项值得多聊聊。运维核心原则CA服务器必须和web服务器分离。CA私钥只在CA服务器上保留原始私钥web服务器只拿到自己的私钥和签好的证书。CA服务器同时充当内核级高权限机器除运维管理员外禁止其他账号登录操作。有条件时建议把CA做成离线冷存储平时不开机签名时才挂载。另一个细节是证书统一管理。我建了一张简单的表记录每张证书的4个关键点签发时间、到期时间、对应服务器、域名/IP。到期前30天检查时就一目了然不用一台一台登录看。日志和签发记录也会定期压缩归档作为审计追溯资料。后续如果内部系统多了还可以引入vault这类现代密钥管理平台替代纯openssl手工签发。vault的PKI secret engine能自动签发、自动续期、自动吊销安全性和效率都高不少。但vault对运维能力要求也高像我这种十来台的规模openssl手工方案已经完全满足需求升级vault属于锦上添花不是必选。最后分享一个小技巧每次签完证书把CA根证书和所有已签发证书的指纹记录到一个固定文件里比如 /etc/pki/CA/fingerprints.txt。哪天有同事问这台机器证书为什么失效或者这张证书是哪台机器的直接对指纹比看文件名、看CN都更可靠。实际上机器重装、域名变更这类情况多了以后指纹是唯一不会骗人的身份标识。
返回列表