免费ssl证书哪里获取?
我选择了 OHTTPS。
怎么玩?
一、域名申请
SSL 证书一般需要有域名。(无域名,仅公网IP,没有尝试过,未知可行否)
建议在大平台上申请,OHTTPS 这类平台对其支持度会好点。
笔者在阿里云申请了域名:life666.top 。其子域名是一款程序员代码生成转换的在线工具 tool.life666.top/ , 欢迎点击使用(^.^))
二、创建https证书
快速上手资料:OHTTPS - 免费HTTPS证书、自动更新、自动部署
按照上文教程,基本没啥问题。
大致步骤:
1、创建 DNS 授权
这一步是为了证明,你对证书要绑定的域名有控制权,即域名确实归你所有。两种方式:
1)手动添加一条它指定的文本域名解析;
2)添加DNS授权,这样OHTTPS便可以自动完成你本需要手动完成的操作。
2、创建 HTTPS 证书
编辑切换为居中
添加图片注释,不超过 140 字(可选)
按照指引一步步操作即可。
3 部署
OHTTPS 平台提供了一些云平台的自动部署:「部署节点」菜单,可实现一键部署。
但是,如果你和笔者一样,比较寒酸,需要部署到刚氪金购买的小小的云主机或VPS上,就需要手动部署了。
下载证书文件到本地,下面两个文件:
cert.key 私钥
fullchain.cer 证书
上传到你的服务器上。
如何配置安装证书?这个依据你的服务而定,如果是 Nginx 可参考 如何在Nginx或Tengine服务器上安装证书_数字证书管理服务-阿里云帮助中心
如何0成本续期?
免费证书,一般初次申请,这份辛苦基本可以接受。
但是免费的午餐总是有期限,这个 OHTTPS 90天有效期,如何0成本续期?
证书管理》点击证书》更新证书,可更新证书。
但是,如果你和我一样需要手动部署,3个月就要手动上传一次,会略微有点小小的不爽。
OHTTPS 提供了 webhook 自动部署能力,就是,你可以提供一个 http 回调地址,当你点击「部署证书」时,调用你的接口,将证书传给你(json)。
为此,笔者在我的vps上常驻了一个小小的web服务,提供回调接口,接收证书请求参数,自动写到 Nginx 的证书目录下,实现自动一键更新和部署的目的。
编辑切换为居中
添加图片注释,不超过 140 字(可选)
自动部署 webhook 服务源码
采用 golang gin web 框架编写。
1、项目结构
编辑
添加图片注释,不超过 140 字(可选)
2、main.go
package main
import (
_ "github.com/CodyGuo/godaemon"
"github.com/gin-gonic/gin"
"log"
"os"
"ssl-webhook/src"
)
func main() {
file, err := os.OpenFile("log.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
log.SetOutput(file)
log.Println("==== ssl-webhook ====")
r := gin.Default()
r.GET(src.CONTEXT_PATH+"/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
src.Initialize(r)
r.Run(":10010")
}
3、handler.go
package src
import (
"crypto/md5"
"encoding/hex"
"fmt"
"github.com/gin-gonic/gin"
"io"
"log"
"net/http"
"os"
"strconv"
"strings"
"time"
)
func Initialize(engine *gin.Engine) {
engine.GET(CONTEXT_PATH+"/hello", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "world",
})
})
ohttps := engine.Group(CONTEXT_PATH + "/ohttps")
{
ohttps.GET("/hello", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"success": "hello ohttps",
})
})
// [OHTTPS - 免费HTTPS证书、自动更新、自动部署](https://ohttps.com/docs/cloud/webhook/webhook)
ohttps.POST("/deploy", func(c *gin.Context) {
//request := make(map[string]interface{})
var request OhttpsSslDeployRequest
fmt.Println(c.Query("a")) // url 参数
err := c.ShouldBind(&request)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"error": err.Error(),
})
log.Panic("[ERROR] ", err)
return
}
log.Println(request.Payload.CertificateName, request.Payload.CertificateDomains)
// 验签
md5 := md5.New()
io.WriteString(md5, strconv.FormatInt(request.Timestamp, 10)+":"+CALLBACK_TOKEN)
md5sum := hex.EncodeToString(md5.Sum(nil))
if md5sum != request.Sign {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"error": "签名验证不通过",
})
log.Panic("[ERROR] 签名验证不通过, request.Sign: ", request.Sign, ", request.Timestamp: ", request.Timestamp, ", md5sum: ", md5sum)
return
}
// 按关联的域名, 生成对应的目录 先带 .tmp 后缀,
// 然后, 正在使用的目录更名带 日期后缀 备份, 新的去掉 tmp 后缀
for _, domain := range request.Payload.CertificateDomains {
domainCertPath := domain
if strings.HasPrefix(domain, "*") {
// 泛域名
domainCertPath = domain[2:]
}
tmpCertPath := NGINX_CERT_BASE_PATH + "/" + domainCertPath + ".tmp"
os.MkdirAll(tmpCertPath, os.ModePerm)
certKeyFile, _ := os.OpenFile(tmpCertPath+"/cert.key", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
defer certKeyFile.Close()
certKeyFile.WriteString(request.Payload.CertificateCertKey)
fullchainFile, _ := os.OpenFile(tmpCertPath+"/fullchain.cer", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
defer fullchainFile.Close()
fullchainFile.WriteString(request.Payload.CertificateFullchainCerts)
// 原目录备份
os.Rename(NGINX_CERT_BASE_PATH+"/"+domainCertPath, NGINX_CERT_BASE_PATH+"/"+domainCertPath+"."+time.Now().Format("20060102150405"))
// 启用新的
os.Rename(tmpCertPath, tmpCertPath[:len(tmpCertPath)-4])
log.Printf("部署 %s 成功, 路径: %s\n", domain, tmpCertPath[:len(tmpCertPath)-4])
}
c.JSON(http.StatusOK, gin.H{
"success": true,
})
})
}
}
type OhttpsSslDeployRequest struct {
Timestamp int64 `form:"timestamp"` // 请求时间戳
Payload struct {
CertificateName string `form:"certificateName"` // 证书ID
CertificateDomains []string `form:"certificateDomains"` // 证书关联域名
CertificateCertKey string `form:"certificateCertKey"` // 证书私钥(PEM格式)
CertificateFullchainCerts string `form:"certificateFullchainCerts"` // 证书(包含证书和中间证书)(PEM格式)
CertificateExpireAt int64 `form:"certificateExpireAt"` // 证书过期时间
} `form:"payload"`
Sign string `form:"sign"` // 请求签名,`${timestamp}:${回调令牌}`的32位小写md5值
}
3、consts.go
package src
// CONTEXT_PATH 根路径
const CONTEXT_PATH = "/sslwebhook"
const CALLBACK_TOKEN = "回调的token,用于验证签名,OHTTPS 平台「部署节点」菜单下可以查到"
// NGINX_CERT_BASE_PATH nginx 证书根目录
const NGINX_CERT_BASE_PATH = "/etc/nginx/cert"
4、构建脚本
# windows 交叉编译 Linux 相关参数配置
export CGO_ENABLED=0
export GOOS=linux
export GOARCH=amd64
# 不加文件名, 生成和当前路径一致的名字
go build
5、README.md
# SSL 证书自动部署 webhook web服务
> 暂仅仅支持 OHTTPS 平台的自动部署。https://ohttps.com/
1. 构建
```shell
cd {项目根目录}
sh bin\build.sh
```
2. 上传
生成 ssl-webhook 可执行文件。
上传至服务器。
添加执行权限
```shell
chmod 777 ssl-webhook
```
3. 启动
启动服务(后台)
```shell
./ssl-webhook -d=true
```
4. 部署证书
证书管理》点选要部署的证书》部署证书
结果:
会在 nginx 证书目录下("/etc/nginx/cert"),生成对应域名的目录(域名即使目录名)。
内含两个文件:
```
cert.key // 私钥
fullchain.cer // 证书
```
改域名下原有证书目录,重命名,添加当时时间后缀备份。
