From 748adadd1ec40353223ea55c7942161cdee92cc3 Mon Sep 17 00:00:00 2001 From: wsczx Date: Tue, 4 Apr 2023 22:35:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=9D=E5=AD=98Lego=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=EF=BC=8C=E9=81=BF=E5=85=8D=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E5=AF=BC=E8=87=B4=E5=A4=B1=E8=B4=A5=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8A=A8=E6=80=81=E5=8A=A0=E8=BD=BDTLS?= =?UTF-8?q?=E8=AF=81=E4=B9=A6=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/admin/api_cert.go | 137 +++++++++++++++------------------------ server/admin/server.go | 19 ++++-- server/dbdata/cert.go | 119 ++++++++++++++++++++++++++++++++++ server/dbdata/db.go | 35 ++++++---- server/dbdata/setting.go | 21 ------ server/handler/server.go | 11 ++-- 6 files changed, 214 insertions(+), 128 deletions(-) create mode 100755 server/dbdata/cert.go diff --git a/server/admin/api_cert.go b/server/admin/api_cert.go index 7b0b162..677e36c 100755 --- a/server/admin/api_cert.go +++ b/server/admin/api_cert.go @@ -1,18 +1,14 @@ package admin import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" "crypto/tls" "crypto/x509" - "encoding/base64" "encoding/json" "fmt" "io" "net/http" "os" + "sync" "time" "github.com/bjdgyc/anylink/base" @@ -21,32 +17,15 @@ import ( "github.com/go-acme/lego/v4/certificate" "github.com/go-acme/lego/v4/challenge/dns01" "github.com/go-acme/lego/v4/lego" - "github.com/go-acme/lego/v4/providers/dns/alidns" - "github.com/go-acme/lego/v4/providers/dns/cloudflare" - "github.com/go-acme/lego/v4/providers/dns/tencentcloud" "github.com/go-acme/lego/v4/registration" - "github.com/xenolf/lego/challenge" - "golang.org/x/crypto/scrypt" ) -type LegoUser struct { - Email string - Registration *registration.Resource - key crypto.PrivateKey -} type LeGoClient struct { + mutex sync.Mutex Client *lego.Client + dbdata.LegoUserData } -func (u *LegoUser) GetEmail() string { - return u.Email -} -func (u LegoUser) GetRegistration() *registration.Resource { - return u.Registration -} -func (u *LegoUser) GetPrivateKey() crypto.PrivateKey { - return u.key -} func CustomCert(w http.ResponseWriter, r *http.Request) { cert, _, err := r.FormFile("cert") if err != nil { @@ -78,18 +57,18 @@ func CustomCert(w http.ResponseWriter, r *http.Request) { RespError(w, RespInternalErr, err) return } + if tlscert, _, err := ParseCert(); err != nil { + return + } else { + dbdata.TLSCert = tlscert + } RespSucess(w, "上传成功") } func GetCertSetting(w http.ResponseWriter, r *http.Request) { - data := &dbdata.SettingDnsProvider{} + data := &dbdata.SettingLetsEncrypt{} if err := dbdata.SettingGet(data); err != nil { RespError(w, RespInternalErr, err) } - data.AliYun.APIKey = Scrypt(data.AliYun.APIKey) - data.AliYun.SecretKey = Scrypt(data.AliYun.SecretKey) - data.TXCloud.SecretID = Scrypt(data.TXCloud.SecretID) - data.TXCloud.SecretKey = Scrypt(data.TXCloud.SecretKey) - data.CfCloud.AuthKey = Scrypt(data.CfCloud.AuthKey) RespSucess(w, data) } func CreatCert(w http.ResponseWriter, r *http.Request) { @@ -103,7 +82,7 @@ func CreatCert(w http.ResponseWriter, r *http.Request) { return } defer r.Body.Close() - config := &dbdata.SettingDnsProvider{} + config := &dbdata.SettingLetsEncrypt{} err = json.Unmarshal(body, config) if err != nil { RespError(w, RespInternalErr, err) @@ -113,8 +92,8 @@ func CreatCert(w http.ResponseWriter, r *http.Request) { RespError(w, RespInternalErr, err) return } - client, err := NewLeGoClient(config) - if err != nil { + client := LeGoClient{} + if err := client.NewClient(config); err != nil { base.Error(err) RespError(w, RespInternalErr, fmt.Sprintf("获取证书失败:%v", err)) return @@ -128,13 +107,13 @@ func CreatCert(w http.ResponseWriter, r *http.Request) { } func ReNewCert() { - certtime, err := GetCerttime() + _, certtime, err := ParseCert() if err != nil { base.Error(err) return } if certtime.AddDate(0, 0, -7).Before(time.Now()) { - config := &dbdata.SettingDnsProvider{} + config := &dbdata.SettingLetsEncrypt{} if err := dbdata.SettingGet(config); err != nil { base.Error(err) return @@ -143,8 +122,8 @@ func ReNewCert() { return } if config.Renew { - client, err := NewLeGoClient(config) - if err != nil { + client := &LeGoClient{} + if err := client.NewClient(config); err != nil { base.Error(err) return } @@ -158,51 +137,38 @@ func ReNewCert() { base.Info(fmt.Sprintf("证书过期时间:%s", certtime.Local().Format("2006-1-2 15:04:05"))) } -func NewLeGoClient(d *dbdata.SettingDnsProvider) (*LeGoClient, error) { - privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +func (c *LeGoClient) NewClient(l *dbdata.SettingLetsEncrypt) error { + c.mutex.Lock() + defer c.mutex.Unlock() + legouser, err := c.GetUserData(l) if err != nil { - return nil, err + return err } - legoUser := LegoUser{ - Email: d.Legomail, - key: privateKey, - } - config := lego.NewConfig(&legoUser) + config := lego.NewConfig(legouser) config.CADirURL = lego.LEDirectoryProduction config.Certificate.KeyType = certcrypto.RSA2048 client, err := lego.NewClient(config) if err != nil { - return nil, err + return err } - if _, err := client.Registration.ResolveAccountByKey(); err != nil { - reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) - if err != nil { - return nil, err - } - legoUser.Registration = reg - } - var Provider challenge.Provider - switch d.Name { - case "aliyun": - if Provider, err = alidns.NewDNSProviderConfig(&alidns.Config{APIKey: d.AliYun.APIKey, SecretKey: d.AliYun.SecretKey, TTL: 600}); err != nil { - return nil, err - } - case "txcloud": - if Provider, err = tencentcloud.NewDNSProviderConfig(&tencentcloud.Config{SecretID: d.TXCloud.SecretID, SecretKey: d.TXCloud.SecretKey, TTL: 600}); err != nil { - return nil, err - } - case "cloudflare": - if Provider, err = cloudflare.NewDNSProviderConfig(&cloudflare.Config{AuthEmail: d.CfCloud.AuthEmail, AuthKey: d.CfCloud.AuthKey, TTL: 600}); err != nil { - return nil, err - } + Provider, err := dbdata.GetDNSProvider(l) + if err != nil { + return err } if err := client.Challenge.SetDNS01Provider(Provider, dns01.AddRecursiveNameservers([]string{"114.114.114.114", "114.114.115.115"})); err != nil { - return nil, err + return err } - return &LeGoClient{ - Client: client, - }, nil + if legouser.Registration == nil { + reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) + if err != nil { + return err + } + legouser.Registration = reg + c.SaveUserData(legouser) + } + c.Client = client + return nil } func (c *LeGoClient) GetCertificate(domain string) error { // 申请证书 @@ -250,6 +216,11 @@ func SaveCertificate(cert *certificate.Resource) error { if err != nil { return err } + if tlscert, _, err := ParseCert(); err != nil { + return err + } else { + dbdata.TLSCert = tlscert + } return nil } @@ -268,24 +239,24 @@ func LoadCertResource(certFile, keyFile string) (*certificate.Resource, error) { }, nil } -func GetCerttime() (*time.Time, error) { +func ParseCert() (*tls.Certificate, *time.Time, error) { cert, err := tls.LoadX509KeyPair(base.Cfg.CertFile, base.Cfg.CertKey) if err != nil { - return nil, err + return nil, nil, err } parseCert, err := x509.ParseCertificate(cert.Certificate[0]) if err != nil { - return nil, err + return nil, nil, err } certtime := parseCert.NotAfter - return &certtime, nil + return &cert, &certtime, nil } -func Scrypt(passwd string) string { - salt := []byte{0xc8, 0x28, 0xf2, 0x58, 0xa7, 0x6a, 0xad, 0x7b} - hashPasswd, err := scrypt.Key([]byte(passwd), salt, 1<<15, 8, 1, 32) - if err != nil { - return err.Error() - } - return base64.StdEncoding.EncodeToString(hashPasswd) -} +// func Scrypt(passwd string) string { +// salt := []byte{0xc8, 0x28, 0xf2, 0x58, 0xa7, 0x6a, 0xad, 0x7b} +// hashPasswd, err := scrypt.Key([]byte(passwd), salt, 1<<15, 8, 1, 32) +// if err != nil { +// return err.Error() +// } +// return base64.StdEncoding.EncodeToString(hashPasswd) +// } diff --git a/server/admin/server.go b/server/admin/server.go index ee4d31d..283d2e9 100644 --- a/server/admin/server.go +++ b/server/admin/server.go @@ -9,6 +9,7 @@ import ( "github.com/arl/statsviz" "github.com/bjdgyc/anylink/base" + "github.com/bjdgyc/anylink/dbdata" "github.com/gorilla/handlers" "github.com/gorilla/mux" ) @@ -99,17 +100,25 @@ func StartAdmin() { for _, s := range cipherSuites { selectedCipherSuites = append(selectedCipherSuites, s.ID) } + + if tlscert, _, err := ParseCert(); err != nil { + base.Error(err) + return + } else { + dbdata.TLSCert = tlscert + } + // 设置tls信息 tlsConfig := &tls.Config{ NextProtos: []string{"http/1.1"}, MinVersion: tls.VersionTLS12, CipherSuites: selectedCipherSuites, GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) { - cert, err := tls.LoadX509KeyPair(base.Cfg.CertFile, base.Cfg.CertKey) - if err != nil { - return nil, err - } - return &cert, nil + // cert, err := tls.LoadX509KeyPair(base.Cfg.CertFile, base.Cfg.CertKey) + // if err != nil { + // return nil, err + // } + return dbdata.TLSCert, nil }, } srv := &http.Server{ diff --git a/server/dbdata/cert.go b/server/dbdata/cert.go new file mode 100755 index 0000000..63f311b --- /dev/null +++ b/server/dbdata/cert.go @@ -0,0 +1,119 @@ +package dbdata + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + + "github.com/go-acme/lego/v4/providers/dns/alidns" + "github.com/go-acme/lego/v4/providers/dns/cloudflare" + "github.com/go-acme/lego/v4/providers/dns/tencentcloud" + "github.com/go-acme/lego/v4/registration" + "github.com/xenolf/lego/challenge" +) + +var TLSCert *tls.Certificate + +type SettingLetsEncrypt struct { + // LegoUser LegoUser + Domain string `json:"domain"` + Legomail string `json:"legomail"` + Name string `json:"name"` + Renew bool `json:"renew"` + DNSProvider +} + +type DNSProvider struct { + AliYun struct { + APIKey string `json:"apiKey"` + SecretKey string `json:"secretKey"` + } `json:"aliyun"` + + TXCloud struct { + SecretID string `json:"secretId"` + SecretKey string `json:"secretKey"` + } `json:"txcloud"` + CfCloud struct { + AuthEmail string `json:"authEmail"` + AuthKey string `json:"authKey"` + } `json:"cfcloud"` +} +type LegoUserData struct { + Email string `json:"email"` + Registration *registration.Resource `json:"registration"` + Key []byte `json:"key"` +} +type LegoUser struct { + Email string + Registration *registration.Resource + Key *ecdsa.PrivateKey +} + +func GetDNSProvider(l *SettingLetsEncrypt) (Provider challenge.Provider, err error) { + switch l.Name { + case "aliyun": + if Provider, err = alidns.NewDNSProviderConfig(&alidns.Config{APIKey: l.DNSProvider.AliYun.APIKey, SecretKey: l.DNSProvider.AliYun.SecretKey, TTL: 600}); err != nil { + return + } + case "txcloud": + if Provider, err = tencentcloud.NewDNSProviderConfig(&tencentcloud.Config{SecretID: l.DNSProvider.TXCloud.SecretID, SecretKey: l.DNSProvider.TXCloud.SecretKey, TTL: 600}); err != nil { + return + } + case "cloudflare": + if Provider, err = cloudflare.NewDNSProviderConfig(&cloudflare.Config{AuthEmail: l.DNSProvider.CfCloud.AuthEmail, AuthKey: l.DNSProvider.CfCloud.AuthKey, TTL: 600}); err != nil { + return + } + } + return +} +func (u *LegoUser) GetEmail() string { + return u.Email +} +func (u LegoUser) GetRegistration() *registration.Resource { + return u.Registration +} +func (u *LegoUser) GetPrivateKey() crypto.PrivateKey { + return u.Key +} + +func (l *LegoUserData) SaveUserData(u *LegoUser) error { + key, err := x509.MarshalECPrivateKey(u.Key) + if err != nil { + return err + } + l.Email = u.Email + l.Registration = u.Registration + l.Key = key + if err := SettingSet(l); err != nil { + return err + } + return nil +} + +func (l *LegoUserData) GetUserData(d *SettingLetsEncrypt) (*LegoUser, error) { + if err := SettingGet(l); err != nil { + return nil, err + } + if l.Email != "" { + key, err := x509.ParseECPrivateKey(l.Key) + if err != nil { + return nil, err + } + return &LegoUser{ + Email: l.Email, + Registration: l.Registration, + Key: key, + }, nil + } + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, err + } + return &LegoUser{ + Email: d.Legomail, + Key: privateKey, + }, nil +} diff --git a/server/dbdata/db.go b/server/dbdata/db.go index 149534d..293e9b8 100644 --- a/server/dbdata/db.go +++ b/server/dbdata/db.go @@ -100,28 +100,35 @@ func addInitData() error { } // SettingDnsProvider - provider := &SettingDnsProvider{ + provider := &SettingLetsEncrypt{ Domain: "vpn.xxx.com", Legomail: "legomail", - Name: "", + Name: "aliyun", Renew: false, - AliYun: struct { - APIKey string `json:"apiKey"` - SecretKey string `json:"secretKey"` - }{APIKey: "", SecretKey: ""}, - TXCloud: struct { - SecretID string `json:"secretId"` - SecretKey string `json:"secretKey"` - }{SecretID: "", SecretKey: ""}, - CfCloud: struct { - AuthEmail string `json:"authEmail"` - AuthKey string `json:"authKey"` - }{AuthEmail: "", AuthKey: ""}, + DNSProvider: DNSProvider{ + AliYun: struct { + APIKey string `json:"apiKey"` + SecretKey string `json:"secretKey"` + }{APIKey: "", SecretKey: ""}, + TXCloud: struct { + SecretID string `json:"secretId"` + SecretKey string `json:"secretKey"` + }{SecretID: "", SecretKey: ""}, + CfCloud: struct { + AuthEmail string `json:"authEmail"` + AuthKey string `json:"authKey"` + }{AuthEmail: "", AuthKey: ""}}, } err = SettingSessAdd(sess, provider) if err != nil { return err } + // LegoUser + legouser := &LegoUserData{} + err = SettingSessAdd(sess, legouser) + if err != nil { + return err + } // SettingOther other := &SettingOther{ LinkAddr: "vpn.xx.com", diff --git a/server/dbdata/setting.go b/server/dbdata/setting.go index 4cba04f..c7e0a36 100644 --- a/server/dbdata/setting.go +++ b/server/dbdata/setting.go @@ -33,26 +33,6 @@ type SettingOther struct { AccountMail string `json:"account_mail"` } -type SettingDnsProvider struct { - Domain string `json:"domain"` - Legomail string `json:"legomail"` - Name string `json:"name"` - Renew bool `json:"renew"` - AliYun struct { - APIKey string `json:"apiKey"` - SecretKey string `json:"secretKey"` - } `json:"aliyun"` - - TXCloud struct { - SecretID string `json:"secretId"` - SecretKey string `json:"secretKey"` - } `json:"txcloud"` - CfCloud struct { - AuthEmail string `json:"authEmail"` - AuthKey string `json:"authKey"` - } `json:"cfcloud"` -} - func StructName(data interface{}) string { ref := reflect.ValueOf(data) s := &ref @@ -69,7 +49,6 @@ func SettingSessAdd(sess *xorm.Session, data interface{}) error { v, _ := json.Marshal(data) s := &Setting{Name: name, Data: v} _, err := sess.InsertOne(s) - return err } diff --git a/server/handler/server.go b/server/handler/server.go index bedd7d5..f6b4e65 100644 --- a/server/handler/server.go +++ b/server/handler/server.go @@ -11,6 +11,7 @@ import ( "time" "github.com/bjdgyc/anylink/base" + "github.com/bjdgyc/anylink/dbdata" "github.com/gorilla/mux" "github.com/pires/go-proxyproto" ) @@ -49,11 +50,11 @@ func startTls() { MinVersion: tls.VersionTLS12, CipherSuites: selectedCipherSuites, GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) { - cert, err := tls.LoadX509KeyPair(base.Cfg.CertFile, base.Cfg.CertKey) - if err != nil { - return nil, err - } - return &cert, nil + // cert, err := tls.LoadX509KeyPair(base.Cfg.CertFile, base.Cfg.CertKey) + // if err != nil { + // return nil, err + // } + return dbdata.TLSCert, nil }, // InsecureSkipVerify: true, }