2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-05 04:37:17 +08:00
Files
panel/app/services/cert.go
2023-11-14 01:54:26 +08:00

493 lines
13 KiB
Go

// Package services 证书服务
package services
import (
"errors"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/goravel/framework/facades"
requests "panel/app/http/requests/cert"
"panel/app/models"
"panel/pkg/acme"
"panel/pkg/tools"
)
type Cert interface {
UserStore(request requests.UserStore) error
UserUpdate(request requests.UserUpdate) error
UserShow(ID uint) (models.CertUser, error)
UserDestroy(ID uint) error
DNSStore(request requests.DNSStore) error
DNSUpdate(request requests.DNSUpdate) error
DNSShow(ID uint) (models.CertDNS, error)
DNSDestroy(ID uint) error
CertStore(request requests.CertStore) error
CertUpdate(request requests.CertUpdate) error
CertShow(ID uint) (models.Cert, error)
CertDestroy(ID uint) error
ObtainAuto(ID uint) (certificate.Resource, error)
ObtainManual(ID uint) (certificate.Resource, error)
ManualDNS(ID uint) (map[string]acme.Resolve, error)
Renew(ID uint) (certificate.Resource, error)
}
type CertImpl struct {
}
func NewCertImpl() *CertImpl {
return &CertImpl{}
}
// UserStore 添加用户
func (s *CertImpl) UserStore(request requests.UserStore) error {
var user models.CertUser
user.CA = request.CA
user.Email = request.Email
user.Kid = &request.Kid
user.HmacEncoded = &request.HmacEncoded
user.KeyType = request.KeyType
var err error
var client *acme.Client
switch user.CA {
case "letsencrypt":
client, err = acme.NewRegisterClient(user.Email, acme.CALetEncrypt, certcrypto.KeyType(user.KeyType))
case "buypass":
client, err = acme.NewRegisterClient(user.Email, acme.CABuypass, certcrypto.KeyType(user.KeyType))
case "zerossl":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAZeroSSL, certcrypto.KeyType(user.KeyType))
case "sslcom":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CASSLcom, certcrypto.KeyType(user.KeyType))
case "google":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAGoogle, certcrypto.KeyType(user.KeyType))
default:
return errors.New("CA 提供商不支持")
}
if err != nil {
return errors.New("向 CA 注册账号失败,请检查参数是否正确")
}
privateKey, err := acme.GetPrivateKey(client.User.GetPrivateKey(), acme.KeyType(user.KeyType))
if err != nil {
return errors.New("获取私钥失败")
}
user.PrivateKey = string(privateKey)
return facades.Orm().Query().Create(&user)
}
// UserUpdate 更新用户
func (s *CertImpl) UserUpdate(request requests.UserUpdate) error {
var user models.CertUser
err := facades.Orm().Query().Where("id = ?", request.ID).First(&user)
if err != nil {
return err
}
user.CA = request.CA
user.Email = request.Email
user.Kid = &request.Kid
user.HmacEncoded = &request.HmacEncoded
user.KeyType = request.KeyType
var client *acme.Client
switch user.CA {
case "letsencrypt":
client, err = acme.NewRegisterClient(user.Email, acme.CALetEncrypt, certcrypto.KeyType(user.KeyType))
case "buypass":
client, err = acme.NewRegisterClient(user.Email, acme.CABuypass, certcrypto.KeyType(user.KeyType))
case "zerossl":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAZeroSSL, certcrypto.KeyType(user.KeyType))
case "sslcom":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CASSLcom, certcrypto.KeyType(user.KeyType))
case "google":
client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAGoogle, certcrypto.KeyType(user.KeyType))
default:
return errors.New("CA 提供商不支持")
}
if err != nil {
return errors.New("向 CA 注册账号失败,请检查参数是否正确")
}
privateKey, err := acme.GetPrivateKey(client.User.GetPrivateKey(), acme.KeyType(user.KeyType))
if err != nil {
return errors.New("获取私钥失败")
}
user.PrivateKey = string(privateKey)
return facades.Orm().Query().Save(&user)
}
// UserShow 根据 ID 获取用户
func (s *CertImpl) UserShow(ID uint) (models.CertUser, error) {
var user models.CertUser
err := facades.Orm().Query().With("Certs").Where("id = ?", ID).First(&user)
return user, err
}
// UserDestroy 删除用户
func (s *CertImpl) UserDestroy(ID uint) error {
var cert models.Cert
err := facades.Orm().Query().Where("user_id = ?", ID).First(&cert)
if err != nil {
return err
}
if cert.ID != 0 {
return errors.New("该用户下存在证书,无法删除")
}
_, err = facades.Orm().Query().Delete(&models.CertUser{}, ID)
return err
}
// DNSStore 添加 DNS
func (s *CertImpl) DNSStore(request requests.DNSStore) error {
var dns models.CertDNS
dns.Type = request.Type
dns.Name = request.Name
dns.Data = request.Data
return facades.Orm().Query().Create(&dns)
}
// DNSUpdate 更新 DNS
func (s *CertImpl) DNSUpdate(request requests.DNSUpdate) error {
var dns models.CertDNS
err := facades.Orm().Query().Where("id = ?", request.ID).First(&dns)
if err != nil {
return err
}
dns.Type = request.Type
dns.Name = request.Name
dns.Data = request.Data
return facades.Orm().Query().Save(&dns)
}
// DNSShow 根据 ID 获取 DNS
func (s *CertImpl) DNSShow(ID uint) (models.CertDNS, error) {
var dns models.CertDNS
err := facades.Orm().Query().With("Certs").Where("id = ?", ID).First(&dns)
return dns, err
}
// DNSDestroy 删除 DNS
func (s *CertImpl) DNSDestroy(ID uint) error {
var cert models.Cert
err := facades.Orm().Query().Where("dns_id = ?", ID).First(&cert)
if err != nil {
return err
}
if cert.ID != 0 {
return errors.New("该 DNS 接口下存在证书,无法删除")
}
_, err = facades.Orm().Query().Delete(&models.CertDNS{}, ID)
return err
}
// CertStore 添加证书
func (s *CertImpl) CertStore(request requests.CertStore) error {
var cert models.Cert
cert.Type = request.Type
cert.Domains = request.Domains
cert.AutoRenew = request.AutoRenew
cert.UserID = request.UserID
if request.DNSID != nil {
cert.DNSID = request.DNSID
}
if request.WebsiteID != nil {
cert.WebsiteID = request.WebsiteID
}
return facades.Orm().Query().Create(&cert)
}
// CertUpdate 更新证书
func (s *CertImpl) CertUpdate(request requests.CertUpdate) error {
var cert models.Cert
err := facades.Orm().Query().Where("id = ?", request.ID).First(&cert)
if err != nil {
return err
}
cert.Type = request.Type
cert.Domains = request.Domains
cert.AutoRenew = request.AutoRenew
cert.UserID = request.UserID
if request.DNSID != nil {
cert.DNSID = request.DNSID
}
if request.WebsiteID != nil {
cert.WebsiteID = request.WebsiteID
}
return facades.Orm().Query().Save(&cert)
}
// CertShow 根据 ID 获取证书
func (s *CertImpl) CertShow(ID uint) (models.Cert, error) {
var cert models.Cert
err := facades.Orm().Query().With("User").With("DNS").With("Website").Where("id = ?", ID).First(&cert)
return cert, err
}
// CertDestroy 删除证书
func (s *CertImpl) CertDestroy(ID uint) error {
var cert models.Cert
err := facades.Orm().Query().Where("id = ?", ID).First(&cert)
if err != nil {
return err
}
_, err = facades.Orm().Query().Delete(&models.Cert{}, ID)
return err
}
// ObtainAuto 自动签发证书
func (s *CertImpl) ObtainAuto(ID uint) (certificate.Resource, error) {
var cert models.Cert
err := facades.Orm().Query().With("Website").With("User").With("DNS").Where("id = ?", ID).First(&cert)
if err != nil {
return certificate.Resource{}, err
}
var ca string
switch cert.User.CA {
case "letsencrypt":
ca = acme.CALetEncrypt
case "buypass":
ca = acme.CABuypass
case "zerossl":
ca = acme.CAZeroSSL
case "sslcom":
ca = acme.CASSLcom
case "google":
ca = acme.CAGoogle
}
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
if err != nil {
return certificate.Resource{}, err
}
if cert.DNS != nil {
err = client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data)
} else {
if cert.Website == nil {
return certificate.Resource{}, errors.New("该证书没有关联网站,无法自动签发")
} else {
err = client.UseHTTP(cert.Website.Path)
}
}
if err != nil {
return certificate.Resource{}, err
}
ssl, err := client.ObtainSSL(cert.Domains)
if err != nil {
return certificate.Resource{}, err
}
cert.CertURL = &ssl.CertURL
cert.Cert = string(ssl.Certificate)
cert.Key = string(ssl.PrivateKey)
err = facades.Orm().Query().Save(&cert)
if err != nil {
return certificate.Resource{}, err
}
if cert.Website != nil {
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644); err != nil {
return certificate.Resource{}, err
}
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644); err != nil {
return certificate.Resource{}, err
}
if _, err := tools.Exec("systemctl reload openresty"); err != nil {
return certificate.Resource{}, err
}
}
return ssl, nil
}
// ObtainManual 手动签发证书
func (s *CertImpl) ObtainManual(ID uint) (certificate.Resource, error) {
var cert models.Cert
err := facades.Orm().Query().With("User").Where("id = ?", ID).First(&cert)
if err != nil {
return certificate.Resource{}, err
}
var ca string
switch cert.User.CA {
case "letsencrypt":
ca = acme.CALetEncrypt
case "buypass":
ca = acme.CABuypass
case "zerossl":
ca = acme.CAZeroSSL
case "sslcom":
ca = acme.CASSLcom
case "google":
ca = acme.CAGoogle
}
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
if err != nil {
return certificate.Resource{}, err
}
err = client.UseManualDns()
if err != nil {
return certificate.Resource{}, err
}
ssl, err := client.ObtainSSL(cert.Domains)
if err != nil {
return certificate.Resource{}, err
}
cert.CertURL = &ssl.CertURL
cert.Cert = string(ssl.Certificate)
cert.Key = string(ssl.PrivateKey)
err = facades.Orm().Query().Save(&cert)
if err != nil {
return certificate.Resource{}, err
}
if cert.Website != nil {
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644); err != nil {
return certificate.Resource{}, err
}
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644); err != nil {
return certificate.Resource{}, err
}
if _, err := tools.Exec("systemctl reload openresty"); err != nil {
return certificate.Resource{}, err
}
}
return ssl, nil
}
// ManualDNS 获取手动 DNS 解析信息
func (s *CertImpl) ManualDNS(ID uint) (map[string]acme.Resolve, error) {
var cert models.Cert
err := facades.Orm().Query().With("User").Where("id = ?", ID).First(&cert)
if err != nil {
return nil, err
}
var ca string
switch cert.User.CA {
case "letsencrypt":
ca = acme.CALetEncrypt
case "buypass":
ca = acme.CABuypass
case "zerossl":
ca = acme.CAZeroSSL
case "sslcom":
ca = acme.CASSLcom
case "google":
ca = acme.CAGoogle
}
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
if err != nil {
return nil, err
}
err = client.UseManualDns()
if err != nil {
return nil, err
}
return client.GetDNSResolve(cert.Domains)
}
// Renew 续签证书
func (s *CertImpl) Renew(ID uint) (certificate.Resource, error) {
var cert models.Cert
err := facades.Orm().Query().With("Website").With("User").With("DNS").Where("id = ?", ID).First(&cert)
if err != nil {
return certificate.Resource{}, err
}
var ca string
switch cert.User.CA {
case "letsencrypt":
ca = acme.CALetEncrypt
case "buypass":
ca = acme.CABuypass
case "zerossl":
ca = acme.CAZeroSSL
case "sslcom":
ca = acme.CASSLcom
case "google":
ca = acme.CAGoogle
}
client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType))
if err != nil {
return certificate.Resource{}, err
}
if cert.CertURL == nil {
return certificate.Resource{}, errors.New("该证书没有签发成功,无法续签")
}
if cert.DNS != nil {
err = client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data)
} else {
if cert.Website == nil {
return certificate.Resource{}, errors.New("该证书没有关联网站,无法续签,可以尝试手动签发")
} else {
err = client.UseHTTP(cert.Website.Path)
}
}
if err != nil {
return certificate.Resource{}, err
}
ssl, err := client.RenewSSL(*cert.CertURL)
if err != nil {
return certificate.Resource{}, err
}
cert.CertURL = &ssl.CertURL
cert.Cert = string(ssl.Certificate)
cert.Key = string(ssl.PrivateKey)
err = facades.Orm().Query().Save(&cert)
if err != nil {
return certificate.Resource{}, err
}
if cert.Website != nil {
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644); err != nil {
return certificate.Resource{}, err
}
if err := tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644); err != nil {
return certificate.Resource{}, err
}
if _, err := tools.Exec("systemctl reload openresty"); err != nil {
return certificate.Resource{}, err
}
}
return ssl, nil
}