2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-05 00:39:32 +08:00
Files
panel/internal/services/cert.go
2023-12-14 02:09:33 +08:00

474 lines
12 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 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
}