mirror of
https://github.com/TeaOSLab/EdgeAPI.git
synced 2025-11-03 15:00:27 +08:00
216 lines
5.2 KiB
Go
216 lines
5.2 KiB
Go
package acme
|
||
|
||
import (
|
||
"fmt"
|
||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||
"github.com/go-acme/lego/v4/certcrypto"
|
||
"github.com/go-acme/lego/v4/certificate"
|
||
"github.com/go-acme/lego/v4/lego"
|
||
acmelog "github.com/go-acme/lego/v4/log"
|
||
"github.com/go-acme/lego/v4/registration"
|
||
"github.com/iwind/TeaGo/Tea"
|
||
"io"
|
||
"log"
|
||
)
|
||
|
||
type Request struct {
|
||
debug bool
|
||
|
||
task *Task
|
||
onAuth AuthCallback
|
||
}
|
||
|
||
func NewRequest(task *Task) *Request {
|
||
return &Request{
|
||
task: task,
|
||
}
|
||
}
|
||
|
||
func (this *Request) Debug() {
|
||
this.debug = true
|
||
}
|
||
|
||
func (this *Request) OnAuth(onAuth AuthCallback) {
|
||
this.onAuth = onAuth
|
||
}
|
||
|
||
func (this *Request) Run() (certData []byte, keyData []byte, err error) {
|
||
if this.task.Provider == nil {
|
||
err = errors.New("provider should not be nil")
|
||
return
|
||
}
|
||
if this.task.Provider.RequireEAB && this.task.Account == nil {
|
||
err = errors.New("account should not be nil when provider require EAB")
|
||
return
|
||
}
|
||
|
||
switch this.task.AuthType {
|
||
case AuthTypeDNS:
|
||
return this.runDNS()
|
||
case AuthTypeHTTP:
|
||
return this.runHTTP()
|
||
default:
|
||
err = errors.New("invalid task type '" + this.task.AuthType + "'")
|
||
return
|
||
}
|
||
}
|
||
|
||
func (this *Request) runDNS() (certData []byte, keyData []byte, err error) {
|
||
if !this.debug {
|
||
if !Tea.IsTesting() {
|
||
acmelog.Logger = log.New(io.Discard, "", log.LstdFlags)
|
||
}
|
||
}
|
||
|
||
if this.task.User == nil {
|
||
err = errors.New("'user' must not be nil")
|
||
return
|
||
}
|
||
if this.task.DNSProvider == nil {
|
||
err = errors.New("'dnsProvider' must not be nil")
|
||
return
|
||
}
|
||
if len(this.task.DNSDomain) == 0 {
|
||
err = errors.New("'dnsDomain' must not be empty")
|
||
return
|
||
}
|
||
if len(this.task.Domains) == 0 {
|
||
err = errors.New("'domains' must not be empty")
|
||
return
|
||
}
|
||
|
||
var config = lego.NewConfig(this.task.User)
|
||
config.Certificate.KeyType = certcrypto.RSA2048
|
||
config.CADirURL = this.task.Provider.APIURL
|
||
config.UserAgent = teaconst.ProductName + "/" + teaconst.Version
|
||
|
||
client, err := lego.NewClient(config)
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
|
||
// 注册用户
|
||
var resource = this.task.User.GetRegistration()
|
||
if resource != nil {
|
||
_, err = client.Registration.QueryRegistration()
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
} else {
|
||
if this.task.Provider.RequireEAB {
|
||
resource, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||
TermsOfServiceAgreed: true,
|
||
Kid: this.task.Account.EABKid,
|
||
HmacEncoded: this.task.Account.EABKey,
|
||
})
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("register user failed: %w", err)
|
||
}
|
||
err = this.task.User.Register(resource)
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
} else {
|
||
resource, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
err = this.task.User.Register(resource)
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
}
|
||
}
|
||
|
||
err = client.Challenge.SetDNS01Provider(NewDNSProvider(this.task.DNSProvider, this.task.DNSDomain))
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
|
||
// 申请è¯<C3A8>书
|
||
var request = certificate.ObtainRequest{
|
||
Domains: this.task.Domains,
|
||
Bundle: true,
|
||
}
|
||
certResource, err := client.Certificate.Obtain(request)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("obtain cert failed: %w", err)
|
||
}
|
||
|
||
return certResource.Certificate, certResource.PrivateKey, nil
|
||
}
|
||
|
||
func (this *Request) runHTTP() (certData []byte, keyData []byte, err error) {
|
||
if !this.debug {
|
||
if !Tea.IsTesting() {
|
||
acmelog.Logger = log.New(io.Discard, "", log.LstdFlags)
|
||
}
|
||
}
|
||
|
||
if this.task.User == nil {
|
||
err = errors.New("'user' must not be nil")
|
||
return
|
||
}
|
||
|
||
var config = lego.NewConfig(this.task.User)
|
||
config.Certificate.KeyType = certcrypto.RSA2048
|
||
config.CADirURL = this.task.Provider.APIURL
|
||
config.UserAgent = teaconst.ProductName + "/" + teaconst.Version
|
||
|
||
client, err := lego.NewClient(config)
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
|
||
// 注册用户
|
||
var resource = this.task.User.GetRegistration()
|
||
if resource != nil {
|
||
_, err = client.Registration.QueryRegistration()
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
} else {
|
||
if this.task.Provider.RequireEAB {
|
||
resource, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
|
||
TermsOfServiceAgreed: true,
|
||
Kid: this.task.Account.EABKid,
|
||
HmacEncoded: this.task.Account.EABKey,
|
||
})
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("register user failed: %w", err)
|
||
}
|
||
err = this.task.User.Register(resource)
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
} else {
|
||
resource, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
err = this.task.User.Register(resource)
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
}
|
||
}
|
||
|
||
err = client.Challenge.SetHTTP01Provider(NewHTTPProvider(this.onAuth))
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
|
||
// 申请è¯<C3A8>书
|
||
var request = certificate.ObtainRequest{
|
||
Domains: this.task.Domains,
|
||
Bundle: true,
|
||
}
|
||
certResource, err := client.Certificate.Obtain(request)
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
|
||
return certResource.Certificate, certResource.PrivateKey, nil
|
||
}
|