diff --git a/api/panel/node_test.go b/api/panel/node_test.go index 575833c..206bf43 100644 --- a/api/panel/node_test.go +++ b/api/panel/node_test.go @@ -2,7 +2,7 @@ package panel import ( "github.com/Yuzuki616/V2bX/conf" - "github.com/Yuzuki616/V2bX/node/controller/legoCmd/log" + "log" "testing" ) diff --git a/common/file/file.go b/common/file/file.go new file mode 100644 index 0000000..998002e --- /dev/null +++ b/common/file/file.go @@ -0,0 +1,8 @@ +package file + +import "os" + +func IsExist(path string) bool { + _, err := os.Stat(path) + return err == nil || !os.IsNotExist(err) +} diff --git a/go.mod b/go.mod index 24a019a..c58e68b 100644 --- a/go.mod +++ b/go.mod @@ -8,10 +8,9 @@ require ( github.com/go-resty/resty/v2 v2.7.0 github.com/golang/protobuf v1.5.2 // indirect github.com/juju/ratelimit v1.0.2 - github.com/stretchr/testify v1.8.1 - github.com/urfave/cli v1.22.10 + github.com/stretchr/testify v1.8.1 // indirect github.com/xtls/xray-core v1.7.2 - golang.org/x/net v0.5.0 + golang.org/x/net v0.5.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 ) @@ -47,7 +46,6 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cloudflare/cloudflare-go v0.49.0 // indirect github.com/cpu/goacmedns v0.1.1 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deepmap/oapi-codegen v1.10.1 // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect @@ -115,10 +113,8 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/otp v1.3.0 // indirect - github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2 // indirect github.com/refraction-networking/utls v1.2.0 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sacloud/libsacloud v1.36.2 // indirect github.com/sagernet/sing v0.1.2 // indirect github.com/sagernet/sing-shadowsocks v0.1.0 // indirect diff --git a/go.sum b/go.sum index 74b736d..756498b 100644 --- a/go.sum +++ b/go.sum @@ -104,10 +104,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4= github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -546,8 +543,6 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2 h1:dq90+d51/hQRaHEqRAsQ1rE/pC1GUS4sc2rCbbFsAIY= -github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= github.com/refraction-networking/utls v1.2.0 h1:U5f8wkij2NVinfLuJdFP3gCMwIHs+EzvhxmYdXgiapo= github.com/refraction-networking/utls v1.2.0/go.mod h1:NPq+cVqzH7D1BeOkmOcb5O/8iVewAsiVt2x1/eO0hgQ= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= @@ -559,8 +554,6 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sacloud/libsacloud v1.36.2 h1:aosI7clbQ9IU0Hj+3rpk3SKJop5nLPpLThnWCivPqjI= github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS7IBEjKVSrjg= @@ -661,8 +654,6 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/urfave/cli v1.22.10 h1:p8Fspmz3iTctJstry1PYS3HVdllxnEzTEsgIgtxTrCk= -github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= diff --git a/node/controller/controller.go b/node/controller/controller.go index e43e789..587c08d 100644 --- a/node/controller/controller.go +++ b/node/controller/controller.go @@ -9,7 +9,6 @@ import ( "github.com/Yuzuki616/V2bX/core" "github.com/xtls/xray-core/common/task" "log" - "time" ) type Node struct { @@ -22,8 +21,9 @@ type Node struct { ipRecorder iprecoder.IpRecorder nodeInfoMonitorPeriodic *task.Periodic userReportPeriodic *task.Periodic + renewCertPeriodic *task.Periodic + dynamicSpeedLimitPeriodic *task.Periodic onlineIpReportPeriodic *task.Periodic - DynamicSpeedLimitPeriodic *task.Periodic *conf.ControllerConfig } @@ -73,62 +73,7 @@ func (c *Node) Start() error { log.Printf("Update rule filed: %s", err) } } - // fetch node info task - c.nodeInfoMonitorPeriodic = &task.Periodic{ - Interval: time.Duration(c.nodeInfo.BaseConfig.PullInterval.(int)) * time.Second, - Execute: c.nodeInfoMonitor, - } - // fetch user list task - c.userReportPeriodic = &task.Periodic{ - Interval: time.Duration(c.nodeInfo.BaseConfig.PushInterval.(int)) * time.Second, - Execute: c.reportUserTraffic, - } - log.Printf("[%s: %d] Start monitor node status", c.nodeInfo.NodeType, c.nodeInfo.NodeId) - // delay to start nodeInfoMonitor - go func() { - time.Sleep(time.Duration(c.nodeInfo.BaseConfig.PullInterval.(int)) * time.Second) - _ = c.nodeInfoMonitorPeriodic.Start() - }() - - log.Printf("[%s: %d] Start report node status", c.nodeInfo.NodeType, c.nodeInfo.NodeId) - // delay to start userReport - go func() { - time.Sleep(time.Duration(c.nodeInfo.BaseConfig.PushInterval.(int)) * time.Second) - _ = c.userReportPeriodic.Start() - }() - if c.EnableDynamicSpeedLimit { - // Check dynamic speed limit task - c.DynamicSpeedLimitPeriodic = &task.Periodic{ - Interval: time.Duration(c.DynamicSpeedLimitConfig.Periodic) * time.Second, - Execute: c.dynamicSpeedLimit, - } - go func() { - time.Sleep(time.Duration(c.DynamicSpeedLimitConfig.Periodic) * time.Second) - _ = c.DynamicSpeedLimitPeriodic.Start() - }() - log.Printf("[%s: %d] Start dynamic speed limit", c.nodeInfo.NodeType, c.nodeInfo.NodeId) - } - if c.EnableIpRecorder { - switch c.IpRecorderConfig.Type { - case "Recorder": - c.ipRecorder = iprecoder.NewRecorder(c.IpRecorderConfig.RecorderConfig) - case "Redis": - c.ipRecorder = iprecoder.NewRedis(c.IpRecorderConfig.RedisConfig) - default: - log.Printf("recorder type: %s is not vail, disable recorder", c.IpRecorderConfig.Type) - return nil - } - // report and fetch online ip list task - c.onlineIpReportPeriodic = &task.Periodic{ - Interval: time.Duration(c.IpRecorderConfig.Periodic) * time.Second, - Execute: c.reportOnlineIp, - } - go func() { - time.Sleep(time.Duration(c.IpRecorderConfig.Periodic) * time.Second) - _ = c.onlineIpReportPeriodic.Start() - }() - log.Printf("[%s: %d] Start report online ip", c.nodeInfo.NodeType, c.nodeInfo.NodeId) - } + c.initTask() return nil } @@ -146,18 +91,24 @@ func (c *Node) Close() error { log.Panicf("user report periodic close failed: %s", err) } } + if c.renewCertPeriodic != nil { + err := c.renewCertPeriodic.Close() + if err != nil { + log.Panicf("renew cert periodic close failed: %s", err) + } + } + if c.dynamicSpeedLimitPeriodic != nil { + err := c.dynamicSpeedLimitPeriodic.Close() + if err != nil { + log.Panicf("dynamic speed limit periodic close failed: %s", err) + } + } if c.onlineIpReportPeriodic != nil { err := c.onlineIpReportPeriodic.Close() if err != nil { log.Panicf("online ip report periodic close failed: %s", err) } } - if c.DynamicSpeedLimitPeriodic != nil { - err := c.DynamicSpeedLimitPeriodic.Close() - if err != nil { - log.Panicf("dynamic speed limit periodic close failed: %s", err) - } - } return nil } diff --git a/node/controller/inbound.go b/node/controller/inbound.go index a3f128a..17bb2b6 100644 --- a/node/controller/inbound.go +++ b/node/controller/inbound.go @@ -6,8 +6,9 @@ import ( "encoding/hex" "fmt" "github.com/Yuzuki616/V2bX/api/panel" + "github.com/Yuzuki616/V2bX/common/file" "github.com/Yuzuki616/V2bX/conf" - "github.com/Yuzuki616/V2bX/node/controller/legoCmd" + "github.com/Yuzuki616/V2bX/node/controller/lego" "github.com/goccy/go-json" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/core" @@ -220,31 +221,25 @@ func buildShadowsocks(config *conf.ControllerConfig, nodeInfo *panel.NodeInfo, i } func getCertFile(certConfig *conf.CertConfig) (certFile string, keyFile string, err error) { - if certConfig.CertMode == "file" { - if certConfig.CertFile == "" || certConfig.KeyFile == "" { - return "", "", fmt.Errorf("cert file path or key file path not exist") + if certConfig.CertFile == "" || certConfig.KeyFile == "" { + return "", "", fmt.Errorf("cert file path or key file path not exist") + } + switch certConfig.CertMode { + case "file": + return certConfig.CertFile, certConfig.KeyFile, nil + case "dns", "http": + if file.IsExist(certConfig.CertFile) && file.IsExist(certConfig.KeyFile) { + return certConfig.CertFile, certConfig.KeyFile, nil + } + l, err := lego.New(certConfig) + if err != nil { + return "", "", fmt.Errorf("create lego object error: %s", err) + } + err = l.CreateCert() + if err != nil { + return "", "", fmt.Errorf("create cert error: %s", err) } return certConfig.CertFile, certConfig.KeyFile, nil - } else if certConfig.CertMode == "dns" { - lego, err := legoCmd.New() - if err != nil { - return "", "", err - } - certPath, keyPath, err := lego.DNSCert(certConfig.CertDomain, certConfig.Email, certConfig.Provider, certConfig.DNSEnv) - if err != nil { - return "", "", err - } - return certPath, keyPath, err - } else if certConfig.CertMode == "http" { - lego, err := legoCmd.New() - if err != nil { - return "", "", err - } - certPath, keyPath, err := lego.HTTPCert(certConfig.CertDomain, certConfig.Email) - if err != nil { - return "", "", err - } - return certPath, keyPath, err } return "", "", fmt.Errorf("unsupported certmode: %s", certConfig.CertMode) } diff --git a/node/controller/lego/cert.go b/node/controller/lego/cert.go new file mode 100644 index 0000000..5774677 --- /dev/null +++ b/node/controller/lego/cert.go @@ -0,0 +1,110 @@ +package lego + +import ( + "fmt" + "github.com/Yuzuki616/V2bX/common/file" + "github.com/go-acme/lego/v4/certcrypto" + "github.com/go-acme/lego/v4/certificate" + "github.com/go-acme/lego/v4/challenge/http01" + "github.com/go-acme/lego/v4/providers/dns" + "os" + "path" + "strings" + "time" +) + +func (l *Lego) SetProvider() error { + switch l.config.CertMode { + case "http": + err := l.client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "5002")) + if err != nil { + return err + } + case "dns": + for k, v := range l.config.DNSEnv { + os.Setenv(k, v) + } + p, err := dns.NewDNSChallengeProviderByName(l.config.Provider) + if err != nil { + return fmt.Errorf("create dns challenge provider error: %s", err) + } + err = l.client.Challenge.SetDNS01Provider(p) + if err != nil { + return fmt.Errorf("set dns provider error: %s", err) + } + } + return nil +} + +func (l *Lego) CreateCert() (err error) { + request := certificate.ObtainRequest{ + Domains: []string{l.config.CertDomain}, + } + certificates, err := l.client.Certificate.Obtain(request) + if err != nil { + return fmt.Errorf("obtain certificate error: %s", err) + } + err = l.writeCert(certificates) + return nil +} + +func (l *Lego) RenewCert() error { + file, err := os.ReadFile(l.config.CertFile) + if err != nil { + return fmt.Errorf("read cert file error: %s", err) + } + if e, err := l.CheckCert(file); !e { + return nil + } else if err != nil { + return fmt.Errorf("check cert error: %s", err) + } + res, err := l.client.Certificate.Renew(certificate.Resource{ + Domain: l.config.CertDomain, + Certificate: file, + }, false, false, "") + if err != nil { + return err + } + err = l.writeCert(res) + return nil +} + +func (l *Lego) CheckCert(file []byte) (bool, error) { + cert, err := certcrypto.ParsePEMCertificate(file) + if err != nil { + return false, err + } + notAfter := int(time.Until(cert.NotAfter).Hours() / 24.0) + if notAfter > 30 { + return false, nil + } + return true, nil +} +func (l *Lego) parseParmas(path string) string { + r := strings.NewReplacer("{domain}", l.config.CertDomain, + "{email}", l.config.Email) + return r.Replace(path) +} +func (l *Lego) writeCert(certificates *certificate.Resource) error { + if !file.IsExist(path.Dir(l.config.CertFile)) { + err := os.MkdirAll(path.Dir(l.config.CertFile), 0755) + if err != nil { + return fmt.Errorf("create dir error: %s", err) + } + } + err := os.WriteFile(l.parseParmas(l.config.CertFile), certificates.Certificate, 0644) + if err != nil { + return err + } + if !file.IsExist(path.Dir(l.config.KeyFile)) { + err := os.MkdirAll(path.Dir(l.config.CertFile), 0755) + if err != nil { + return fmt.Errorf("create dir error: %s", err) + } + } + err = os.WriteFile(l.parseParmas(l.config.KeyFile), certificates.PrivateKey, 0644) + if err != nil { + return err + } + return nil +} diff --git a/node/controller/lego/lego.go b/node/controller/lego/lego.go new file mode 100644 index 0000000..374acd4 --- /dev/null +++ b/node/controller/lego/lego.go @@ -0,0 +1,51 @@ +package lego + +import ( + "fmt" + "github.com/Yuzuki616/V2bX/common/file" + "github.com/Yuzuki616/V2bX/conf" + "github.com/go-acme/lego/v4/certcrypto" + "github.com/go-acme/lego/v4/lego" + "os" + "path" +) + +type Lego struct { + client *lego.Client + config *conf.CertConfig +} + +func New(config *conf.CertConfig) (*Lego, error) { + user, err := NewUser(path.Join("/etc/V2bX/cert/user/", + fmt.Sprintf("user-%s.json", config.Email)), + config.Email) + if err != nil { + return nil, fmt.Errorf("create user error: %s", err) + } + c := lego.NewConfig(user) + //c.CADirURL = "http://192.168.99.100:4000/directory" + c.Certificate.KeyType = certcrypto.RSA2048 + client, err := lego.NewClient(c) + if err != nil { + return nil, err + } + l := Lego{ + client: client, + config: config, + } + err = l.SetProvider() + if err != nil { + return nil, fmt.Errorf("set provider error: %s", err) + } + return &l, nil +} + +func checkPath(p string) error { + if !file.IsExist(path.Dir(p)) { + err := os.MkdirAll(path.Dir(p), 0755) + if err != nil { + return fmt.Errorf("create dir error: %s", err) + } + } + return nil +} diff --git a/node/controller/lego/lego_test.go b/node/controller/lego/lego_test.go new file mode 100644 index 0000000..3e44ec8 --- /dev/null +++ b/node/controller/lego/lego_test.go @@ -0,0 +1,40 @@ +package lego + +import ( + "github.com/Yuzuki616/V2bX/conf" + "log" + "os" + "testing" +) + +var l *Lego + +func init() { + var err error + l, err = New(&conf.CertConfig{ + CertMode: "dns", + Email: "test@test.com", + CertDomain: "test.test.com", + Provider: "cloudflare", + DNSEnv: map[string]string{ + "CF_DNS_API_TOKEN": "123", + }, + CertFile: "./cert/1.pem", + KeyFile: "./cert/1.key", + }) + if err != nil { + log.Println(err) + os.Exit(1) + } +} + +func TestLego_CreateCertByDns(t *testing.T) { + err := l.CreateCert() + if err != nil { + t.Error(err) + } +} + +func TestLego_RenewCert(t *testing.T) { + log.Println(l.RenewCert()) +} diff --git a/node/controller/lego/user.go b/node/controller/lego/user.go new file mode 100644 index 0000000..dde78d3 --- /dev/null +++ b/node/controller/lego/user.go @@ -0,0 +1,129 @@ +package lego + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "encoding/pem" + "fmt" + "github.com/Yuzuki616/V2bX/common/file" + "github.com/go-acme/lego/v4/lego" + "github.com/go-acme/lego/v4/registration" + "github.com/goccy/go-json" + "os" +) + +type User struct { + Email string `json:"Email"` + Registration *registration.Resource `json:"Registration"` + key crypto.PrivateKey + KeyEncoded string `json:"Key"` +} + +func (u *User) GetEmail() string { + return u.Email +} +func (u *User) GetRegistration() *registration.Resource { + return u.Registration +} +func (u *User) GetPrivateKey() crypto.PrivateKey { + return u.key +} + +func NewUser(path string, email string) (*User, error) { + var user User + if file.IsExist(path) { + err := user.Load(path) + if err != nil { + return nil, err + } + if user.Email != email { + user.Registration = nil + user.Email = email + err := registerUser(&user, path) + if err != nil { + return nil, err + } + } + } else { + user.Email = email + err := registerUser(&user, path) + if err != nil { + return nil, err + } + } + return &user, nil +} + +func registerUser(user *User, path string) error { + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return fmt.Errorf("generate key error: %s", err) + } + user.key = privateKey + c := lego.NewConfig(user) + client, err := lego.NewClient(c) + if err != nil { + return fmt.Errorf("create lego client error: %s", err) + } + reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) + if err != nil { + return err + } + user.Registration = reg + err = user.Save(path) + if err != nil { + return fmt.Errorf("save user error: %s", err) + } + return nil +} + +func EncodePrivate(privKey *ecdsa.PrivateKey) (string, error) { + encoded, err := x509.MarshalECPrivateKey(privKey) + if err != nil { + return "", err + } + pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: encoded}) + return string(pemEncoded), nil +} +func (u *User) Save(path string) error { + err := checkPath(path) + if err != nil { + return fmt.Errorf("check path error: %s", err) + } + u.KeyEncoded, _ = EncodePrivate(u.key.(*ecdsa.PrivateKey)) + f, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) + if err != nil { + return err + } + err = json.NewEncoder(f).Encode(u) + if err != nil { + return fmt.Errorf("marshal json error: %s", err) + } + u.KeyEncoded = "" + return nil +} + +func (u *User) DecodePrivate(pemEncodedPriv string) (*ecdsa.PrivateKey, error) { + blockPriv, _ := pem.Decode([]byte(pemEncodedPriv)) + x509EncodedPriv := blockPriv.Bytes + privateKey, err := x509.ParseECPrivateKey(x509EncodedPriv) + return privateKey, err +} +func (u *User) Load(path string) error { + f, err := os.Open(path) + if err != nil { + return fmt.Errorf("open file error: %s", err) + } + err = json.NewDecoder(f).Decode(u) + if err != nil { + return fmt.Errorf("unmarshal json error: %s", err) + } + u.key, err = u.DecodePrivate(u.KeyEncoded) + if err != nil { + return fmt.Errorf("decode private key error: %s", err) + } + return nil +} diff --git a/node/controller/legoCmd/cmd/account.go b/node/controller/legoCmd/cmd/account.go deleted file mode 100644 index 73247c9..0000000 --- a/node/controller/legoCmd/cmd/account.go +++ /dev/null @@ -1,33 +0,0 @@ -package cmd - -import ( - "crypto" - - "github.com/go-acme/lego/v4/registration" -) - -// Account represents a users local saved credentials. -type Account struct { - Email string `json:"email"` - Registration *registration.Resource `json:"registration"` - key crypto.PrivateKey -} - -/** Implementation of the registration.User interface **/ - -// GetEmail returns the email address for the account. -func (a *Account) GetEmail() string { - return a.Email -} - -// GetPrivateKey returns the private RSA account key. -func (a *Account) GetPrivateKey() crypto.PrivateKey { - return a.key -} - -// GetRegistration returns the server registration. -func (a *Account) GetRegistration() *registration.Resource { - return a.Registration -} - -/** End **/ diff --git a/node/controller/legoCmd/cmd/accounts_storage.go b/node/controller/legoCmd/cmd/accounts_storage.go deleted file mode 100644 index 3db8a84..0000000 --- a/node/controller/legoCmd/cmd/accounts_storage.go +++ /dev/null @@ -1,242 +0,0 @@ -package cmd - -import ( - "crypto" - "crypto/x509" - "encoding/pem" - "errors" - "fmt" - "github.com/Yuzuki616/V2bX/node/controller/legoCmd/log" - "github.com/goccy/go-json" - "io/ioutil" - "net/url" - "os" - "path/filepath" - "strings" - - "github.com/go-acme/lego/v4/certcrypto" - "github.com/go-acme/lego/v4/lego" - "github.com/go-acme/lego/v4/registration" - "github.com/urfave/cli" -) - -const ( - baseAccountsRootFolderName = "accounts" - baseKeysFolderName = "keys" - accountFileName = "account.json" -) - -// AccountsStorage A storage for account data. -// -// rootPath: -// -// ./.lego/accounts/ -// │ └── root accounts directory -// └── "path" option -// -// rootUserPath: -// -// ./.lego/accounts/localhost_14000/hubert@hubert.com/ -// │ │ │ └── userID ("email" option) -// │ │ └── CA server ("server" option) -// │ └── root accounts directory -// └── "path" option -// -// keysPath: -// -// ./.lego/accounts/localhost_14000/hubert@hubert.com/keys/ -// │ │ │ │ └── root keys directory -// │ │ │ └── userID ("email" option) -// │ │ └── CA server ("server" option) -// │ └── root accounts directory -// └── "path" option -// -// accountFilePath: -// -// ./.lego/accounts/localhost_14000/hubert@hubert.com/account.json -// │ │ │ │ └── account file -// │ │ │ └── userID ("email" option) -// │ │ └── CA server ("server" option) -// │ └── root accounts directory -// └── "path" option -type AccountsStorage struct { - userID string - rootPath string - rootUserPath string - keysPath string - accountFilePath string - ctx *cli.Context -} - -// NewAccountsStorage Creates a new AccountsStorage. -func NewAccountsStorage(ctx *cli.Context) *AccountsStorage { - // TODO: move to account struct? Currently MUST pass email. - email := getEmail(ctx) - - serverURL, err := url.Parse(ctx.GlobalString("server")) - if err != nil { - log.Panic(err) - } - - rootPath := filepath.Join(ctx.GlobalString("path"), baseAccountsRootFolderName) - serverPath := strings.NewReplacer(":", "_", "/", string(os.PathSeparator)).Replace(serverURL.Host) - accountsPath := filepath.Join(rootPath, serverPath) - rootUserPath := filepath.Join(accountsPath, email) - - return &AccountsStorage{ - userID: email, - rootPath: rootPath, - rootUserPath: rootUserPath, - keysPath: filepath.Join(rootUserPath, baseKeysFolderName), - accountFilePath: filepath.Join(rootUserPath, accountFileName), - ctx: ctx, - } -} - -func (s *AccountsStorage) ExistsAccountFilePath() bool { - accountFile := filepath.Join(s.rootUserPath, accountFileName) - if _, err := os.Stat(accountFile); os.IsNotExist(err) { - return false - } else if err != nil { - log.Panic(err) - } - return true -} - -func (s *AccountsStorage) GetRootPath() string { - return s.rootPath -} - -func (s *AccountsStorage) GetRootUserPath() string { - return s.rootUserPath -} - -func (s *AccountsStorage) GetUserID() string { - return s.userID -} - -func (s *AccountsStorage) Save(account *Account) error { - jsonBytes, err := json.MarshalIndent(account, "", "\t") - if err != nil { - return err - } - - return ioutil.WriteFile(s.accountFilePath, jsonBytes, filePerm) -} - -func (s *AccountsStorage) LoadAccount(privateKey crypto.PrivateKey) *Account { - fileBytes, err := ioutil.ReadFile(s.accountFilePath) - if err != nil { - log.Panicf("Could not load file for account %s: %v", s.userID, err) - } - - var account Account - err = json.Unmarshal(fileBytes, &account) - if err != nil { - log.Panicf("Could not parse file for account %s: %v", s.userID, err) - } - - account.key = privateKey - - if account.Registration == nil || account.Registration.Body.Status == "" { - reg, err := tryRecoverRegistration(s.ctx, privateKey) - if err != nil { - log.Panicf("Could not load account for %s. Registration is nil: %#v", s.userID, err) - } - - account.Registration = reg - err = s.Save(&account) - if err != nil { - log.Panicf("Could not save account for %s. Registration is nil: %#v", s.userID, err) - } - } - - return &account -} - -func (s *AccountsStorage) GetPrivateKey(keyType certcrypto.KeyType) crypto.PrivateKey { - accKeyPath := filepath.Join(s.keysPath, s.userID+".key") - - if _, err := os.Stat(accKeyPath); os.IsNotExist(err) { - log.Printf("No key found for account %s. Generating a %s key.", s.userID, keyType) - s.createKeysFolder() - - privateKey, err := generatePrivateKey(accKeyPath, keyType) - if err != nil { - log.Panicf("Could not generate RSA private account key for account %s: %v", s.userID, err) - } - - log.Printf("Saved key to %s", accKeyPath) - return privateKey - } - - privateKey, err := loadPrivateKey(accKeyPath) - if err != nil { - log.Panicf("Could not load RSA private key from file %s: %v", accKeyPath, err) - } - - return privateKey -} - -func (s *AccountsStorage) createKeysFolder() { - if err := createNonExistingFolder(s.keysPath); err != nil { - log.Panicf("Could not check/create directory for account %s: %v", s.userID, err) - } -} - -func generatePrivateKey(file string, keyType certcrypto.KeyType) (crypto.PrivateKey, error) { - privateKey, err := certcrypto.GeneratePrivateKey(keyType) - if err != nil { - return nil, err - } - - certOut, err := os.Create(file) - if err != nil { - return nil, err - } - defer certOut.Close() - - pemKey := certcrypto.PEMBlock(privateKey) - err = pem.Encode(certOut, pemKey) - if err != nil { - return nil, err - } - - return privateKey, nil -} - -func loadPrivateKey(file string) (crypto.PrivateKey, error) { - keyBytes, err := ioutil.ReadFile(file) - if err != nil { - return nil, err - } - - keyBlock, _ := pem.Decode(keyBytes) - - switch keyBlock.Type { - case "RSA PRIVATE KEY": - return x509.ParsePKCS1PrivateKey(keyBlock.Bytes) - case "EC PRIVATE KEY": - return x509.ParseECPrivateKey(keyBlock.Bytes) - } - - return nil, errors.New("unknown private key type") -} - -func tryRecoverRegistration(ctx *cli.Context, privateKey crypto.PrivateKey) (*registration.Resource, error) { - // couldn't load account but got a key. Try to look the account up. - config := lego.NewConfig(&Account{key: privateKey}) - config.CADirURL = ctx.GlobalString("server") - config.UserAgent = fmt.Sprintf("lego-cli/%s", ctx.App.Version) - - client, err := lego.NewClient(config) - if err != nil { - return nil, err - } - - reg, err := client.Registration.ResolveAccountByKey() - if err != nil { - return nil, err - } - return reg, nil -} diff --git a/node/controller/legoCmd/cmd/certs_storage.go b/node/controller/legoCmd/cmd/certs_storage.go deleted file mode 100644 index 42fd2a6..0000000 --- a/node/controller/legoCmd/cmd/certs_storage.go +++ /dev/null @@ -1,204 +0,0 @@ -package cmd - -import ( - "bytes" - "crypto/x509" - "github.com/Yuzuki616/V2bX/node/controller/legoCmd/log" - "github.com/goccy/go-json" - "io/ioutil" - "os" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/go-acme/lego/v4/certcrypto" - "github.com/go-acme/lego/v4/certificate" - "github.com/urfave/cli" - "golang.org/x/net/idna" -) - -const ( - baseCertificatesFolderName = "certificates" - baseArchivesFolderName = "archives" -) - -// CertificatesStorage a certificates storage. -// -// rootPath: -// -// ./.lego/certificates/ -// │ └── root certificates directory -// └── "path" option -// -// archivePath: -// -// ./.lego/archives/ -// │ └── archived certificates directory -// └── "path" option -type CertificatesStorage struct { - rootPath string - archivePath string - pem bool - filename string // Deprecated -} - -// NewCertificatesStorage create a new certificates storage. -func NewCertificatesStorage(ctx *cli.Context) *CertificatesStorage { - return &CertificatesStorage{ - rootPath: filepath.Join(ctx.GlobalString("path"), baseCertificatesFolderName), - archivePath: filepath.Join(ctx.GlobalString("path"), baseArchivesFolderName), - pem: ctx.GlobalBool("pem"), - filename: ctx.GlobalString("filename"), - } -} - -func (s *CertificatesStorage) CreateRootFolder() { - err := createNonExistingFolder(s.rootPath) - if err != nil { - log.Panicf("Could not check/create path: %v", err) - } -} - -func (s *CertificatesStorage) CreateArchiveFolder() { - err := createNonExistingFolder(s.archivePath) - if err != nil { - log.Panicf("Could not check/create path: %v", err) - } -} - -func (s *CertificatesStorage) GetRootPath() string { - return s.rootPath -} - -func (s *CertificatesStorage) SaveResource(certRes *certificate.Resource) { - domain := certRes.Domain - - // We store the certificate, private key and metadata in different files - // as web servers would not be able to work with a combined file. - err := s.WriteFile(domain, ".crt", certRes.Certificate) - if err != nil { - log.Panicf("Unable to save Certificate for domain %s\n\t%v", domain, err) - } - - if certRes.IssuerCertificate != nil { - err = s.WriteFile(domain, ".issuer.crt", certRes.IssuerCertificate) - if err != nil { - log.Panicf("Unable to save IssuerCertificate for domain %s\n\t%v", domain, err) - } - } - - if certRes.PrivateKey != nil { - // if we were given a CSR, we don't know the private key - err = s.WriteFile(domain, ".key", certRes.PrivateKey) - if err != nil { - log.Panicf("Unable to save PrivateKey for domain %s\n\t%v", domain, err) - } - - if s.pem { - err = s.WriteFile(domain, ".pem", bytes.Join([][]byte{certRes.Certificate, certRes.PrivateKey}, nil)) - if err != nil { - log.Panicf("Unable to save Certificate and PrivateKey in .pem for domain %s\n\t%v", domain, err) - } - } - } else if s.pem { - // we don't have the private key; can't write the .pem file - log.Panicf("Unable to save pem without private key for domain %s\n\t%v; are you using a CSR?", domain, err) - } - - jsonBytes, err := json.MarshalIndent(certRes, "", "\t") - if err != nil { - log.Panicf("Unable to marshal CertResource for domain %s\n\t%v", domain, err) - } - - err = s.WriteFile(domain, ".json", jsonBytes) - if err != nil { - log.Panicf("Unable to save CertResource for domain %s\n\t%v", domain, err) - } -} - -func (s *CertificatesStorage) ReadResource(domain string) certificate.Resource { - raw, err := s.ReadFile(domain, ".json") - if err != nil { - log.Panicf("Error while loading the meta data for domain %s\n\t%v", domain, err) - } - - var resource certificate.Resource - if err = json.Unmarshal(raw, &resource); err != nil { - log.Panicf("Error while marshaling the meta data for domain %s\n\t%v", domain, err) - } - - return resource -} - -func (s *CertificatesStorage) ExistsFile(domain, extension string) bool { - filePath := s.GetFileName(domain, extension) - - if _, err := os.Stat(filePath); os.IsNotExist(err) { - return false - } else if err != nil { - log.Panic(err) - } - return true -} - -func (s *CertificatesStorage) ReadFile(domain, extension string) ([]byte, error) { - return ioutil.ReadFile(s.GetFileName(domain, extension)) -} - -func (s *CertificatesStorage) GetFileName(domain, extension string) string { - filename := sanitizedDomain(domain) + extension - return filepath.Join(s.rootPath, filename) -} - -func (s *CertificatesStorage) ReadCertificate(domain, extension string) ([]*x509.Certificate, error) { - content, err := s.ReadFile(domain, extension) - if err != nil { - return nil, err - } - - // The input may be a bundle or a single certificate. - return certcrypto.ParsePEMBundle(content) -} - -func (s *CertificatesStorage) WriteFile(domain, extension string, data []byte) error { - var baseFileName string - if s.filename != "" { - baseFileName = s.filename - } else { - baseFileName = sanitizedDomain(domain) - } - - filePath := filepath.Join(s.rootPath, baseFileName+extension) - - return ioutil.WriteFile(filePath, data, filePerm) -} - -func (s *CertificatesStorage) MoveToArchive(domain string) error { - matches, err := filepath.Glob(filepath.Join(s.rootPath, sanitizedDomain(domain)+".*")) - if err != nil { - return err - } - - for _, oldFile := range matches { - date := strconv.FormatInt(time.Now().Unix(), 10) - filename := date + "." + filepath.Base(oldFile) - newFile := filepath.Join(s.archivePath, filename) - - err = os.Rename(oldFile, newFile) - if err != nil { - return err - } - } - - return nil -} - -// sanitizedDomain Make sure no funny chars are in the cert names (like wildcards ;)). -func sanitizedDomain(domain string) string { - safe, err := idna.ToASCII(strings.ReplaceAll(domain, "*", "_")) - if err != nil { - log.Panic(err) - } - return safe -} diff --git a/node/controller/legoCmd/cmd/cmd.go b/node/controller/legoCmd/cmd/cmd.go deleted file mode 100644 index 05b135f..0000000 --- a/node/controller/legoCmd/cmd/cmd.go +++ /dev/null @@ -1,14 +0,0 @@ -package cmd - -import "github.com/urfave/cli" - -// CreateCommands Creates all CLI commands. -func CreateCommands() []cli.Command { - return []cli.Command{ - createRun(), - createRevoke(), - createRenew(), - createDNSHelp(), - createList(), - } -} diff --git a/node/controller/legoCmd/cmd/cmd_before.go b/node/controller/legoCmd/cmd/cmd_before.go deleted file mode 100644 index f5361a9..0000000 --- a/node/controller/legoCmd/cmd/cmd_before.go +++ /dev/null @@ -1,23 +0,0 @@ -package cmd - -import ( - "github.com/Yuzuki616/V2bX/node/controller/legoCmd/log" - "github.com/urfave/cli" -) - -func Before(ctx *cli.Context) error { - if ctx.GlobalString("path") == "" { - log.Panic("Could not determine current working directory. Please pass --path.") - } - - err := createNonExistingFolder(ctx.GlobalString("path")) - if err != nil { - log.Panicf("Could not check/create path: %v", err) - } - - if ctx.GlobalString("server") == "" { - log.Panic("Could not determine current working server. Please pass --server.") - } - - return nil -} diff --git a/node/controller/legoCmd/cmd/cmd_dnshelp.go b/node/controller/legoCmd/cmd/cmd_dnshelp.go deleted file mode 100644 index 3359211..0000000 --- a/node/controller/legoCmd/cmd/cmd_dnshelp.go +++ /dev/null @@ -1,73 +0,0 @@ -package cmd - -import ( - "fmt" - "io" - "os" - "strings" - "text/tabwriter" - - "github.com/urfave/cli" -) - -func createDNSHelp() cli.Command { - return cli.Command{ - Name: "dnshelp", - Usage: "Shows additional help for the '--dns' global option", - Action: dnsHelp, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "code, c", - Usage: fmt.Sprintf("DNS code: %s", allDNSCodes()), - }, - }, - } -} - -func dnsHelp(ctx *cli.Context) error { - code := ctx.String("code") - if code == "" { - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - ew := &errWriter{w: w} - - ew.writeln(`Credentials for DNS providers must be passed through environment variables.`) - ew.writeln() - ew.writeln(`To display the documentation for a DNS providers:`) - ew.writeln() - ew.writeln("\t$ lego dnshelp -c code") - ew.writeln() - ew.writeln("All DNS codes:") - ew.writef("\t%s\n", allDNSCodes()) - ew.writeln() - ew.writeln("More information: https://go-acme.github.io/lego/dns") - - if ew.err != nil { - return ew.err - } - - return w.Flush() - } - - return displayDNSHelp(strings.ToLower(code)) -} - -type errWriter struct { - w io.Writer - err error -} - -func (ew *errWriter) writeln(a ...interface{}) { - if ew.err != nil { - return - } - - _, ew.err = fmt.Fprintln(ew.w, a...) -} - -func (ew *errWriter) writef(format string, a ...interface{}) { - if ew.err != nil { - return - } - - _, ew.err = fmt.Fprintf(ew.w, format, a...) -} diff --git a/node/controller/legoCmd/cmd/cmd_list.go b/node/controller/legoCmd/cmd/cmd_list.go deleted file mode 100644 index 7e13f0c..0000000 --- a/node/controller/legoCmd/cmd/cmd_list.go +++ /dev/null @@ -1,136 +0,0 @@ -package cmd - -import ( - "fmt" - "github.com/goccy/go-json" - "io/ioutil" - "net/url" - "path/filepath" - "strings" - - "github.com/go-acme/lego/v4/certcrypto" - "github.com/urfave/cli" -) - -func createList() cli.Command { - return cli.Command{ - Name: "list", - Usage: "Display certificates and accounts information.", - Action: list, - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "accounts, a", - Usage: "Display accounts.", - }, - cli.BoolFlag{ - Name: "names, n", - Usage: "Display certificate common names only.", - }, - }, - } -} - -func list(ctx *cli.Context) error { - if ctx.Bool("accounts") && !ctx.Bool("names") { - if err := listAccount(ctx); err != nil { - return err - } - } - - return listCertificates(ctx) -} - -func listCertificates(ctx *cli.Context) error { - certsStorage := NewCertificatesStorage(ctx) - - matches, err := filepath.Glob(filepath.Join(certsStorage.GetRootPath(), "*.crt")) - if err != nil { - return err - } - - names := ctx.Bool("names") - - if len(matches) == 0 { - if !names { - fmt.Println("No certificates found.") - } - return nil - } - - if !names { - fmt.Println("Found the following certs:") - } - - for _, filename := range matches { - if strings.HasSuffix(filename, ".issuer.crt") { - continue - } - - data, err := ioutil.ReadFile(filename) - if err != nil { - return err - } - - pCert, err := certcrypto.ParsePEMCertificate(data) - if err != nil { - return err - } - - if names { - fmt.Println(pCert.Subject.CommonName) - } else { - fmt.Println(" Certificate Name:", pCert.Subject.CommonName) - fmt.Println(" Domains:", strings.Join(pCert.DNSNames, ", ")) - fmt.Println(" Expiry Date:", pCert.NotAfter) - fmt.Println(" Certificate Path:", filename) - fmt.Println() - } - } - - return nil -} - -func listAccount(ctx *cli.Context) error { - // fake email, needed by NewAccountsStorage - if err := ctx.GlobalSet("email", "unknown"); err != nil { - return err - } - - accountsStorage := NewAccountsStorage(ctx) - - matches, err := filepath.Glob(filepath.Join(accountsStorage.GetRootPath(), "*", "*", "*.json")) - if err != nil { - return err - } - - if len(matches) == 0 { - fmt.Println("No accounts found.") - return nil - } - - fmt.Println("Found the following accounts:") - for _, filename := range matches { - data, err := ioutil.ReadFile(filename) - if err != nil { - return err - } - - var account Account - err = json.Unmarshal(data, &account) - if err != nil { - return err - } - - uri, err := url.Parse(account.Registration.URI) - if err != nil { - return err - } - - fmt.Println(" Email:", account.Email) - fmt.Println(" Server:", uri.Host) - fmt.Println(" Path:", filepath.Dir(filename)) - fmt.Println() - } - - return nil -} diff --git a/node/controller/legoCmd/cmd/cmd_renew.go b/node/controller/legoCmd/cmd/cmd_renew.go deleted file mode 100644 index bc5a131..0000000 --- a/node/controller/legoCmd/cmd/cmd_renew.go +++ /dev/null @@ -1,225 +0,0 @@ -package cmd - -import ( - "crypto" - "crypto/x509" - "github.com/Yuzuki616/V2bX/node/controller/legoCmd/log" - "time" - - "github.com/go-acme/lego/v4/certcrypto" - "github.com/go-acme/lego/v4/certificate" - "github.com/go-acme/lego/v4/lego" - "github.com/urfave/cli" -) - -const ( - renewEnvAccountEmail = "LEGO_ACCOUNT_EMAIL" - renewEnvCertDomain = "LEGO_CERT_DOMAIN" - renewEnvCertPath = "LEGO_CERT_PATH" - renewEnvCertKeyPath = "LEGO_CERT_KEY_PATH" -) - -func createRenew() cli.Command { - return cli.Command{ - Name: "renew", - Usage: "Renew a certificate", - Action: renew, - Before: func(ctx *cli.Context) error { - // we require either domains or csr, but not both - hasDomains := len(ctx.GlobalStringSlice("domains")) > 0 - hasCsr := len(ctx.GlobalString("csr")) > 0 - if hasDomains && hasCsr { - log.Panic("Please specify either --domains/-d or --csr/-c, but not both") - } - if !hasDomains && !hasCsr { - log.Panic("Please specify --domains/-d (or --csr/-c if you already have a CSR)") - } - return nil - }, - Flags: []cli.Flag{ - cli.IntFlag{ - Name: "days", - Value: 30, - Usage: "The number of days left on a certificate to renew it.", - }, - cli.BoolFlag{ - Name: "reuse-key", - Usage: "Used to indicate you want to reuse your current private key for the new certificate.", - }, - cli.BoolFlag{ - Name: "no-bundle", - Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.", - }, - cli.BoolFlag{ - Name: "must-staple", - Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.", - }, - cli.StringFlag{ - Name: "renew-hook", - Usage: "Define a hook. The hook is executed only when the certificates are effectively renewed.", - }, - cli.StringFlag{ - Name: "preferred-chain", - Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.", - }, - }, - } -} - -func renew(ctx *cli.Context) error { - account, client := setup(ctx, NewAccountsStorage(ctx)) - setupChallenges(ctx, client) - - if account.Registration == nil { - log.Panicf("Account %s is not registered. Use 'run' to register a new account.\n", account.Email) - } - - certsStorage := NewCertificatesStorage(ctx) - - bundle := !ctx.Bool("no-bundle") - - meta := map[string]string{renewEnvAccountEmail: account.Email} - - // CSR - if ctx.GlobalIsSet("csr") { - return renewForCSR(ctx, client, certsStorage, bundle, meta) - } - - // Domains - return renewForDomains(ctx, client, certsStorage, bundle, meta) -} - -func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error { - domains := ctx.GlobalStringSlice("domains") - domain := domains[0] - - // load the cert resource from files. - // We store the certificate, private key and metadata in different files - // as web servers would not be able to work with a combined file. - certificates, err := certsStorage.ReadCertificate(domain, ".crt") - if err != nil { - log.Panicf("Error while loading the certificate for domain %s\n\t%v", domain, err) - } - - cert := certificates[0] - - if !needRenewal(cert, domain, ctx.Int("days")) { - return nil - } - - // This is just meant to be informal for the user. - timeLeft := cert.NotAfter.Sub(time.Now().UTC()) - log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours())) - - certDomains := certcrypto.ExtractDomains(cert) - - var privateKey crypto.PrivateKey - if ctx.Bool("reuse-key") { - keyBytes, errR := certsStorage.ReadFile(domain, ".key") - if errR != nil { - log.Panicf("Error while loading the private key for domain %s\n\t%v", domain, errR) - } - - privateKey, errR = certcrypto.ParsePEMPrivateKey(keyBytes) - if errR != nil { - return errR - } - } - - request := certificate.ObtainRequest{ - Domains: merge(certDomains, domains), - Bundle: bundle, - PrivateKey: privateKey, - MustStaple: ctx.Bool("must-staple"), - PreferredChain: ctx.String("preferred-chain"), - } - certRes, err := client.Certificate.Obtain(request) - if err != nil { - log.Panic(err) - } - - certsStorage.SaveResource(certRes) - - meta[renewEnvCertDomain] = domain - meta[renewEnvCertPath] = certsStorage.GetFileName(domain, ".crt") - meta[renewEnvCertKeyPath] = certsStorage.GetFileName(domain, ".key") - - return launchHook(ctx.String("renew-hook"), meta) -} - -func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error { - csr, err := readCSRFile(ctx.GlobalString("csr")) - if err != nil { - log.Panic(err) - } - - domain := csr.Subject.CommonName - - // load the cert resource from files. - // We store the certificate, private key and metadata in different files - // as web servers would not be able to work with a combined file. - certificates, err := certsStorage.ReadCertificate(domain, ".crt") - if err != nil { - log.Panicf("Error while loading the certificate for domain %s\n\t%v", domain, err) - } - - cert := certificates[0] - - if !needRenewal(cert, domain, ctx.Int("days")) { - return nil - } - - // This is just meant to be informal for the user. - timeLeft := cert.NotAfter.Sub(time.Now().UTC()) - log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours())) - - certRes, err := client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{ - CSR: csr, - Bundle: bundle, - PreferredChain: ctx.String("preferred-chain"), - }) - if err != nil { - log.Panic(err) - } - - certsStorage.SaveResource(certRes) - - meta[renewEnvCertDomain] = domain - meta[renewEnvCertPath] = certsStorage.GetFileName(domain, ".crt") - meta[renewEnvCertKeyPath] = certsStorage.GetFileName(domain, ".key") - - return launchHook(ctx.String("renew-hook"), meta) -} - -func needRenewal(x509Cert *x509.Certificate, domain string, days int) bool { - if x509Cert.IsCA { - log.Panicf("[%s] Certificate bundle starts with a CA certificate", domain) - } - - if days >= 0 { - notAfter := int(time.Until(x509Cert.NotAfter).Hours() / 24.0) - if notAfter > days { - log.Printf("[%s] The certificate expires in %d days, the number of days defined to perform the renewal is %d: no renewal.", - domain, notAfter, days) - return false - } - } - - return true -} - -func merge(prevDomains, nextDomains []string) []string { - for _, next := range nextDomains { - var found bool - for _, prev := range prevDomains { - if prev == next { - found = true - break - } - } - if !found { - prevDomains = append(prevDomains, next) - } - } - return prevDomains -} diff --git a/node/controller/legoCmd/cmd/cmd_renew_test.go b/node/controller/legoCmd/cmd/cmd_renew_test.go deleted file mode 100644 index 0ad9526..0000000 --- a/node/controller/legoCmd/cmd/cmd_renew_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package cmd - -import ( - "crypto/x509" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func Test_merge(t *testing.T) { - testCases := []struct { - desc string - prevDomains []string - nextDomains []string - expected []string - }{ - { - desc: "all empty", - prevDomains: []string{}, - nextDomains: []string{}, - expected: []string{}, - }, - { - desc: "next empty", - prevDomains: []string{"a", "b", "c"}, - nextDomains: []string{}, - expected: []string{"a", "b", "c"}, - }, - { - desc: "prev empty", - prevDomains: []string{}, - nextDomains: []string{"a", "b", "c"}, - expected: []string{"a", "b", "c"}, - }, - { - desc: "merge append", - prevDomains: []string{"a", "b", "c"}, - nextDomains: []string{"a", "c", "d"}, - expected: []string{"a", "b", "c", "d"}, - }, - { - desc: "merge same", - prevDomains: []string{"a", "b", "c"}, - nextDomains: []string{"a", "b", "c"}, - expected: []string{"a", "b", "c"}, - }, - } - - for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - actual := merge(test.prevDomains, test.nextDomains) - assert.Equal(t, test.expected, actual) - }) - } -} - -func Test_needRenewal(t *testing.T) { - testCases := []struct { - desc string - x509Cert *x509.Certificate - days int - expected bool - }{ - { - desc: "30 days, NotAfter now", - x509Cert: &x509.Certificate{ - NotAfter: time.Now(), - }, - days: 30, - expected: true, - }, - { - desc: "30 days, NotAfter 31 days", - x509Cert: &x509.Certificate{ - NotAfter: time.Now().Add(31*24*time.Hour + 1*time.Second), - }, - days: 30, - expected: false, - }, - { - desc: "30 days, NotAfter 30 days", - x509Cert: &x509.Certificate{ - NotAfter: time.Now().Add(30 * 24 * time.Hour), - }, - days: 30, - expected: true, - }, - { - desc: "0 days, NotAfter 30 days: only the day of the expiration", - x509Cert: &x509.Certificate{ - NotAfter: time.Now().Add(30 * 24 * time.Hour), - }, - days: 0, - expected: false, - }, - { - desc: "-1 days, NotAfter 30 days: always renew", - x509Cert: &x509.Certificate{ - NotAfter: time.Now().Add(30 * 24 * time.Hour), - }, - days: -1, - expected: true, - }, - } - - for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { - actual := needRenewal(test.x509Cert, "foo.com", test.days) - - assert.Equal(t, test.expected, actual) - }) - } -} diff --git a/node/controller/legoCmd/cmd/cmd_revoke.go b/node/controller/legoCmd/cmd/cmd_revoke.go deleted file mode 100644 index d7ee201..0000000 --- a/node/controller/legoCmd/cmd/cmd_revoke.go +++ /dev/null @@ -1,62 +0,0 @@ -package cmd - -import ( - "github.com/Yuzuki616/V2bX/node/controller/legoCmd/log" - "github.com/urfave/cli" -) - -func createRevoke() cli.Command { - return cli.Command{ - Name: "revoke", - Usage: "Revoke a certificate", - Action: revoke, - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "keep, k", - Usage: "Keep the certificates after the revocation instead of archiving them.", - }, - }, - } -} - -func revoke(ctx *cli.Context) error { - acc, client := setup(ctx, NewAccountsStorage(ctx)) - - if acc.Registration == nil { - log.Panicf("Account %s is not registered. Use 'run' to register a new account.\n", acc.Email) - } - - certsStorage := NewCertificatesStorage(ctx) - certsStorage.CreateRootFolder() - - for _, domain := range ctx.GlobalStringSlice("domains") { - log.Printf("Trying to revoke certificate for domain %s", domain) - - certBytes, err := certsStorage.ReadFile(domain, ".crt") - if err != nil { - log.Panicf("Error while revoking the certificate for domain %s\n\t%v", domain, err) - } - - err = client.Certificate.Revoke(certBytes) - if err != nil { - log.Panicf("Error while revoking the certificate for domain %s\n\t%v", domain, err) - } - - log.Println("Certificate was revoked.") - - if ctx.Bool("keep") { - return nil - } - - certsStorage.CreateArchiveFolder() - - err = certsStorage.MoveToArchive(domain) - if err != nil { - return err - } - - log.Println("Certificate was archived for domain:", domain) - } - - return nil -} diff --git a/node/controller/legoCmd/cmd/cmd_run.go b/node/controller/legoCmd/cmd/cmd_run.go deleted file mode 100644 index 298195c..0000000 --- a/node/controller/legoCmd/cmd/cmd_run.go +++ /dev/null @@ -1,186 +0,0 @@ -package cmd - -import ( - "bufio" - "fmt" - "github.com/Yuzuki616/V2bX/node/controller/legoCmd/log" - "os" - "strings" - - "github.com/go-acme/lego/v4/certificate" - "github.com/go-acme/lego/v4/lego" - "github.com/go-acme/lego/v4/registration" - "github.com/urfave/cli" -) - -func createRun() cli.Command { - return cli.Command{ - Name: "run", - Usage: "Register an account, then create and install a certificate", - Before: func(ctx *cli.Context) error { - // we require either domains or csr, but not both - hasDomains := len(ctx.GlobalStringSlice("domains")) > 0 - hasCsr := len(ctx.GlobalString("csr")) > 0 - if hasDomains && hasCsr { - log.Panic("Please specify either --domains/-d or --csr/-c, but not both") - } - if !hasDomains && !hasCsr { - log.Panic("Please specify --domains/-d (or --csr/-c if you already have a CSR)") - } - return nil - }, - Action: run, - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "no-bundle", - Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.", - }, - cli.BoolFlag{ - Name: "must-staple", - Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.", - }, - cli.StringFlag{ - Name: "run-hook", - Usage: "Define a hook. The hook is executed when the certificates are effectively created.", - }, - cli.StringFlag{ - Name: "preferred-chain", - Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.", - }, - }, - } -} - -const rootPathWarningMessage = `!!!! HEADS UP !!!! - -Your account credentials have been saved in your Let's Encrypt -configuration directory at "%s". - -You should make a secure backup of this folder now. This -configuration directory will also contain certificates and -private keys obtained from Let's Encrypt so making regular -backups of this folder is ideal. -` - -func run(ctx *cli.Context) error { - accountsStorage := NewAccountsStorage(ctx) - - account, client := setup(ctx, accountsStorage) - setupChallenges(ctx, client) - - if account.Registration == nil { - reg, err := register(ctx, client) - if err != nil { - log.Panicf("Could not complete registration\n\t%v", err) - } - - account.Registration = reg - if err = accountsStorage.Save(account); err != nil { - log.Panic(err) - } - - fmt.Printf(rootPathWarningMessage, accountsStorage.GetRootPath()) - } - - certsStorage := NewCertificatesStorage(ctx) - certsStorage.CreateRootFolder() - - cert, err := obtainCertificate(ctx, client) - if err != nil { - // Make sure to return a non-zero exit code if ObtainSANCertificate returned at least one error. - // Due to us not returning partial certificate we can just exit here instead of at the end. - log.Panicf("Could not obtain certificates:\n\t%v", err) - } - - certsStorage.SaveResource(cert) - - meta := map[string]string{ - renewEnvAccountEmail: account.Email, - renewEnvCertDomain: cert.Domain, - renewEnvCertPath: certsStorage.GetFileName(cert.Domain, ".crt"), - renewEnvCertKeyPath: certsStorage.GetFileName(cert.Domain, ".key"), - } - - return launchHook(ctx.String("run-hook"), meta) -} - -func handleTOS(ctx *cli.Context, client *lego.Client) bool { - // Check for a global accept override - if ctx.GlobalBool("accept-tos") { - return true - } - - reader := bufio.NewReader(os.Stdin) - log.Printf("Please review the TOS at %s", client.GetToSURL()) - - for { - fmt.Println("Do you accept the TOS? Y/n") - text, err := reader.ReadString('\n') - if err != nil { - log.Panicf("Could not read from console: %v", err) - } - - text = strings.Trim(text, "\r\n") - switch text { - case "", "y", "Y": - return true - case "n", "N": - return false - default: - fmt.Println("Your input was invalid. Please answer with one of Y/y, n/N or by pressing enter.") - } - } -} - -func register(ctx *cli.Context, client *lego.Client) (*registration.Resource, error) { - accepted := handleTOS(ctx, client) - if !accepted { - log.Panic("You did not accept the TOS. Unable to proceed.") - } - - if ctx.GlobalBool("eab") { - kid := ctx.GlobalString("kid") - hmacEncoded := ctx.GlobalString("hmac") - - if kid == "" || hmacEncoded == "" { - log.Panicf("Requires arguments --kid and --hmac.") - } - - return client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ - TermsOfServiceAgreed: accepted, - Kid: kid, - HmacEncoded: hmacEncoded, - }) - } - - return client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) -} - -func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Resource, error) { - bundle := !ctx.Bool("no-bundle") - - domains := ctx.GlobalStringSlice("domains") - if len(domains) > 0 { - // obtain a certificate, generating a new private key - request := certificate.ObtainRequest{ - Domains: domains, - Bundle: bundle, - MustStaple: ctx.Bool("must-staple"), - PreferredChain: ctx.String("preferred-chain"), - } - return client.Certificate.Obtain(request) - } - - // read the CSR - csr, err := readCSRFile(ctx.GlobalString("csr")) - if err != nil { - return nil, err - } - - // obtain a certificate for this CSR - return client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{ - CSR: csr, - Bundle: bundle, - PreferredChain: ctx.String("preferred-chain"), - }) -} diff --git a/node/controller/legoCmd/cmd/flags.go b/node/controller/legoCmd/cmd/flags.go deleted file mode 100644 index 8307b2c..0000000 --- a/node/controller/legoCmd/cmd/flags.go +++ /dev/null @@ -1,120 +0,0 @@ -package cmd - -import ( - "github.com/go-acme/lego/v4/lego" - "github.com/urfave/cli" -) - -func CreateFlags(defaultPath string) []cli.Flag { - return []cli.Flag{ - cli.StringSliceFlag{ - Name: "domains, d", - Usage: "Add a domain to the process. Can be specified multiple times.", - }, - cli.StringFlag{ - Name: "server, s", - Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.", - Value: lego.LEDirectoryProduction, - }, - cli.BoolFlag{ - Name: "accept-tos, a", - Usage: "By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.", - }, - cli.StringFlag{ - Name: "email, m", - Usage: "Email used for registration and recovery contact.", - }, - cli.StringFlag{ - Name: "csr, c", - Usage: "Certificate signing request filename, if an external CSR is to be used.", - }, - cli.BoolFlag{ - Name: "eab", - Usage: "Use External Account Binding for account registration. Requires --kid and --hmac.", - }, - cli.StringFlag{ - Name: "kid", - Usage: "Key identifier from External CA. Used for External Account Binding.", - }, - cli.StringFlag{ - Name: "hmac", - Usage: "MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.", - }, - cli.StringFlag{ - Name: "key-type, k", - Value: "ec256", - Usage: "Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384.", - }, - cli.StringFlag{ - Name: "filename", - Usage: "(deprecated) Filename of the generated certificate.", - }, - cli.StringFlag{ - Name: "path", - EnvVar: "LEGO_PATH", - Usage: "Directory to use for storing the data.", - Value: defaultPath, - }, - cli.BoolFlag{ - Name: "http", - Usage: "Use the HTTP challenge to solve challenges. Can be mixed with other types of challenges.", - }, - cli.StringFlag{ - Name: "http.port", - Usage: "Set the port and interface to use for HTTP based challenges to listen on.Supported: interface:port or :port.", - Value: ":80", - }, - cli.StringFlag{ - Name: "http.proxy-header", - Usage: "Validate against this HTTP header when solving HTTP based challenges behind a reverse proxy.", - Value: "Host", - }, - cli.StringFlag{ - Name: "http.webroot", - Usage: "Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge. This disables the built-in server and expects the given directory to be publicly served with access to .well-known/acme-challenge", - }, - cli.StringSliceFlag{ - Name: "http.memcached-host", - Usage: "Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.", - }, - cli.BoolFlag{ - Name: "tls", - Usage: "Use the TLS challenge to solve challenges. Can be mixed with other types of challenges.", - }, - cli.StringFlag{ - Name: "tls.port", - Usage: "Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port.", - Value: ":443", - }, - cli.StringFlag{ - Name: "dns", - Usage: "Solve a DNS challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.", - }, - cli.BoolFlag{ - Name: "dns.disable-cp", - Usage: "By setting this flag to true, disables the need to wait the propagation of the TXT record to all authoritative name servers.", - }, - cli.StringSliceFlag{ - Name: "dns.resolvers", - Usage: "Set the resolvers to use for performing recursive DNS queries. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.", - }, - cli.IntFlag{ - Name: "http-timeout", - Usage: "Set the HTTP timeout value to a specific value in seconds.", - }, - cli.IntFlag{ - Name: "dns-timeout", - Usage: "Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries.", - Value: 10, - }, - cli.BoolFlag{ - Name: "pem", - Usage: "Generate a .pem file by concatenating the .key and .crt files together.", - }, - cli.IntFlag{ - Name: "cert.timeout", - Usage: "Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates.", - Value: 30, - }, - } -} diff --git a/node/controller/legoCmd/cmd/hook.go b/node/controller/legoCmd/cmd/hook.go deleted file mode 100644 index 0b0ca40..0000000 --- a/node/controller/legoCmd/cmd/hook.go +++ /dev/null @@ -1,47 +0,0 @@ -package cmd - -import ( - "context" - "errors" - "fmt" - "os" - "os/exec" - "strings" - "time" -) - -func launchHook(hook string, meta map[string]string) error { - if hook == "" { - return nil - } - - ctxCmd, cancel := context.WithTimeout(context.Background(), 120*time.Second) - defer cancel() - - parts := strings.Fields(hook) - - cmdCtx := exec.CommandContext(ctxCmd, parts[0], parts[1:]...) - cmdCtx.Env = append(os.Environ(), metaToEnv(meta)...) - - output, err := cmdCtx.CombinedOutput() - - if len(output) > 0 { - fmt.Println(string(output)) - } - - if errors.Is(ctxCmd.Err(), context.DeadlineExceeded) { - return errors.New("hook timed out") - } - - return err -} - -func metaToEnv(meta map[string]string) []string { - var envs []string - - for k, v := range meta { - envs = append(envs, k+"="+v) - } - - return envs -} diff --git a/node/controller/legoCmd/cmd/setup.go b/node/controller/legoCmd/cmd/setup.go deleted file mode 100644 index 86bcff1..0000000 --- a/node/controller/legoCmd/cmd/setup.go +++ /dev/null @@ -1,129 +0,0 @@ -package cmd - -import ( - "crypto/x509" - "encoding/pem" - "fmt" - "github.com/Yuzuki616/V2bX/node/controller/legoCmd/log" - "io/ioutil" - "os" - "strings" - "time" - - "github.com/go-acme/lego/v4/certcrypto" - "github.com/go-acme/lego/v4/lego" - "github.com/go-acme/lego/v4/registration" - "github.com/urfave/cli" -) - -const filePerm os.FileMode = 0o600 - -func setup(ctx *cli.Context, accountsStorage *AccountsStorage) (*Account, *lego.Client) { - keyType := getKeyType(ctx) - privateKey := accountsStorage.GetPrivateKey(keyType) - - var account *Account - if accountsStorage.ExistsAccountFilePath() { - account = accountsStorage.LoadAccount(privateKey) - } else { - account = &Account{Email: accountsStorage.GetUserID(), key: privateKey} - } - - client := newClient(ctx, account, keyType) - - return account, client -} - -func newClient(ctx *cli.Context, acc registration.User, keyType certcrypto.KeyType) *lego.Client { - config := lego.NewConfig(acc) - config.CADirURL = ctx.GlobalString("server") - - config.Certificate = lego.CertificateConfig{ - KeyType: keyType, - Timeout: time.Duration(ctx.GlobalInt("cert.timeout")) * time.Second, - } - config.UserAgent = fmt.Sprintf("lego-cli/%s", ctx.App.Version) - - if ctx.GlobalIsSet("http-timeout") { - config.HTTPClient.Timeout = time.Duration(ctx.GlobalInt("http-timeout")) * time.Second - } - - client, err := lego.NewClient(config) - if err != nil { - log.Panicf("Could not create client: %v", err) - } - - if client.GetExternalAccountRequired() && !ctx.GlobalIsSet("eab") { - log.Panic("Server requires External Account Binding. Use --eab with --kid and --hmac.") - } - - return client -} - -// getKeyType the type from which private keys should be generated. -func getKeyType(ctx *cli.Context) certcrypto.KeyType { - keyType := ctx.GlobalString("key-type") - switch strings.ToUpper(keyType) { - case "RSA2048": - return certcrypto.RSA2048 - case "RSA4096": - return certcrypto.RSA4096 - case "RSA8192": - return certcrypto.RSA8192 - case "EC256": - return certcrypto.EC256 - case "EC384": - return certcrypto.EC384 - } - - log.Panicf("Unsupported KeyType: %s", keyType) - return "" -} - -func getEmail(ctx *cli.Context) string { - email := ctx.GlobalString("email") - if email == "" { - log.Panic("You have to pass an account (email address) to the program using --email or -m") - } - return email -} - -func createNonExistingFolder(path string) error { - if _, err := os.Stat(path); os.IsNotExist(err) { - return os.MkdirAll(path, 0o700) - } else if err != nil { - return err - } - return nil -} - -func readCSRFile(filename string) (*x509.CertificateRequest, error) { - bytes, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - raw := bytes - - // see if we can find a PEM-encoded CSR - var p *pem.Block - rest := bytes - for { - // decode a PEM block - p, rest = pem.Decode(rest) - - // did we fail? - if p == nil { - break - } - - // did we get a CSR? - if p.Type == "CERTIFICATE REQUEST" { - raw = p.Bytes - } - } - - // no PEM-encoded CSR - // assume we were given a DER-encoded ASN.1 CSR - // (if this assumption is wrong, parsing these bytes will fail) - return x509.ParseCertificateRequest(raw) -} diff --git a/node/controller/legoCmd/cmd/setup_challenges.go b/node/controller/legoCmd/cmd/setup_challenges.go deleted file mode 100644 index 449ca74..0000000 --- a/node/controller/legoCmd/cmd/setup_challenges.go +++ /dev/null @@ -1,126 +0,0 @@ -package cmd - -import ( - "github.com/Yuzuki616/V2bX/node/controller/legoCmd/log" - "net" - "strings" - "time" - - "github.com/go-acme/lego/v4/challenge" - "github.com/go-acme/lego/v4/challenge/dns01" - "github.com/go-acme/lego/v4/challenge/http01" - "github.com/go-acme/lego/v4/challenge/tlsalpn01" - "github.com/go-acme/lego/v4/lego" - "github.com/go-acme/lego/v4/providers/dns" - "github.com/go-acme/lego/v4/providers/http/memcached" - "github.com/go-acme/lego/v4/providers/http/webroot" - "github.com/urfave/cli" -) - -func setupChallenges(ctx *cli.Context, client *lego.Client) { - if !ctx.GlobalBool("http") && !ctx.GlobalBool("tls") && !ctx.GlobalIsSet("dns") { - log.Panic("No challenge selected. You must specify at least one challenge: `--http`, `--tls`, `--dns`.") - } - - if ctx.GlobalBool("http") { - err := client.Challenge.SetHTTP01Provider(setupHTTPProvider(ctx)) - if err != nil { - log.Panic(err) - } - } - - if ctx.GlobalBool("tls") { - err := client.Challenge.SetTLSALPN01Provider(setupTLSProvider(ctx)) - if err != nil { - log.Panic(err) - } - } - - if ctx.GlobalIsSet("dns") { - setupDNS(ctx, client) - } -} - -func setupHTTPProvider(ctx *cli.Context) challenge.Provider { - switch { - case ctx.GlobalIsSet("http.webroot"): - ps, err := webroot.NewHTTPProvider(ctx.GlobalString("http.webroot")) - if err != nil { - log.Panic(err) - } - return ps - case ctx.GlobalIsSet("http.memcached-host"): - ps, err := memcached.NewMemcachedProvider(ctx.GlobalStringSlice("http.memcached-host")) - if err != nil { - log.Panic(err) - } - return ps - case ctx.GlobalIsSet("http.port"): - iface := ctx.GlobalString("http.port") - if !strings.Contains(iface, ":") { - log.Panicf("The --http switch only accepts interface:port or :port for its argument.") - } - - host, port, err := net.SplitHostPort(iface) - if err != nil { - log.Panic(err) - } - - srv := http01.NewProviderServer(host, port) - if header := ctx.GlobalString("http.proxy-header"); header != "" { - srv.SetProxyHeader(header) - } - return srv - case ctx.GlobalBool("http"): - srv := http01.NewProviderServer("", "") - if header := ctx.GlobalString("http.proxy-header"); header != "" { - srv.SetProxyHeader(header) - } - return srv - default: - log.Panic("Invalid HTTP challenge options.") - return nil - } -} - -func setupTLSProvider(ctx *cli.Context) challenge.Provider { - switch { - case ctx.GlobalIsSet("tls.port"): - iface := ctx.GlobalString("tls.port") - if !strings.Contains(iface, ":") { - log.Panicf("The --tls switch only accepts interface:port or :port for its argument.") - } - - host, port, err := net.SplitHostPort(iface) - if err != nil { - log.Panic(err) - } - - return tlsalpn01.NewProviderServer(host, port) - case ctx.GlobalBool("tls"): - return tlsalpn01.NewProviderServer("", "") - default: - log.Panic("Invalid HTTP challenge options.") - return nil - } -} - -func setupDNS(ctx *cli.Context, client *lego.Client) { - provider, err := dns.NewDNSChallengeProviderByName(ctx.GlobalString("dns")) - if err != nil { - log.Panic(err) - } - - servers := ctx.GlobalStringSlice("dns.resolvers") - err = client.Challenge.SetDNS01Provider(provider, - dns01.CondOption(len(servers) > 0, - dns01.AddRecursiveNameservers(dns01.ParseNameservers(ctx.GlobalStringSlice("dns.resolvers")))), - dns01.CondOption(ctx.GlobalBool("dns.disable-cp"), - dns01.DisableCompletePropagationRequirement()), - dns01.CondOption(ctx.GlobalIsSet("dns-timeout"), - dns01.AddDNSTimeout(time.Duration(ctx.GlobalInt("dns-timeout"))*time.Second)), - ) - if err != nil { - log.Panic(err) - } -} diff --git a/node/controller/legoCmd/cmd/zz_gen_cmd_dnshelp.go b/node/controller/legoCmd/cmd/zz_gen_cmd_dnshelp.go deleted file mode 100644 index 29db62c..0000000 --- a/node/controller/legoCmd/cmd/zz_gen_cmd_dnshelp.go +++ /dev/null @@ -1,1884 +0,0 @@ -package cmd - -// CODE GENERATED AUTOMATICALLY -// THIS FILE MUST NOT BE EDITED BY HAND - -import ( - "fmt" - "os" - "sort" - "strings" - "text/tabwriter" -) - -func allDNSCodes() string { - providers := []string{ - "manual", - "acme-dns", - "alidns", - "arvancloud", - "auroradns", - "autodns", - "azure", - "bindman", - "bluecat", - "checkdomain", - "clouddns", - "cloudflare", - "cloudns", - "cloudxns", - "conoha", - "constellix", - "desec", - "designate", - "digitalocean", - "dnsimple", - "dnsmadeeasy", - "dnspod", - "dode", - "domeneshop", - "dreamhost", - "duckdns", - "dyn", - "dynu", - "easydns", - "edgedns", - "exec", - "exoscale", - "gandi", - "gandiv5", - "gcloud", - "glesys", - "godaddy", - "hetzner", - "hostingde", - "httpreq", - "hurricane", - "hyperone", - "iij", - "infomaniak", - "inwx", - "ionos", - "joker", - "lightsail", - "linode", - "liquidweb", - "loopia", - "luadns", - "mydnsjp", - "mythicbeasts", - "namecheap", - "namedotcom", - "namesilo", - "netcup", - "netlify", - "nifcloud", - "njalla", - "ns1", - "oraclecloud", - "otc", - "ovh", - "pdns", - "rackspace", - "regru", - "rfc2136", - "rimuhosting", - "route53", - "sakuracloud", - "scaleway", - "selectel", - "servercow", - "stackpath", - "transip", - "vegadns", - "versio", - "vscale", - "vultr", - "yandex", - "zoneee", - "zonomi", - } - sort.Strings(providers) - return strings.Join(providers, ", ") -} - -func displayDNSHelp(name string) error { - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - ew := &errWriter{w: w} - - switch name { - case "acme-dns": - // generated from: providers/dns/acmedns/acmedns.toml - ew.writeln(`Configuration for Joohoi's ACME-DNS.`) - ew.writeln(`Code: 'acme-dns'`) - ew.writeln(`Since: 'v1.1.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "ACME_DNS_API_BASE": The ACME-DNS API address`) - ew.writeln(` - "ACME_DNS_STORAGE_PATH": The ACME-DNS JSON account data file. A per-domain account will be registered/persisted to this file and used for TXT updates.`) - ew.writeln() - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/acme-dns`) - - case "alidns": - // generated from: providers/dns/alidns/alidns.toml - ew.writeln(`Configuration for Alibaba Cloud DNS.`) - ew.writeln(`Code: 'alidns'`) - ew.writeln(`Since: 'v1.1.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "ALICLOUD_ACCESS_KEY": Access key ID`) - ew.writeln(` - "ALICLOUD_SECRET_KEY": Access Key secret`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "ALICLOUD_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "ALICLOUD_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "ALICLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "ALICLOUD_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/alidns`) - - case "arvancloud": - // generated from: providers/dns/arvancloud/arvancloud.toml - ew.writeln(`Configuration for ArvanCloud.`) - ew.writeln(`Code: 'arvancloud'`) - ew.writeln(`Since: 'v3.8.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "ARVANCLOUD_API_KEY": API key`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "ARVANCLOUD_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "ARVANCLOUD_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "ARVANCLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "ARVANCLOUD_SEQUENCE_INTERVAL": Interval between iteration`) - ew.writeln(` - "ARVANCLOUD_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/arvancloud`) - - case "auroradns": - // generated from: providers/dns/auroradns/auroradns.toml - ew.writeln(`Configuration for Aurora DNS.`) - ew.writeln(`Code: 'auroradns'`) - ew.writeln(`Since: 'v0.4.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "AURORA_KEY": User API key`) - ew.writeln(` - "AURORA_USER_ID": User ID`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "AURORA_ENDPOINT": API endpoint URL`) - ew.writeln(` - "AURORA_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "AURORA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "AURORA_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/auroradns`) - - case "autodns": - // generated from: providers/dns/autodns/autodns.toml - ew.writeln(`Configuration for Autodns.`) - ew.writeln(`Code: 'autodns'`) - ew.writeln(`Since: 'v3.2.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "AUTODNS_API_PASSWORD": User Password`) - ew.writeln(` - "AUTODNS_API_USER": Username`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "AUTODNS_CONTEXT": API context (4 for production, 1 for testing. Defaults to 4)`) - ew.writeln(` - "AUTODNS_ENDPOINT": API endpoint URL, defaults to https://api.autodns.com/v1/`) - ew.writeln(` - "AUTODNS_HTTP_TIMEOUT": API request timeout, defaults to 30 seconds`) - ew.writeln(` - "AUTODNS_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "AUTODNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "AUTODNS_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/autodns`) - - case "azure": - // generated from: providers/dns/azure/azure.toml - ew.writeln(`Configuration for Azure.`) - ew.writeln(`Code: 'azure'`) - ew.writeln(`Since: 'v0.4.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "AZURE_CLIENT_ID": Client ID`) - ew.writeln(` - "AZURE_CLIENT_SECRET": Client secret`) - ew.writeln(` - "AZURE_ENVIRONMENT": Azure environment, one of: public, usgovernment, german, and china`) - ew.writeln(` - "AZURE_RESOURCE_GROUP": Resource group`) - ew.writeln(` - "AZURE_SUBSCRIPTION_ID": Subscription ID`) - ew.writeln(` - "AZURE_TENANT_ID": Tenant ID`) - ew.writeln(` - "instance metadata service": If the credentials are **not** set via the environment, then it will attempt to get a bearer token via the [instance metadata service](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service).`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "AZURE_METADATA_ENDPOINT": Metadata Service endpoint URL`) - ew.writeln(` - "AZURE_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "AZURE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "AZURE_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/azure`) - - case "bindman": - // generated from: providers/dns/bindman/bindman.toml - ew.writeln(`Configuration for Bindman.`) - ew.writeln(`Code: 'bindman'`) - ew.writeln(`Since: 'v2.6.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "BINDMAN_MANAGER_ADDRESS": The server URL, should have scheme, hostname, and port (if required) of the Bindman-DNS Manager server`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "BINDMAN_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "BINDMAN_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "BINDMAN_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/bindman`) - - case "bluecat": - // generated from: providers/dns/bluecat/bluecat.toml - ew.writeln(`Configuration for Bluecat.`) - ew.writeln(`Code: 'bluecat'`) - ew.writeln(`Since: 'v0.5.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "BLUECAT_CONFIG_NAME": Configuration name`) - ew.writeln(` - "BLUECAT_DNS_VIEW": External DNS View Name`) - ew.writeln(` - "BLUECAT_PASSWORD": API password`) - ew.writeln(` - "BLUECAT_SERVER_URL": The server URL, should have scheme, hostname, and port (if required) of the authoritative Bluecat BAM serve`) - ew.writeln(` - "BLUECAT_USER_NAME": API username`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "BLUECAT_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "BLUECAT_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "BLUECAT_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "BLUECAT_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/bluecat`) - - case "checkdomain": - // generated from: providers/dns/checkdomain/checkdomain.toml - ew.writeln(`Configuration for Checkdomain.`) - ew.writeln(`Code: 'checkdomain'`) - ew.writeln(`Since: 'v3.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "CHECKDOMAIN_TOKEN": API token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "CHECKDOMAIN_ENDPOINT": API endpoint URL, defaults to https://api.checkdomain.de`) - ew.writeln(` - "CHECKDOMAIN_HTTP_TIMEOUT": API request timeout, defaults to 30 seconds`) - ew.writeln(` - "CHECKDOMAIN_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "CHECKDOMAIN_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "CHECKDOMAIN_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/checkdomain`) - - case "clouddns": - // generated from: providers/dns/clouddns/clouddns.toml - ew.writeln(`Configuration for CloudDNS.`) - ew.writeln(`Code: 'clouddns'`) - ew.writeln(`Since: 'v3.6.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "CLOUDDNS_CLIENT_ID": Client ID`) - ew.writeln(` - "CLOUDDNS_EMAIL": Account email`) - ew.writeln(` - "CLOUDDNS_PASSWORD": Account password`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "CLOUDDNS_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "CLOUDDNS_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "CLOUDDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "CLOUDDNS_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/clouddns`) - - case "cloudflare": - // generated from: providers/dns/cloudflare/cloudflare.toml - ew.writeln(`Configuration for Cloudflare.`) - ew.writeln(`Code: 'cloudflare'`) - ew.writeln(`Since: 'v0.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "CF_API_EMAIL": Account email`) - ew.writeln(` - "CF_API_KEY": API key`) - ew.writeln(` - "CF_DNS_API_TOKEN": API token with DNS:Edit permission (since v3.1.0)`) - ew.writeln(` - "CF_ZONE_API_TOKEN": API token with Zone:Read permission (since v3.1.0)`) - ew.writeln(` - "CLOUDFLARE_API_KEY": Alias to CF_API_KEY`) - ew.writeln(` - "CLOUDFLARE_DNS_API_TOKEN": Alias to CF_DNS_API_TOKEN`) - ew.writeln(` - "CLOUDFLARE_EMAIL": Alias to CF_API_EMAIL`) - ew.writeln(` - "CLOUDFLARE_ZONE_API_TOKEN": Alias to CF_ZONE_API_TOKEN`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "CLOUDFLARE_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "CLOUDFLARE_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "CLOUDFLARE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "CLOUDFLARE_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/cloudflare`) - - case "cloudns": - // generated from: providers/dns/cloudns/cloudns.toml - ew.writeln(`Configuration for ClouDNS.`) - ew.writeln(`Code: 'cloudns'`) - ew.writeln(`Since: 'v2.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "CLOUDNS_AUTH_ID": The API user ID`) - ew.writeln(` - "CLOUDNS_AUTH_PASSWORD": The password for API user ID`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "CLOUDNS_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "CLOUDNS_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "CLOUDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "CLOUDNS_SUB_AUTH_ID": The API sub user ID`) - ew.writeln(` - "CLOUDNS_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/cloudns`) - - case "cloudxns": - // generated from: providers/dns/cloudxns/cloudxns.toml - ew.writeln(`Configuration for CloudXNS.`) - ew.writeln(`Code: 'cloudxns'`) - ew.writeln(`Since: 'v0.5.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "CLOUDXNS_API_KEY": The API key`) - ew.writeln(` - "CLOUDXNS_SECRET_KEY": The API secret key`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "CLOUDXNS_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "CLOUDXNS_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "CLOUDXNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "CLOUDXNS_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/cloudxns`) - - case "conoha": - // generated from: providers/dns/conoha/conoha.toml - ew.writeln(`Configuration for ConoHa.`) - ew.writeln(`Code: 'conoha'`) - ew.writeln(`Since: 'v1.2.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "CONOHA_API_PASSWORD": The API password`) - ew.writeln(` - "CONOHA_API_USERNAME": The API username`) - ew.writeln(` - "CONOHA_TENANT_ID": Tenant ID`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "CONOHA_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "CONOHA_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "CONOHA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "CONOHA_REGION": The region`) - ew.writeln(` - "CONOHA_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/conoha`) - - case "constellix": - // generated from: providers/dns/constellix/constellix.toml - ew.writeln(`Configuration for Constellix.`) - ew.writeln(`Code: 'constellix'`) - ew.writeln(`Since: 'v3.4.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "CONSTELLIX_API_KEY": User API key`) - ew.writeln(` - "CONSTELLIX_SECRET_KEY": User secret key`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "CONSTELLIX_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "CONSTELLIX_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "CONSTELLIX_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "CONSTELLIX_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/constellix`) - - case "desec": - // generated from: providers/dns/desec/desec.toml - ew.writeln(`Configuration for deSEC.io.`) - ew.writeln(`Code: 'desec'`) - ew.writeln(`Since: 'v3.7.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "DESEC_TOKEN": Domain token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "DESEC_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "DESEC_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "DESEC_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "DESEC_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/desec`) - - case "designate": - // generated from: providers/dns/designate/designate.toml - ew.writeln(`Configuration for Designate DNSaaS for Openstack.`) - ew.writeln(`Code: 'designate'`) - ew.writeln(`Since: 'v2.2.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "OS_APPLICATION_CREDENTIAL_ID": Application credential ID`) - ew.writeln(` - "OS_APPLICATION_CREDENTIAL_NAME": Application credential name`) - ew.writeln(` - "OS_APPLICATION_CREDENTIAL_SECRET": Application credential secret`) - ew.writeln(` - "OS_AUTH_URL": Identity endpoint URL`) - ew.writeln(` - "OS_PASSWORD": Password`) - ew.writeln(` - "OS_PROJECT_NAME": Project name`) - ew.writeln(` - "OS_REGION_NAME": Region name`) - ew.writeln(` - "OS_USERNAME": Username`) - ew.writeln(` - "OS_USER_ID": User ID`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "DESIGNATE_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "DESIGNATE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "DESIGNATE_TTL": The TTL of the TXT record used for the DNS challenge`) - ew.writeln(` - "OS_PROJECT_ID": Project ID`) - ew.writeln(` - "OS_TENANT_NAME": Tenant name (deprecated see OS_PROJECT_NAME and OS_PROJECT_ID)`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/designate`) - - case "digitalocean": - // generated from: providers/dns/digitalocean/digitalocean.toml - ew.writeln(`Configuration for Digital Ocean.`) - ew.writeln(`Code: 'digitalocean'`) - ew.writeln(`Since: 'v0.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "DO_AUTH_TOKEN": Authentication token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "DO_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "DO_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "DO_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "DO_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/digitalocean`) - - case "dnsimple": - // generated from: providers/dns/dnsimple/dnsimple.toml - ew.writeln(`Configuration for DNSimple.`) - ew.writeln(`Code: 'dnsimple'`) - ew.writeln(`Since: 'v0.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "DNSIMPLE_OAUTH_TOKEN": OAuth token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "DNSIMPLE_BASE_URL": API endpoint URL`) - ew.writeln(` - "DNSIMPLE_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "DNSIMPLE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "DNSIMPLE_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/dnsimple`) - - case "dnsmadeeasy": - // generated from: providers/dns/dnsmadeeasy/dnsmadeeasy.toml - ew.writeln(`Configuration for DNS Made Easy.`) - ew.writeln(`Code: 'dnsmadeeasy'`) - ew.writeln(`Since: 'v0.4.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "DNSMADEEASY_API_KEY": The API key`) - ew.writeln(` - "DNSMADEEASY_API_SECRET": The API Secret key`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "DNSMADEEASY_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "DNSMADEEASY_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "DNSMADEEASY_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "DNSMADEEASY_SANDBOX": Activate the sandbox (boolean)`) - ew.writeln(` - "DNSMADEEASY_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/dnsmadeeasy`) - - case "dnspod": - // generated from: providers/dns/dnspod/dnspod.toml - ew.writeln(`Configuration for DNSPod.`) - ew.writeln(`Code: 'dnspod'`) - ew.writeln(`Since: 'v0.4.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "DNSPOD_API_KEY": The user token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "DNSPOD_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "DNSPOD_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "DNSPOD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "DNSPOD_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/dnspod`) - - case "dode": - // generated from: providers/dns/dode/dode.toml - ew.writeln(`Configuration for Domain Offensive (do.de).`) - ew.writeln(`Code: 'dode'`) - ew.writeln(`Since: 'v2.4.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "DODE_TOKEN": API token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "DODE_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "DODE_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "DODE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "DODE_SEQUENCE_INTERVAL": Interval between iteration`) - ew.writeln(` - "DODE_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/dode`) - - case "domeneshop": - // generated from: providers/dns/domeneshop/domeneshop.toml - ew.writeln(`Configuration for Domeneshop.`) - ew.writeln(`Code: 'domeneshop'`) - ew.writeln(`Since: 'v4.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "DOMENESHOP_API_PASSWORD": API secret`) - ew.writeln(` - "DOMENESHOP_API_TOKEN": API token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "DOMENESHOP_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "DOMENESHOP_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "DOMENESHOP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/domeneshop`) - - case "dreamhost": - // generated from: providers/dns/dreamhost/dreamhost.toml - ew.writeln(`Configuration for DreamHost.`) - ew.writeln(`Code: 'dreamhost'`) - ew.writeln(`Since: 'v1.1.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "DREAMHOST_API_KEY": The API key`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "DREAMHOST_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "DREAMHOST_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "DREAMHOST_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "DREAMHOST_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/dreamhost`) - - case "duckdns": - // generated from: providers/dns/duckdns/duckdns.toml - ew.writeln(`Configuration for Duck DNS.`) - ew.writeln(`Code: 'duckdns'`) - ew.writeln(`Since: 'v0.5.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "DUCKDNS_TOKEN": Account token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "DUCKDNS_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "DUCKDNS_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "DUCKDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "DUCKDNS_SEQUENCE_INTERVAL": Interval between iteration`) - ew.writeln(` - "DUCKDNS_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/duckdns`) - - case "dyn": - // generated from: providers/dns/dyn/dyn.toml - ew.writeln(`Configuration for Dyn.`) - ew.writeln(`Code: 'dyn'`) - ew.writeln(`Since: 'v0.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "DYN_CUSTOMER_NAME": Customer name`) - ew.writeln(` - "DYN_PASSWORD": Password`) - ew.writeln(` - "DYN_USER_NAME": User name`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "DYN_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "DYN_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "DYN_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "DYN_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/dyn`) - - case "dynu": - // generated from: providers/dns/dynu/dynu.toml - ew.writeln(`Configuration for Dynu.`) - ew.writeln(`Code: 'dynu'`) - ew.writeln(`Since: 'v3.5.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "DYNU_API_KEY": API key`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "DYNU_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "DYNU_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "DYNU_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "DYNU_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/dynu`) - - case "easydns": - // generated from: providers/dns/easydns/easydns.toml - ew.writeln(`Configuration for EasyDNS.`) - ew.writeln(`Code: 'easydns'`) - ew.writeln(`Since: 'v2.6.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "EASYDNS_KEY": API Key`) - ew.writeln(` - "EASYDNS_TOKEN": API Token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "EASYDNS_ENDPOINT": The endpoint URL of the API Server`) - ew.writeln(` - "EASYDNS_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "EASYDNS_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "EASYDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "EASYDNS_SEQUENCE_INTERVAL": Time between sequential requests`) - ew.writeln(` - "EASYDNS_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/easydns`) - - case "edgedns": - // generated from: providers/dns/edgedns/edgedns.toml - ew.writeln(`Configuration for Akamai EdgeDNS.`) - ew.writeln(`Code: 'edgedns'`) - ew.writeln(`Since: 'v3.9.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "AKAMAI_ACCESS_TOKEN": Access token, managed by the Akamai EdgeGrid client`) - ew.writeln(` - "AKAMAI_CLIENT_SECRET": Client secret, managed by the Akamai EdgeGrid client`) - ew.writeln(` - "AKAMAI_CLIENT_TOKEN": Client token, managed by the Akamai EdgeGrid client`) - ew.writeln(` - "AKAMAI_EDGERC": Path to the .edgerc file, managed by the Akamai EdgeGrid client`) - ew.writeln(` - "AKAMAI_EDGERC_SECTION": Configuration section, managed by the Akamai EdgeGrid client`) - ew.writeln(` - "AKAMAI_HOST": API host, managed by the Akamai EdgeGrid client`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "AKAMAI_POLLING_INTERVAL": Time between DNS propagation check. Default: 15 seconds`) - ew.writeln(` - "AKAMAI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation. Default: 3 minutes`) - ew.writeln(` - "AKAMAI_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/edgedns`) - - case "exec": - // generated from: providers/dns/exec/exec.toml - ew.writeln(`Configuration for External program.`) - ew.writeln(`Code: 'exec'`) - ew.writeln(`Since: 'v0.5.0'`) - ew.writeln() - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/exec`) - - case "exoscale": - // generated from: providers/dns/exoscale/exoscale.toml - ew.writeln(`Configuration for Exoscale.`) - ew.writeln(`Code: 'exoscale'`) - ew.writeln(`Since: 'v0.4.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "EXOSCALE_API_KEY": API key`) - ew.writeln(` - "EXOSCALE_API_SECRET": API secret`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "EXOSCALE_ENDPOINT": API endpoint URL`) - ew.writeln(` - "EXOSCALE_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "EXOSCALE_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "EXOSCALE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "EXOSCALE_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/exoscale`) - - case "gandi": - // generated from: providers/dns/gandi/gandi.toml - ew.writeln(`Configuration for Gandi.`) - ew.writeln(`Code: 'gandi'`) - ew.writeln(`Since: 'v0.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "GANDI_API_KEY": API key`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "GANDI_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "GANDI_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "GANDI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "GANDI_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/gandi`) - - case "gandiv5": - // generated from: providers/dns/gandiv5/gandiv5.toml - ew.writeln(`Configuration for Gandi Live DNS (v5).`) - ew.writeln(`Code: 'gandiv5'`) - ew.writeln(`Since: 'v0.5.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "GANDIV5_API_KEY": API key`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "GANDIV5_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "GANDIV5_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "GANDIV5_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "GANDIV5_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/gandiv5`) - - case "gcloud": - // generated from: providers/dns/gcloud/gcloud.toml - ew.writeln(`Configuration for Google Cloud.`) - ew.writeln(`Code: 'gcloud'`) - ew.writeln(`Since: 'v0.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "Application Default Credentials": [Documentation](https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application)`) - ew.writeln(` - "GCE_PROJECT": Project name (by default, the project name is auto-detected by using the metadata service)`) - ew.writeln(` - "GCE_SERVICE_ACCOUNT": Account`) - ew.writeln(` - "GCE_SERVICE_ACCOUNT_FILE": Account file path`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "GCE_ALLOW_PRIVATE_ZONE": Allows requested domain to be in private DNS zone, works only with a private ACME server (by default: false)`) - ew.writeln(` - "GCE_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "GCE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "GCE_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/gcloud`) - - case "glesys": - // generated from: providers/dns/glesys/glesys.toml - ew.writeln(`Configuration for Glesys.`) - ew.writeln(`Code: 'glesys'`) - ew.writeln(`Since: 'v0.5.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "GLESYS_API_KEY": API key`) - ew.writeln(` - "GLESYS_API_USER": API user`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "GLESYS_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "GLESYS_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "GLESYS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "GLESYS_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/glesys`) - - case "godaddy": - // generated from: providers/dns/godaddy/godaddy.toml - ew.writeln(`Configuration for Go Daddy.`) - ew.writeln(`Code: 'godaddy'`) - ew.writeln(`Since: 'v0.5.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "GODADDY_API_KEY": API key`) - ew.writeln(` - "GODADDY_API_SECRET": API secret`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "GODADDY_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "GODADDY_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "GODADDY_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "GODADDY_SEQUENCE_INTERVAL": Interval between iteration`) - ew.writeln(` - "GODADDY_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/godaddy`) - - case "hetzner": - // generated from: providers/dns/hetzner/hetzner.toml - ew.writeln(`Configuration for Hetzner.`) - ew.writeln(`Code: 'hetzner'`) - ew.writeln(`Since: 'v3.7.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "HETZNER_API_KEY": API key`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "HETZNER_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "HETZNER_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "HETZNER_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "HETZNER_SEQUENCE_INTERVAL": Interval between iteration`) - ew.writeln(` - "HETZNER_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/hetzner`) - - case "hostingde": - // generated from: providers/dns/hostingde/hostingde.toml - ew.writeln(`Configuration for Hosting.de.`) - ew.writeln(`Code: 'hostingde'`) - ew.writeln(`Since: 'v1.1.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "HOSTINGDE_API_KEY": API key`) - ew.writeln(` - "HOSTINGDE_ZONE_NAME": Zone name in ACE format`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "HOSTINGDE_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "HOSTINGDE_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "HOSTINGDE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "HOSTINGDE_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/hostingde`) - - case "httpreq": - // generated from: providers/dns/httpreq/httpreq.toml - ew.writeln(`Configuration for HTTP request.`) - ew.writeln(`Code: 'httpreq'`) - ew.writeln(`Since: 'v2.0.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "HTTPREQ_ENDPOINT": The URL of the server`) - ew.writeln(` - "HTTPREQ_MODE": 'RAW', none`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "HTTPREQ_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "HTTPREQ_PASSWORD": Basic authentication password`) - ew.writeln(` - "HTTPREQ_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "HTTPREQ_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "HTTPREQ_USERNAME": Basic authentication username`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/httpreq`) - - case "hurricane": - // generated from: providers/dns/hurricane/hurricane.toml - ew.writeln(`Configuration for Hurricane Electric DNS.`) - ew.writeln(`Code: 'hurricane'`) - ew.writeln(`Since: 'v4.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "HURRICANE_TOKENS": TXT record names and tokens`) - ew.writeln() - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/hurricane`) - - case "hyperone": - // generated from: providers/dns/hyperone/hyperone.toml - ew.writeln(`Configuration for HyperOne.`) - ew.writeln(`Code: 'hyperone'`) - ew.writeln(`Since: 'v3.9.0'`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "HYPERONE_API_URL": Allows to pass custom API Endpoint to be used in the challenge (default https://api.hyperone.com/v2)`) - ew.writeln(` - "HYPERONE_LOCATION_ID": Specifies location (region) to be used in API calls. (default pl-waw-1)`) - ew.writeln(` - "HYPERONE_PASSPORT_LOCATION": Allows to pass custom passport file location (default ~/.h1/passport.json)`) - ew.writeln(` - "HYPERONE_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "HYPERONE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "HYPERONE_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/hyperone`) - - case "iij": - // generated from: providers/dns/iij/iij.toml - ew.writeln(`Configuration for Internet Initiative Japan.`) - ew.writeln(`Code: 'iij'`) - ew.writeln(`Since: 'v1.1.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "IIJ_API_ACCESS_KEY": API access key`) - ew.writeln(` - "IIJ_API_SECRET_KEY": API secret key`) - ew.writeln(` - "IIJ_DO_SERVICE_CODE": DO service code`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "IIJ_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "IIJ_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "IIJ_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/iij`) - - case "infomaniak": - // generated from: providers/dns/infomaniak/infomaniak.toml - ew.writeln(`Configuration for Infomaniak.`) - ew.writeln(`Code: 'infomaniak'`) - ew.writeln(`Since: 'v4.1.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "INFOMANIAK_ACCESS_TOKEN": Access token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "INFOMANIAK_ENDPOINT": https://api.infomaniak.com`) - ew.writeln(` - "INFOMANIAK_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "INFOMANIAK_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "INFOMANIAK_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "INFOMANIAK_TTL": The TTL of the TXT record used for the DNS challenge in seconds`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/infomaniak`) - - case "inwx": - // generated from: providers/dns/inwx/inwx.toml - ew.writeln(`Configuration for INWX.`) - ew.writeln(`Code: 'inwx'`) - ew.writeln(`Since: 'v2.0.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "INWX_PASSWORD": Password`) - ew.writeln(` - "INWX_USERNAME": Username`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "INWX_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "INWX_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation (default 360s)`) - ew.writeln(` - "INWX_SANDBOX": Activate the sandbox (boolean)`) - ew.writeln(` - "INWX_SHARED_SECRET": shared secret related to 2FA`) - ew.writeln(` - "INWX_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/inwx`) - - case "ionos": - // generated from: providers/dns/ionos/ionos.toml - ew.writeln(`Configuration for Ionos.`) - ew.writeln(`Code: 'ionos'`) - ew.writeln(`Since: 'v4.2.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "IONOS_API_KEY": API key '.' https://developer.hosting.ionos.com/docs/getstarted`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "IONOS_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "IONOS_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "IONOS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "IONOS_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/ionos`) - - case "joker": - // generated from: providers/dns/joker/joker.toml - ew.writeln(`Configuration for Joker.`) - ew.writeln(`Code: 'joker'`) - ew.writeln(`Since: 'v2.6.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "JOKER_API_KEY": API key (only with DMAPI mode)`) - ew.writeln(` - "JOKER_API_MODE": 'DMAPI' or 'SVC'. DMAPI is for resellers accounts. (Default: DMAPI)`) - ew.writeln(` - "JOKER_PASSWORD": Joker.com password`) - ew.writeln(` - "JOKER_USERNAME": Joker.com username`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "JOKER_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "JOKER_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "JOKER_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "JOKER_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/joker`) - - case "lightsail": - // generated from: providers/dns/lightsail/lightsail.toml - ew.writeln(`Configuration for Amazon Lightsail.`) - ew.writeln(`Code: 'lightsail'`) - ew.writeln(`Since: 'v0.5.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "AWS_ACCESS_KEY_ID": Access key ID`) - ew.writeln(` - "AWS_SECRET_ACCESS_KEY": Secret access key`) - ew.writeln(` - "DNS_ZONE": DNS zone`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "LIGHTSAIL_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "LIGHTSAIL_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/lightsail`) - - case "linode": - // generated from: providers/dns/linode/linode.toml - ew.writeln(`Configuration for Linode (v4).`) - ew.writeln(`Code: 'linode'`) - ew.writeln(`Since: 'v1.1.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "LINODE_TOKEN": API token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "LINODE_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "LINODE_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "LINODE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "LINODE_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/linode`) - - case "liquidweb": - // generated from: providers/dns/liquidweb/liquidweb.toml - ew.writeln(`Configuration for Liquid Web.`) - ew.writeln(`Code: 'liquidweb'`) - ew.writeln(`Since: 'v3.1.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "LIQUID_WEB_PASSWORD": Storm API Password`) - ew.writeln(` - "LIQUID_WEB_USERNAME": Storm API Username`) - ew.writeln(` - "LIQUID_WEB_ZONE": DNS Zone`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "LIQUID_WEB_HTTP_TIMEOUT": Maximum waiting time for the DNS records to be created (not verified)`) - ew.writeln(` - "LIQUID_WEB_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "LIQUID_WEB_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "LIQUID_WEB_TTL": The TTL of the TXT record used for the DNS challenge`) - ew.writeln(` - "LIQUID_WEB_URL": Storm API endpoint`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/liquidweb`) - - case "loopia": - // generated from: providers/dns/loopia/loopia.toml - ew.writeln(`Configuration for Loopia.`) - ew.writeln(`Code: 'loopia'`) - ew.writeln(`Since: 'v4.2.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "LOOPIA_API_PASSWORD": API password`) - ew.writeln(` - "LOOPIA_API_USER": API username`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "LOOPIA_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "LOOPIA_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "LOOPIA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "LOOPIA_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/loopia`) - - case "luadns": - // generated from: providers/dns/luadns/luadns.toml - ew.writeln(`Configuration for LuaDNS.`) - ew.writeln(`Code: 'luadns'`) - ew.writeln(`Since: 'v3.7.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "LUADNS_API_TOKEN": API token`) - ew.writeln(` - "LUADNS_API_USERNAME": Username (your email)`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "LUADNS_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "LUADNS_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "LUADNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "LUADNS_SEQUENCE_INTERVAL": Interval between iteration`) - ew.writeln(` - "LUADNS_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/luadns`) - - case "mydnsjp": - // generated from: providers/dns/mydnsjp/mydnsjp.toml - ew.writeln(`Configuration for MyDNS.jp.`) - ew.writeln(`Code: 'mydnsjp'`) - ew.writeln(`Since: 'v1.2.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "MYDNSJP_MASTER_ID": Master ID`) - ew.writeln(` - "MYDNSJP_PASSWORD": Password`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "MYDNSJP_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "MYDNSJP_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "MYDNSJP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "MYDNSJP_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/mydnsjp`) - - case "mythicbeasts": - // generated from: providers/dns/mythicbeasts/mythicbeasts.toml - ew.writeln(`Configuration for MythicBeasts.`) - ew.writeln(`Code: 'mythicbeasts'`) - ew.writeln(`Since: 'v0.3.7'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "MYTHICBEASTS_PASSWORD": Password`) - ew.writeln(` - "MYTHICBEASTS_USERNAME": User name`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "MYTHICBEASTS_API_ENDPOINT": The endpoint for the API (must implement v2)`) - ew.writeln(` - "MYTHICBEASTS_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "MYTHICBEASTS_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "MYTHICBEASTS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "MYTHICBEASTS_TTL": The TTL of the TXT record used for the DNS challenge`) - ew.writeln(` - "MYTHICBEASYS_AUTH_API_ENDPOINT": The endpoint for Mythic Beasts' Authentication`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/mythicbeasts`) - - case "namecheap": - // generated from: providers/dns/namecheap/namecheap.toml - ew.writeln(`Configuration for Namecheap.`) - ew.writeln(`Code: 'namecheap'`) - ew.writeln(`Since: 'v0.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "NAMECHEAP_API_KEY": API key`) - ew.writeln(` - "NAMECHEAP_API_USER": API user`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "NAMECHEAP_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "NAMECHEAP_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "NAMECHEAP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "NAMECHEAP_SANDBOX": Activate the sandbox (boolean)`) - ew.writeln(` - "NAMECHEAP_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/namecheap`) - - case "namedotcom": - // generated from: providers/dns/namedotcom/namedotcom.toml - ew.writeln(`Configuration for Name.com.`) - ew.writeln(`Code: 'namedotcom'`) - ew.writeln(`Since: 'v0.5.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "NAMECOM_API_TOKEN": API token`) - ew.writeln(` - "NAMECOM_USERNAME": Username`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "NAMECOM_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "NAMECOM_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "NAMECOM_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "NAMECOM_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/namedotcom`) - - case "namesilo": - // generated from: providers/dns/namesilo/namesilo.toml - ew.writeln(`Configuration for Namesilo.`) - ew.writeln(`Code: 'namesilo'`) - ew.writeln(`Since: 'v2.7.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "NAMESILO_API_KEY": Client ID`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "NAMESILO_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "NAMESILO_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation, it is better to set larger than 15m`) - ew.writeln(` - "NAMESILO_TTL": The TTL of the TXT record used for the DNS challenge, should be in [3600, 2592000]`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/namesilo`) - - case "netcup": - // generated from: providers/dns/netcup/netcup.toml - ew.writeln(`Configuration for Netcup.`) - ew.writeln(`Code: 'netcup'`) - ew.writeln(`Since: 'v1.1.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "NETCUP_API_KEY": API key`) - ew.writeln(` - "NETCUP_API_PASSWORD": API password`) - ew.writeln(` - "NETCUP_CUSTOMER_NUMBER": Customer number`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "NETCUP_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "NETCUP_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "NETCUP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "NETCUP_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/netcup`) - - case "netlify": - // generated from: providers/dns/netlify/netlify.toml - ew.writeln(`Configuration for Netlify.`) - ew.writeln(`Code: 'netlify'`) - ew.writeln(`Since: 'v3.7.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "NETLIFY_TOKEN": Token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "NETLIFY_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "NETLIFY_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "NETLIFY_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "NETLIFY_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/netlify`) - - case "nifcloud": - // generated from: providers/dns/nifcloud/nifcloud.toml - ew.writeln(`Configuration for NIFCloud.`) - ew.writeln(`Code: 'nifcloud'`) - ew.writeln(`Since: 'v1.1.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "NIFCLOUD_ACCESS_KEY_ID": Access key`) - ew.writeln(` - "NIFCLOUD_SECRET_ACCESS_KEY": Secret access key`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "NIFCLOUD_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "NIFCLOUD_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "NIFCLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "NIFCLOUD_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/nifcloud`) - - case "njalla": - // generated from: providers/dns/njalla/njalla.toml - ew.writeln(`Configuration for Njalla.`) - ew.writeln(`Code: 'njalla'`) - ew.writeln(`Since: 'v4.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "NJALLA_TOKEN": API token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "NJALLA_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "NJALLA_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "NJALLA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "NJALLA_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/njalla`) - - case "ns1": - // generated from: providers/dns/ns1/ns1.toml - ew.writeln(`Configuration for NS1.`) - ew.writeln(`Code: 'ns1'`) - ew.writeln(`Since: 'v0.4.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "NS1_API_KEY": API key`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "NS1_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "NS1_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "NS1_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "NS1_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/ns1`) - - case "oraclecloud": - // generated from: providers/dns/oraclecloud/oraclecloud.toml - ew.writeln(`Configuration for Oracle Cloud.`) - ew.writeln(`Code: 'oraclecloud'`) - ew.writeln(`Since: 'v2.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "OCI_COMPARTMENT_OCID": Compartment OCID`) - ew.writeln(` - "OCI_PRIVKEY_FILE": Private key file`) - ew.writeln(` - "OCI_PRIVKEY_PASS": Private key password`) - ew.writeln(` - "OCI_PUBKEY_FINGERPRINT": Public key fingerprint`) - ew.writeln(` - "OCI_REGION": Region`) - ew.writeln(` - "OCI_TENANCY_OCID": Tenancy OCID`) - ew.writeln(` - "OCI_USER_OCID": User OCID`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "OCI_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "OCI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "OCI_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/oraclecloud`) - - case "otc": - // generated from: providers/dns/otc/otc.toml - ew.writeln(`Configuration for Open Telekom Cloud.`) - ew.writeln(`Code: 'otc'`) - ew.writeln(`Since: 'v0.4.1'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "OTC_DOMAIN_NAME": Domain name`) - ew.writeln(` - "OTC_IDENTITY_ENDPOINT": Identity endpoint URL`) - ew.writeln(` - "OTC_PASSWORD": Password`) - ew.writeln(` - "OTC_PROJECT_NAME": Project name`) - ew.writeln(` - "OTC_USER_NAME": User name`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "OTC_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "OTC_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "OTC_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "OTC_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/otc`) - - case "ovh": - // generated from: providers/dns/ovh/ovh.toml - ew.writeln(`Configuration for OVH.`) - ew.writeln(`Code: 'ovh'`) - ew.writeln(`Since: 'v0.4.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "OVH_APPLICATION_KEY": Application key`) - ew.writeln(` - "OVH_APPLICATION_SECRET": Application secret`) - ew.writeln(` - "OVH_CONSUMER_KEY": Consumer key`) - ew.writeln(` - "OVH_ENDPOINT": Endpoint URL (ovh-eu or ovh-ca)`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "OVH_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "OVH_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "OVH_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "OVH_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/ovh`) - - case "pdns": - // generated from: providers/dns/pdns/pdns.toml - ew.writeln(`Configuration for PowerDNS.`) - ew.writeln(`Code: 'pdns'`) - ew.writeln(`Since: 'v0.4.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "PDNS_API_KEY": API key`) - ew.writeln(` - "PDNS_API_URL": API URL`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "PDNS_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "PDNS_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "PDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "PDNS_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/pdns`) - - case "rackspace": - // generated from: providers/dns/rackspace/rackspace.toml - ew.writeln(`Configuration for Rackspace.`) - ew.writeln(`Code: 'rackspace'`) - ew.writeln(`Since: 'v0.4.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "RACKSPACE_API_KEY": API key`) - ew.writeln(` - "RACKSPACE_USER": API user`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "RACKSPACE_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "RACKSPACE_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "RACKSPACE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "RACKSPACE_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/rackspace`) - - case "regru": - // generated from: providers/dns/regru/regru.toml - ew.writeln(`Configuration for reg.ru.`) - ew.writeln(`Code: 'regru'`) - ew.writeln(`Since: 'v3.5.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "REGRU_PASSWORD": API password`) - ew.writeln(` - "REGRU_USERNAME": API username`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "REGRU_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "REGRU_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "REGRU_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "REGRU_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/regru`) - - case "rfc2136": - // generated from: providers/dns/rfc2136/rfc2136.toml - ew.writeln(`Configuration for RFC2136.`) - ew.writeln(`Code: 'rfc2136'`) - ew.writeln(`Since: 'v0.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "RFC2136_NAMESERVER": Network address in the form "host" or "host:port"`) - ew.writeln(` - "RFC2136_TSIG_ALGORITHM": TSIG algorithm. See [miekg/dns#tsig.go](https://github.com/miekg/dns/blob/master/tsig.go) for supported values. To disable TSIG authentication, leave the 'RFC2136_TSIG*' variables unset.`) - ew.writeln(` - "RFC2136_TSIG_KEY": Name of the secret key as defined in DNS server configuration. To disable TSIG authentication, leave the 'RFC2136_TSIG*' variables unset.`) - ew.writeln(` - "RFC2136_TSIG_SECRET": Secret key payload. To disable TSIG authentication, leave the' RFC2136_TSIG*' variables unset.`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "RFC2136_DNS_TIMEOUT": API request timeout`) - ew.writeln(` - "RFC2136_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "RFC2136_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "RFC2136_SEQUENCE_INTERVAL": Interval between iteration`) - ew.writeln(` - "RFC2136_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/rfc2136`) - - case "rimuhosting": - // generated from: providers/dns/rimuhosting/rimuhosting.toml - ew.writeln(`Configuration for RimuHosting.`) - ew.writeln(`Code: 'rimuhosting'`) - ew.writeln(`Since: 'v0.3.5'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "RIMUHOSTING_API_KEY": User API key`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "RIMUHOSTING_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "RIMUHOSTING_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "RIMUHOSTING_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "RIMUHOSTING_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/rimuhosting`) - - case "route53": - // generated from: providers/dns/route53/route53.toml - ew.writeln(`Configuration for Amazon Route 53.`) - ew.writeln(`Code: 'route53'`) - ew.writeln(`Since: 'v0.3.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "AWS_ACCESS_KEY_ID": Managed by the AWS client ('AWS_ACCESS_KEY_ID_FILE' is not supported)`) - ew.writeln(` - "AWS_HOSTED_ZONE_ID": Override the hosted zone ID`) - ew.writeln(` - "AWS_PROFILE": Managed by the AWS client ('AWS_PROFILE_FILE' is not supported)`) - ew.writeln(` - "AWS_REGION": Managed by the AWS client ('AWS_REGION_FILE' is not supported)`) - ew.writeln(` - "AWS_SDK_LOAD_CONFIG": Retrieve the region from the CLI config file ('AWS_SDK_LOAD_CONFIG_FILE' is not supported)`) - ew.writeln(` - "AWS_SECRET_ACCESS_KEY": Managed by the AWS client ('AWS_SECRET_ACCESS_KEY_FILE' is not supported)`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "AWS_MAX_RETRIES": The number of maximum returns the service will use to make an individual API request`) - ew.writeln(` - "AWS_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "AWS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "AWS_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/route53`) - - case "sakuracloud": - // generated from: providers/dns/sakuracloud/sakuracloud.toml - ew.writeln(`Configuration for Sakura Cloud.`) - ew.writeln(`Code: 'sakuracloud'`) - ew.writeln(`Since: 'v1.1.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "SAKURACLOUD_ACCESS_TOKEN": Access token`) - ew.writeln(` - "SAKURACLOUD_ACCESS_TOKEN_SECRET": Access token secret`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "SAKURACLOUD_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "SAKURACLOUD_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "SAKURACLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "SAKURACLOUD_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/sakuracloud`) - - case "scaleway": - // generated from: providers/dns/scaleway/scaleway.toml - ew.writeln(`Configuration for Scaleway.`) - ew.writeln(`Code: 'scaleway'`) - ew.writeln(`Since: 'v3.4.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "SCALEWAY_API_TOKEN": API token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "SCALEWAY_API_VERSION": API version`) - ew.writeln(` - "SCALEWAY_BASE_URL": API endpoint URL`) - ew.writeln(` - "SCALEWAY_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "SCALEWAY_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "SCALEWAY_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "SCALEWAY_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/scaleway`) - - case "selectel": - // generated from: providers/dns/selectel/selectel.toml - ew.writeln(`Configuration for Selectel.`) - ew.writeln(`Code: 'selectel'`) - ew.writeln(`Since: 'v1.2.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "SELECTEL_API_TOKEN": API token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "SELECTEL_BASE_URL": API endpoint URL`) - ew.writeln(` - "SELECTEL_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "SELECTEL_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "SELECTEL_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "SELECTEL_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/selectel`) - - case "servercow": - // generated from: providers/dns/servercow/servercow.toml - ew.writeln(`Configuration for Servercow.`) - ew.writeln(`Code: 'servercow'`) - ew.writeln(`Since: 'v3.4.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "SERVERCOW_PASSWORD": API password`) - ew.writeln(` - "SERVERCOW_USERNAME": API username`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "SERVERCOW_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "SERVERCOW_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "SERVERCOW_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "SERVERCOW_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/servercow`) - - case "stackpath": - // generated from: providers/dns/stackpath/stackpath.toml - ew.writeln(`Configuration for Stackpath.`) - ew.writeln(`Code: 'stackpath'`) - ew.writeln(`Since: 'v1.1.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "STACKPATH_CLIENT_ID": Client ID`) - ew.writeln(` - "STACKPATH_CLIENT_SECRET": Client secret`) - ew.writeln(` - "STACKPATH_STACK_ID": Stack ID`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "STACKPATH_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "STACKPATH_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "STACKPATH_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/stackpath`) - - case "transip": - // generated from: providers/dns/transip/transip.toml - ew.writeln(`Configuration for TransIP.`) - ew.writeln(`Code: 'transip'`) - ew.writeln(`Since: 'v2.0.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "TRANSIP_ACCOUNT_NAME": Account name`) - ew.writeln(` - "TRANSIP_PRIVATE_KEY_PATH": Private key path`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "TRANSIP_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "TRANSIP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "TRANSIP_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/transip`) - - case "vegadns": - // generated from: providers/dns/vegadns/vegadns.toml - ew.writeln(`Configuration for VegaDNS.`) - ew.writeln(`Code: 'vegadns'`) - ew.writeln(`Since: 'v1.1.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "SECRET_VEGADNS_KEY": API key`) - ew.writeln(` - "SECRET_VEGADNS_SECRET": API secret`) - ew.writeln(` - "VEGADNS_URL": API endpoint URL`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "VEGADNS_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "VEGADNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "VEGADNS_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/vegadns`) - - case "versio": - // generated from: providers/dns/versio/versio.toml - ew.writeln(`Configuration for Versio.[nl|eu|uk].`) - ew.writeln(`Code: 'versio'`) - ew.writeln(`Since: 'v2.7.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "VERSIO_PASSWORD": Basic authentication password`) - ew.writeln(` - "VERSIO_USERNAME": Basic authentication username`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "VERSIO_ENDPOINT": The endpoint URL of the API Server`) - ew.writeln(` - "VERSIO_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "VERSIO_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "VERSIO_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "VERSIO_SEQUENCE_INTERVAL": Interval between iteration, default 60s`) - ew.writeln(` - "VERSIO_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/versio`) - - case "vscale": - // generated from: providers/dns/vscale/vscale.toml - ew.writeln(`Configuration for Vscale.`) - ew.writeln(`Code: 'vscale'`) - ew.writeln(`Since: 'v2.0.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "VSCALE_API_TOKEN": API token`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "VSCALE_BASE_URL": API endpoint URL`) - ew.writeln(` - "VSCALE_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "VSCALE_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "VSCALE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "VSCALE_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/vscale`) - - case "vultr": - // generated from: providers/dns/vultr/vultr.toml - ew.writeln(`Configuration for Vultr.`) - ew.writeln(`Code: 'vultr'`) - ew.writeln(`Since: 'v0.3.1'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "VULTR_API_KEY": API key`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "VULTR_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "VULTR_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "VULTR_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "VULTR_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/vultr`) - - case "yandex": - // generated from: providers/dns/yandex/yandex.toml - ew.writeln(`Configuration for Yandex.`) - ew.writeln(`Code: 'yandex'`) - ew.writeln(`Since: 'v3.7.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "YANDEX_PDD_TOKEN": Basic authentication username`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "YANDEX_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "YANDEX_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "YANDEX_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "YANDEX_SEQUENCE_INTERVAL": Interval between iteration, default 60s`) - ew.writeln(` - "YANDEX_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/yandex`) - - case "zoneee": - // generated from: providers/dns/zoneee/zoneee.toml - ew.writeln(`Configuration for Zone.ee.`) - ew.writeln(`Code: 'zoneee'`) - ew.writeln(`Since: 'v2.1.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "ZONEEE_API_KEY": API key`) - ew.writeln(` - "ZONEEE_API_USER": API user`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "ZONEEE_ENDPOINT": API endpoint URL`) - ew.writeln(` - "ZONEEE_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "ZONEEE_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "ZONEEE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "ZONEEE_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/zoneee`) - - case "zonomi": - // generated from: providers/dns/zonomi/zonomi.toml - ew.writeln(`Configuration for Zonomi.`) - ew.writeln(`Code: 'zonomi'`) - ew.writeln(`Since: 'v3.5.0'`) - ew.writeln() - - ew.writeln(`Credentials:`) - ew.writeln(` - "ZONOMI_API_KEY": User API key`) - ew.writeln() - - ew.writeln(`Additional Configuration:`) - ew.writeln(` - "ZONOMI_HTTP_TIMEOUT": API request timeout`) - ew.writeln(` - "ZONOMI_POLLING_INTERVAL": Time between DNS propagation check`) - ew.writeln(` - "ZONOMI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) - ew.writeln(` - "ZONOMI_TTL": The TTL of the TXT record used for the DNS challenge`) - - ew.writeln() - ew.writeln(`More information: https://go-acme.github.io/lego/dns/zonomi`) - - case "manual": - ew.writeln(`Solving the DNS-01 challenge using CLI prompt.`) - default: - return fmt.Errorf("%q is not yet supported", name) - } - - if ew.err != nil { - return fmt.Errorf("error: %w", ew.err) - } - - return w.Flush() -} diff --git a/node/controller/legoCmd/lego.go b/node/controller/legoCmd/lego.go deleted file mode 100644 index 4490475..0000000 --- a/node/controller/legoCmd/lego.go +++ /dev/null @@ -1,189 +0,0 @@ -// Package legoCmd Let's Encrypt client to go! -// CLI application for generating Let's Encrypt certificates using the ACME package. -package legoCmd - -import ( - "errors" - "fmt" - "github.com/Yuzuki616/V2bX/node/controller/legoCmd/cmd" - "os" - "path" - "path/filepath" - "runtime" - "strings" - - "github.com/urfave/cli" -) - -var version = "dev" -var defaultPath string - -type LegoCMD struct { - cmdClient *cli.App -} - -func New() (*LegoCMD, error) { - app := cli.NewApp() - app.Name = "lego" - app.HelpName = "lego" - app.Usage = "Let's Encrypt client written in Go" - app.EnableBashCompletion = true - - app.Version = version - cli.VersionPrinter = func(c *cli.Context) { - fmt.Printf("lego version %s %s/%s\n", c.App.Version, runtime.GOOS, runtime.GOARCH) - } - - // Set default pathTemp to configPath/cert - var pathTemp = "" - configPath := os.Getenv("XRAY_LOCATION_CONFIG") - if configPath != "" { - pathTemp = configPath - } else if cwd, err := os.Getwd(); err == nil { - pathTemp = cwd - } else { - pathTemp = "." - } - - defaultPath = filepath.Join(pathTemp, "cert") - - app.Flags = cmd.CreateFlags(defaultPath) - - app.Before = cmd.Before - - app.Commands = cmd.CreateCommands() - - lego := &LegoCMD{ - cmdClient: app, - } - - return lego, nil -} - -// DNSCert cert a domain using DNS API -func (l *LegoCMD) DNSCert(domain, email, provider string, DNSEnv map[string]string) (CertPath string, KeyPath string, err error) { - defer func() (string, string, error) { - // Handle any error - if r := recover(); r != nil { - switch x := r.(type) { - case string: - err = errors.New(x) - case error: - err = x - default: - err = errors.New("unknow panic") - } - return "", "", err - } - return CertPath, KeyPath, nil - }() - // Set Env for DNS configuration - for key, value := range DNSEnv { - os.Setenv(key, value) - } - // First check if the certificate exists - CertPath, KeyPath, err = checkCertfile(domain) - if err == nil { - return CertPath, KeyPath, err - } - - argstring := fmt.Sprintf("lego -a -d %s -m %s --dns %s run", domain, email, provider) - err = l.cmdClient.Run(strings.Split(argstring, " ")) - if err != nil { - return "", "", err - } - CertPath, KeyPath, err = checkCertfile(domain) - if err != nil { - return "", "", err - } - return CertPath, KeyPath, nil -} - -// HTTPCert cert a domain using http methods -func (l *LegoCMD) HTTPCert(domain, email string) (CertPath string, KeyPath string, err error) { - defer func() (string, string, error) { - // Handle any error - if r := recover(); r != nil { - switch x := r.(type) { - case string: - err = errors.New(x) - case error: - err = x - default: - err = errors.New("unknow panic") - } - return "", "", err - } - return CertPath, KeyPath, nil - }() - // First check if the certificate exists - CertPath, KeyPath, err = checkCertfile(domain) - if err == nil { - return CertPath, KeyPath, err - } - argString := fmt.Sprintf("lego -a -d %s -m %s --http run", domain, email) - err = l.cmdClient.Run(strings.Split(argString, " ")) - - if err != nil { - return "", "", err - } - CertPath, KeyPath, err = checkCertfile(domain) - if err != nil { - return "", "", err - } - return CertPath, KeyPath, nil -} - -// RenewCert renew a domain cert -func (l *LegoCMD) RenewCert(domain, email, certMode, provider string, DNSEnv map[string]string) (CertPath string, KeyPath string, err error) { - var argstring string - defer func() (string, string, error) { - // Handle any error - if r := recover(); r != nil { - switch x := r.(type) { - case string: - err = errors.New(x) - case error: - err = x - default: - err = errors.New("unknown panic") - } - return "", "", err - } - return CertPath, KeyPath, nil - }() - if certMode == "http" { - argstring = fmt.Sprintf("lego -a -d %s -m %s --http renew --days 30", domain, email) - } else if certMode == "dns" { - // Set Env for DNS configuration - for key, value := range DNSEnv { - os.Setenv(key, value) - } - argstring = fmt.Sprintf("lego -a -d %s -m %s --dns %s renew --days 30", domain, email, provider) - } else { - return "", "", fmt.Errorf("unsupport cert mode: %s", certMode) - } - err = l.cmdClient.Run(strings.Split(argstring, " ")) - - if err != nil { - return "", "", err - } - CertPath, KeyPath, err = checkCertfile(domain) - if err != nil { - return "", "", err - } - return CertPath, KeyPath, nil -} -func checkCertfile(domain string) (string, string, error) { - keyPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.key", domain)) - certPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.crt", domain)) - if _, err := os.Stat(keyPath); os.IsNotExist(err) { - return "", "", fmt.Errorf("cert key failed: %s", domain) - } - if _, err := os.Stat(certPath); os.IsNotExist(err) { - return "", "", fmt.Errorf("cert cert failed: %s", domain) - } - absKeyPath, _ := filepath.Abs(keyPath) - absCertPath, _ := filepath.Abs(certPath) - return absCertPath, absKeyPath, nil -} diff --git a/node/controller/legoCmd/lego_test.go b/node/controller/legoCmd/lego_test.go deleted file mode 100644 index fa50c8f..0000000 --- a/node/controller/legoCmd/lego_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package legoCmd_test - -import ( - "github.com/Yuzuki616/V2bX/node/controller/legoCmd" - "testing" -) - -func TestLegoClient(t *testing.T) { - _, err := legoCmd.New() - if err != nil { - t.Error(err) - } -} - -func TestLegoDNSCert(t *testing.T) { - lego, err := legoCmd.New() - if err != nil { - t.Error(err) - } - var ( - domain string = "node1.test.com" - email string = "test@gmail.com" - provider string = "alidns" - DNSEnv map[string]string - ) - DNSEnv = make(map[string]string) - DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa" - DNSEnv["ALICLOUD_SECRET_KEY"] = "bbb" - certPath, keyPath, err := lego.DNSCert(domain, email, provider, DNSEnv) - if err != nil { - t.Error(err) - } - t.Log(certPath) - t.Log(keyPath) -} - -func TestLegoHTTPCert(t *testing.T) { - lego, err := legoCmd.New() - if err != nil { - t.Error(err) - } - var ( - domain string = "node1.test.com" - email string = "test@gmail.com" - ) - certPath, keyPath, err := lego.HTTPCert(domain, email) - if err != nil { - t.Error(err) - } - t.Log(certPath) - t.Log(keyPath) -} - -func TestLegoRenewCert(t *testing.T) { - lego, err := legoCmd.New() - if err != nil { - t.Error(err) - } - var ( - domain string = "node1.test.com" - email string = "test@gmail.com" - provider string = "alidns" - DNSEnv map[string]string - ) - DNSEnv = make(map[string]string) - DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa" - DNSEnv["ALICLOUD_SECRET_KEY"] = "bbb" - certPath, keyPath, err := lego.RenewCert(domain, email, "dns", provider, DNSEnv) - if err != nil { - t.Error(err) - } - t.Log(certPath) - t.Log(keyPath) - - certPath, keyPath, err = lego.RenewCert(domain, email, "http", provider, DNSEnv) - if err != nil { - t.Error(err) - } - t.Log(certPath) - t.Log(keyPath) -} diff --git a/node/controller/legoCmd/log/log.go b/node/controller/legoCmd/log/log.go deleted file mode 100644 index 35d5c93..0000000 --- a/node/controller/legoCmd/log/log.go +++ /dev/null @@ -1,59 +0,0 @@ -package log - -import ( - "log" - "os" -) - -// Logger is an optional custom logger. -var Logger StdLogger = log.New(os.Stdout, "", log.LstdFlags) - -// StdLogger interface for Standard Logger. -type StdLogger interface { - Panic(args ...interface{}) - Fatalln(args ...interface{}) - Panicf(format string, args ...interface{}) - Print(args ...interface{}) - Println(args ...interface{}) - Printf(format string, args ...interface{}) -} - -// Panic writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Panic(args ...interface{}) { - Logger.Panic(args...) -} - -// Panicf writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Panicf(format string, args ...interface{}) { - Logger.Panicf(format, args...) -} - -// Print writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Print(args ...interface{}) { - Logger.Print(args...) -} - -// Println writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Println(args ...interface{}) { - Logger.Println(args...) -} - -// Printf writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Printf(format string, args ...interface{}) { - Logger.Printf(format, args...) -} - -// Warnf writes a log entry. -func Warnf(format string, args ...interface{}) { - Printf("[WARN] "+format, args...) -} - -// Infof writes a log entry. -func Infof(format string, args ...interface{}) { - Printf("[INFO] "+format, args...) -} diff --git a/node/controller/task.go b/node/controller/task.go index dde3321..de368c5 100644 --- a/node/controller/task.go +++ b/node/controller/task.go @@ -2,15 +2,87 @@ package controller import ( "fmt" + "github.com/Yuzuki616/V2bX/api/iprecoder" "github.com/Yuzuki616/V2bX/api/panel" - "github.com/Yuzuki616/V2bX/node/controller/legoCmd" + "github.com/Yuzuki616/V2bX/node/controller/lego" "github.com/xtls/xray-core/common/protocol" + "github.com/xtls/xray-core/common/task" "log" "runtime" "strconv" "time" ) +func (c *Node) initTask() { + // fetch node info task + c.nodeInfoMonitorPeriodic = &task.Periodic{ + Interval: time.Duration(c.nodeInfo.BaseConfig.PullInterval.(int)) * time.Second, + Execute: c.nodeInfoMonitor, + } + // fetch user list task + c.userReportPeriodic = &task.Periodic{ + Interval: time.Duration(c.nodeInfo.BaseConfig.PushInterval.(int)) * time.Second, + Execute: c.reportUserTraffic, + } + log.Printf("[%s: %d] Start monitor node status", c.nodeInfo.NodeType, c.nodeInfo.NodeId) + // delay to start nodeInfoMonitor + go func() { + time.Sleep(time.Duration(c.nodeInfo.BaseConfig.PullInterval.(int)) * time.Second) + _ = c.nodeInfoMonitorPeriodic.Start() + }() + log.Printf("[%s: %d] Start report node status", c.nodeInfo.NodeType, c.nodeInfo.NodeId) + // delay to start userReport + go func() { + time.Sleep(time.Duration(c.nodeInfo.BaseConfig.PushInterval.(int)) * time.Second) + _ = c.userReportPeriodic.Start() + }() + if c.nodeInfo.EnableTls && c.CertConfig.CertMode != "none" && + (c.CertConfig.CertMode == "dns" || c.CertConfig.CertMode == "http") { + c.renewCertPeriodic = &task.Periodic{ + Interval: time.Hour * 24, + Execute: c.reportUserTraffic, + } + log.Printf("[%s: %d] Start renew cert", c.nodeInfo.NodeType, c.nodeInfo.NodeId) + // delay to start renewCert + go func() { + _ = c.renewCertPeriodic.Start() + }() + } + if c.EnableDynamicSpeedLimit { + // Check dynamic speed limit task + c.dynamicSpeedLimitPeriodic = &task.Periodic{ + Interval: time.Duration(c.DynamicSpeedLimitConfig.Periodic) * time.Second, + Execute: c.dynamicSpeedLimit, + } + go func() { + time.Sleep(time.Duration(c.DynamicSpeedLimitConfig.Periodic) * time.Second) + _ = c.dynamicSpeedLimitPeriodic.Start() + }() + log.Printf("[%s: %d] Start dynamic speed limit", c.nodeInfo.NodeType, c.nodeInfo.NodeId) + } + if c.EnableIpRecorder { + switch c.IpRecorderConfig.Type { + case "Recorder": + c.ipRecorder = iprecoder.NewRecorder(c.IpRecorderConfig.RecorderConfig) + case "Redis": + c.ipRecorder = iprecoder.NewRedis(c.IpRecorderConfig.RedisConfig) + default: + log.Printf("recorder type: %s is not vail, disable recorder", c.IpRecorderConfig.Type) + return + } + // report and fetch online ip list task + c.onlineIpReportPeriodic = &task.Periodic{ + Interval: time.Duration(c.IpRecorderConfig.Periodic) * time.Second, + Execute: c.reportOnlineIp, + } + go func() { + time.Sleep(time.Duration(c.IpRecorderConfig.Periodic) * time.Second) + _ = c.onlineIpReportPeriodic.Start() + }() + log.Printf("[%s: %d] Start report online ip", c.nodeInfo.NodeType, c.nodeInfo.NodeId) + } +} + func (c *Node) nodeInfoMonitor() (err error) { // First fetch Node Info newNodeInfo, err := c.apiClient.GetNodeInfo() @@ -46,20 +118,6 @@ func (c *Node) nodeInfoMonitor() (err error) { log.Print(err) } } - // Check Cert - if c.nodeInfo.EnableTls && c.CertConfig.CertMode != "none" && - (c.CertConfig.CertMode == "dns" || c.CertConfig.CertMode == "http") { - lego, err := legoCmd.New() - if err != nil { - log.Print(err) - } - // Core-core supports the OcspStapling certification hot renew - _, _, err = lego.RenewCert(c.CertConfig.CertDomain, c.CertConfig.Email, - c.CertConfig.CertMode, c.CertConfig.Provider, c.CertConfig.DNSEnv) - if err != nil { - log.Print(err) - } - } // Update User newUserInfo, err := c.apiClient.GetUserList() if err != nil { @@ -280,3 +338,16 @@ func (c *Node) dynamicSpeedLimit() error { } return nil } + +func (c *Node) RenewCert() { + l, err := lego.New(c.CertConfig) + if err != nil { + log.Print(err) + return + } + err = l.RenewCert() + if err != nil { + log.Print(err) + return + } +}