2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 11:27:17 +08:00

feat(证书): 优化签发及部署

This commit is contained in:
耗子
2024-03-24 15:55:10 +08:00
parent f50f2f7f2b
commit ef7d38f2af
11 changed files with 215 additions and 24 deletions

View File

@@ -669,3 +669,33 @@ func (r *CertController) ManualDNS(ctx http.Context) http.Response {
return Success(ctx, resolves)
}
// Deploy
//
// @Summary 部署证书
// @Description 部署面板证书管理的证书
// @Tags 证书管理
// @Accept json
// @Produce json
// @Security BearerToken
// @Param data body requests.CertDeploy true "request"
// @Success 200 {object} SuccessResponse
// @Router /panel/cert/deploy [post]
func (r *CertController) Deploy(ctx http.Context) http.Response {
var deployRequest requests.CertDeploy
sanitize := Sanitize(ctx, &deployRequest)
if sanitize != nil {
return sanitize
}
err := r.cert.Deploy(deployRequest.ID, deployRequest.WebsiteID)
if err != nil {
facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{
"certID": deployRequest.ID,
"error": err.Error(),
}).Info("部署证书失败")
return Error(ctx, http.StatusInternalServerError, err.Error())
}
return Success(ctx, nil)
}

View File

@@ -6,8 +6,8 @@ import (
)
type CertDeploy struct {
ID uint `form:"id" json:"id"`
WebsiteID uint `form:"website_id" json:"website_id"`
ID uint `form:"id" json:"id" filter:"uint"`
WebsiteID uint `form:"website_id" json:"website_id" filter:"uint"`
}
func (r *CertDeploy) Authorize(ctx http.Context) error {

View File

@@ -270,6 +270,45 @@ const docTemplate = `{
}
}
},
"/panel/cert/deploy": {
"post": {
"security": [
{
"BearerToken": []
}
],
"description": "部署面板证书管理的证书",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"证书管理"
],
"summary": "部署证书",
"parameters": [
{
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.CertDeploy"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/panel/cert/dns": {
"get": {
"security": [
@@ -4068,6 +4107,17 @@ const docTemplate = `{
}
}
},
"requests.CertDeploy": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"website_id": {
"type": "integer"
}
}
},
"requests.CertStore": {
"type": "object",
"properties": {

View File

@@ -263,6 +263,45 @@
}
}
},
"/panel/cert/deploy": {
"post": {
"security": [
{
"BearerToken": []
}
],
"description": "部署面板证书管理的证书",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"证书管理"
],
"summary": "部署证书",
"parameters": [
{
"description": "request",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/requests.CertDeploy"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.SuccessResponse"
}
}
}
}
},
"/panel/cert/dns": {
"get": {
"security": [
@@ -4061,6 +4100,17 @@
}
}
},
"requests.CertDeploy": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"website_id": {
"type": "integer"
}
}
},
"requests.CertStore": {
"type": "object",
"properties": {

View File

@@ -253,6 +253,13 @@ definitions:
type: string
type: array
type: object
requests.CertDeploy:
properties:
id:
type: integer
website_id:
type: integer
type: object
requests.CertStore:
properties:
auto_renew:
@@ -793,6 +800,30 @@ paths:
summary: 更新证书
tags:
- 证书管理
/panel/cert/deploy:
post:
consumes:
- application/json
description: 部署面板证书管理的证书
parameters:
- description: request
in: body
name: data
required: true
schema:
$ref: '#/definitions/requests.CertDeploy'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controllers.SuccessResponse'
security:
- BearerToken: []
summary: 部署证书
tags:
- 证书管理
/panel/cert/dns:
get:
description: 获取面板证书管理的 DNS 接口列表

View File

@@ -23,4 +23,5 @@ type Cert interface {
ObtainManual(ID uint) (acme.Certificate, error)
ManualDNS(ID uint) ([]acme.DNSRecord, error)
Renew(ID uint) (acme.Certificate, error)
Deploy(ID, WebsiteID uint) error
}

View File

@@ -4,6 +4,7 @@ package services
import (
"context"
"errors"
"strings"
"time"
"github.com/goravel/framework/facades"
@@ -257,6 +258,11 @@ func (s *CertImpl) ObtainAuto(ID uint) (acme.Certificate, error) {
if cert.Website == nil {
return acme.Certificate{}, errors.New("该证书没有关联网站,无法自动签发")
} else {
for _, domain := range cert.Domains {
if strings.Contains(domain, "*") {
return acme.Certificate{}, errors.New("通配符域名无法使用 HTTP 验证")
}
}
client.UseHTTP(cert.Website.Path)
}
}
@@ -275,10 +281,10 @@ func (s *CertImpl) ObtainAuto(ID uint) (acme.Certificate, error) {
}
if cert.Website != nil {
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
return acme.Certificate{}, err
}
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
return acme.Certificate{}, err
}
if err = tools.ServiceReload("openresty"); err != nil {
@@ -315,10 +321,10 @@ func (s *CertImpl) ObtainManual(ID uint) (acme.Certificate, error) {
}
if cert.Website != nil {
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
return acme.Certificate{}, err
}
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
return acme.Certificate{}, err
}
if err = tools.ServiceReload("openresty"); err != nil {
@@ -377,6 +383,11 @@ func (s *CertImpl) Renew(ID uint) (acme.Certificate, error) {
if cert.Website == nil {
return acme.Certificate{}, errors.New("该证书没有关联网站,无法续签,可以尝试手动签发")
} else {
for _, domain := range cert.Domains {
if strings.Contains(domain, "*") {
return acme.Certificate{}, errors.New("通配符域名无法使用 HTTP 验证")
}
}
client.UseHTTP(cert.Website.Path)
}
}
@@ -398,10 +409,10 @@ func (s *CertImpl) Renew(ID uint) (acme.Certificate, error) {
}
if cert.Website != nil {
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", cert.Cert, 0644); err != nil {
return acme.Certificate{}, err
}
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
if err = tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", cert.Key, 0644); err != nil {
return acme.Certificate{}, err
}
if err = tools.ServiceReload("openresty"); err != nil {
@@ -412,6 +423,37 @@ func (s *CertImpl) Renew(ID uint) (acme.Certificate, error) {
return ssl, nil
}
// Deploy 部署证书
func (s *CertImpl) Deploy(ID, WebsiteID uint) error {
var cert models.Cert
err := facades.Orm().Query().Where("id = ?", ID).First(&cert)
if err != nil {
return err
}
if cert.Cert == "" || cert.Key == "" {
return errors.New("该证书没有签发成功,无法部署")
}
website := models.Website{}
err = facades.Orm().Query().Where("id = ?", WebsiteID).First(&website)
if err != nil {
return err
}
if err = tools.Write("/www/server/vhost/ssl/"+website.Name+".pem", cert.Cert, 0644); err != nil {
return err
}
if err = tools.Write("/www/server/vhost/ssl/"+website.Name+".key", cert.Key, 0644); err != nil {
return err
}
if err = tools.ServiceReload("openresty"); err != nil {
return err
}
return nil
}
func (s *CertImpl) getClient(cert models.Cert) (*acme.Client, error) {
var ca string
var eab *acme.EAB

View File

@@ -393,10 +393,10 @@ func (r *WebsiteImpl) SaveConfig(config requests.SaveConfig) error {
// SSL
ssl := config.Ssl
website.Ssl = ssl
if err := tools.Write("/www/server/vhost/ssl/"+website.Name+".pem", config.SslCertificate, 0644); err != nil {
if err = tools.Write("/www/server/vhost/ssl/"+website.Name+".pem", config.SslCertificate, 0644); err != nil {
return err
}
if err := tools.Write("/www/server/vhost/ssl/"+website.Name+".key", config.SslCertificateKey, 0644); err != nil {
if err = tools.Write("/www/server/vhost/ssl/"+website.Name+".key", config.SslCertificateKey, 0644); err != nil {
return err
}
if ssl {

View File

@@ -30,12 +30,3 @@ func Camel(s string) string {
func LowerCamel(s string) string {
return strcase.ToLowerCamel(s)
}
func ContainsString(arr []string, str string) bool {
for _, s := range arr {
if s == str {
return true
}
}
return false
}

View File

@@ -38,8 +38,3 @@ func (s *StrTestSuite) TestLowerCamel() {
s.Equal("topicComment", LowerCamel("topic_comment"))
s.Equal("topicComment", LowerCamel("TopicComment"))
}
func (s *StrTestSuite) TestContainsString() {
s.True(ContainsString([]string{"a", "b", "c"}, "a"))
s.False(ContainsString([]string{"a", "b", "c"}, "d"))
}

View File

@@ -80,6 +80,7 @@ func Api() {
r.Post("obtain", certController.Obtain)
r.Post("renew", certController.Renew)
r.Post("manualDNS", certController.ManualDNS)
r.Post("deploy", certController.Deploy)
})
r.Prefix("plugin").Middleware(middleware.Jwt()).Group(func(r route.Router) {
pluginController := controllers.NewPluginController()