diff --git a/.codeclimate.yml b/.codeclimate.yml index 8927af5..623c533 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -13,7 +13,7 @@ checks: threshold: 1000 method-complexity: # 函数和方法的逻辑复杂度 config: - threshold: 12 + threshold: 14 method-count: # 结构体的方法限制 config: threshold: 60 diff --git a/wechaty-puppet-service/ca.go b/wechaty-puppet-service/ca.go new file mode 100644 index 0000000..20b9640 --- /dev/null +++ b/wechaty-puppet-service/ca.go @@ -0,0 +1,68 @@ +package puppetservice + +import "context" + +// TLSInsecureServerCertCommonName Common Name: +// - Server Name Indication (SNI) +// - Case insensitive +// Wechaty Token format: `${SNI}/${UUIDv4}` +const TLSInsecureServerCertCommonName = "insecure" + +// TLSCaCert Wechaty Certificate Authority Repo: +/* https://github.com/wechaty/dotenv/tree/main/ca + +The SSL_ROOT_CERT is a root certificate generated by and for wechaty community. + +Because it's the default root cert for the puppet service, + so all the Polyglot Wechaty SDK should set this cert to be trusted by default. + +Update: + - Huan(202108): init, expired in 3650 days (after 2031/07) +*/ +const TLSCaCert = `-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE-----` + +type callCredToken struct { + token string +} + +func (r callCredToken) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { + return map[string]string{ + "authorization": "Wechaty " + r.token, + }, nil +} + +func (r callCredToken) RequireTransportSecurity() bool { + return true +} diff --git a/wechaty-puppet-service/config.go b/wechaty-puppet-service/config.go index 68418dc..46ff808 100644 --- a/wechaty-puppet-service/config.go +++ b/wechaty-puppet-service/config.go @@ -15,9 +15,11 @@ var ( WechatyPuppetHostieEndpoint string // WechatyPuppetServiceToken ... + // Deprecated WechatyPuppetServiceToken string // WechatyPuppetServiceEndpoint ... + // Deprecated WechatyPuppetServiceEndpoint string ) diff --git a/wechaty-puppet-service/envvars.go b/wechaty-puppet-service/envvars.go new file mode 100644 index 0000000..47cc20f --- /dev/null +++ b/wechaty-puppet-service/envvars.go @@ -0,0 +1,84 @@ +package puppetservice + +import ( + "errors" + "os" +) + +// ErrTokenNotFound err token not found +var ErrTokenNotFound = errors.New("wechaty-puppet-service: WECHATY_PUPPET_SERVICE_TOKEN not found") + +func envServiceToken(token string) (string, error) { + if token != "" { + return token, nil + } + + token = os.Getenv("WECHATY_PUPPET_SERVICE_TOKEN") + if token != "" { + return token, nil + } + + token = os.Getenv("WECHATY_PUPPET_HOSTIE_ENDPOINT") + if token != "" { + log.Trace("WECHATY_PUPPET_HOSTIE_TOKEN has been deprecated," + + "please use WECHATY_PUPPET_SERVICE_TOKEN instead.") + return token, nil + } + + return "", ErrTokenNotFound +} + +func envEndpoint(endpoint string) string { + if endpoint != "" { + return endpoint + } + + endpoint = os.Getenv("WECHATY_PUPPET_SERVICE_ENDPOINT") + if endpoint != "" { + return endpoint + } + + endpoint = os.Getenv("WECHATY_PUPPET_HOSTIE_ENDPOINT") + if endpoint != "" { + log.Println("WECHATY_PUPPET_HOSTIE_ENDPOINT has been deprecated," + + "please use WECHATY_PUPPET_SERVICE_ENDPOINT instead.") + return endpoint + } + return "" +} + +func envAuthority(authority string) string { + if authority != "" { + return authority + } + + authority = os.Getenv("WECHATY_PUPPET_SERVICE_AUTHORITY") + if authority != "" { + return authority + } + + return "api.chatie.io" +} + +func envNoTLSInsecureClient(disable bool) bool { + return disable || os.Getenv("WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_CLIENT") == "true" +} + +func envTLSServerName(serverName string) string { + if serverName != "" { + return serverName + } + + return os.Getenv("WECHATY_PUPPET_SERVICE_TLS_SERVER_NAME") +} + +func envTLSCaCert(caCert string) string { + if caCert != "" { + return caCert + } + caCert = os.Getenv("WECHATY_PUPPET_SERVICE_TLS_CA_CERT") + if caCert != "" { + return caCert + } + return TLSCaCert +} diff --git a/wechaty-puppet-service/grpc.go b/wechaty-puppet-service/grpc.go new file mode 100644 index 0000000..b94e690 --- /dev/null +++ b/wechaty-puppet-service/grpc.go @@ -0,0 +1,86 @@ +package puppetservice + +import ( + pbwechaty "github.com/wechaty/go-grpc/wechaty" + "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" + "time" +) + +func (p *PuppetService) startGrpcClient() error { + var err error + var creds credentials.TransportCredentials + var callOptions []grpc.CallOption + if p.disableTLS { + // TODO 目前不支持 tls,不用打印这个提醒 + //log.Warn("PuppetService.startGrpcClient TLS: disabled (INSECURE)") + creds = insecure.NewCredentials() + } else { + callOptions = append(callOptions, grpc.PerRPCCredentials(callCredToken{token: p.token})) + creds, err = p.createCred() + if err != nil { + return err + } + } + + dialOptions := []grpc.DialOption{ + grpc.WithTransportCredentials(creds), + grpc.WithDefaultCallOptions(callOptions...), + grpc.WithResolvers(wechatyResolver()), + } + + if p.disableTLS { + // Deprecated: this block will be removed after Dec 21, 2022. + dialOptions = append(dialOptions, grpc.WithAuthority(p.token)) + } + + conn, err := grpc.Dial(p.endpoint, dialOptions...) + if err != nil { + return err + } + p.grpcConn = conn + + go p.autoReconnectGrpcConn() + + p.grpcClient = pbwechaty.NewPuppetClient(conn) + return nil +} + +func (p *PuppetService) autoReconnectGrpcConn() { + <-p.started + isClose := false + ticker := p.newGrpcReconnectTicket() + defer ticker.Stop() + for { + select { + case <-ticker.C: + connState := p.grpcConn.GetState() + // 重新连接成功 + if isClose && connectivity.Ready == connState { + isClose = false + log.Warn("PuppetService.autoReconnectGrpcConn grpc reconnection successful") + if err := p.startGrpcStream(); err != nil { + log.Errorf("PuppetService.autoReconnectGrpcConn startGrpcStream err:%s", err.Error()) + } + } + + if p.grpcConn.GetState() == connectivity.Idle { + isClose = true + p.grpcConn.Connect() + log.Warn("PuppetService.autoReconnectGrpcConn grpc reconnection...") + } + case <-p.stop: + return + } + } +} + +func (p *PuppetService) newGrpcReconnectTicket() *time.Ticker { + interval := 2 * time.Second + if p.opts.GrpcReconnectInterval > 0 { + interval = p.opts.GrpcReconnectInterval + } + return time.NewTicker(interval) +} diff --git a/wechaty-puppet-service/options.go b/wechaty-puppet-service/options.go new file mode 100644 index 0000000..1bbd40a --- /dev/null +++ b/wechaty-puppet-service/options.go @@ -0,0 +1,23 @@ +package puppetservice + +import ( + wechatypuppet "github.com/wechaty/go-wechaty/wechaty-puppet" + "time" +) + +// TLSConfig tls config +type TLSConfig struct { + CaCert string + ServerName string + + Disable bool // only for compatible with old clients/servers +} + +// Options puppet-service options +type Options struct { + wechatypuppet.Option + + GrpcReconnectInterval time.Duration + Authority string + TLS TLSConfig +} diff --git a/wechaty-puppet-service/puppet_service.go b/wechaty-puppet-service/puppet_service.go index 68f03a0..22df876 100644 --- a/wechaty-puppet-service/puppet_service.go +++ b/wechaty-puppet-service/puppet_service.go @@ -2,6 +2,7 @@ package puppetservice import ( "context" + "crypto/x509" "encoding/json" "errors" "fmt" @@ -12,9 +13,9 @@ import ( "github.com/wechaty/go-wechaty/wechaty-puppet/helper" "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" "google.golang.org/grpc" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/credentials" "io" + "strings" "time" ) @@ -37,52 +38,81 @@ type PuppetService struct { grpcConn *grpc.ClientConn grpcClient pbwechaty.PuppetClient eventStream pbwechaty.Puppet_EventClient + opts Options - stop chan struct{} + disableTLS bool + serverName string + endpoint string + token string + caCert string + + started chan struct{} + stop chan struct{} } // NewPuppetService new PuppetService struct +// Deprecated: please use NewNewPuppetService func NewPuppetService(o wechatyPuppet.Option) (*PuppetService, error) { - if o.Token == "" { - o.Token = getPuppetServiceTokenFromEnv() + return NewNewPuppetService(Options{ + Option: o, + GrpcReconnectInterval: o.GrpcReconnectInterval, //nolint:staticcheck + }) +} + +// NewNewPuppetService create puppet service +func NewNewPuppetService(opts Options) (*PuppetService, error) { + token, err := envServiceToken(opts.Token) + if err != nil { + return nil, err + } + endpoint := envEndpoint(opts.Endpoint) + if endpoint == "" { + endpoint = fmt.Sprintf("wechaty://%s/%s", + envAuthority(opts.Authority), token) } - if o.Endpoint == "" { - o.Endpoint = getPuppetServiceEndpointFromEnv() + + //disableTLS := envNoTLSInsecureClient(opts.TLS.Disable) + // TODO 禁用 tls, type script ca 在 golang 不能使用 + disableTLS := true + + serverNameIndication := envTLSServerName(opts.TLS.ServerName) + if serverNameIndication == "" { + serverNameIndication = sni(token) } - puppetAbstract, err := wechatyPuppet.NewPuppet(o) + if serverNameIndication == "" { + return nil, fmt.Errorf( + `wechaty Puppet Service requires a SNI as prefix of the token. +You can add the "%s_" prefix to your token +like: "%s_%s +and try again`, TLSInsecureServerCertCommonName, TLSInsecureServerCertCommonName, token) + } + + // TODO puppet is poorly designed, consider refactoring + puppetAbstract, err := wechatyPuppet.NewPuppet(opts.Option) if err != nil { return nil, err } puppetService := &PuppetService{ - Puppet: puppetAbstract, - stop: make(chan struct{}, 1), + Puppet: puppetAbstract, + disableTLS: disableTLS, + serverName: serverNameIndication, + endpoint: endpoint, + token: token, + caCert: envTLSCaCert(opts.TLS.CaCert), + opts: opts, + stop: make(chan struct{}, 1), + started: make(chan struct{}, 1), } puppetAbstract.SetPuppetImplementation(puppetService) return puppetService, nil } -func getPuppetServiceTokenFromEnv() string { - if WechatyPuppetServiceToken != "" { - return WechatyPuppetServiceToken - } - if WechatyPuppetHostieToken != "" { - log.Warn(`warn: WECHATY_PUPPET_HOSTIE_TOKEN environment be deprecated -please use new environment name to avoid unnecessary bugs`) - return WechatyPuppetHostieToken +func sni(token string) string { + underscoreIndex := strings.LastIndex(token, "_") + if underscoreIndex == -1 { + return "" } - return "" -} - -func getPuppetServiceEndpointFromEnv() string { - if WechatyPuppetServiceEndpoint != "" { - return WechatyPuppetServiceEndpoint - } - if WechatyPuppetServiceEndpoint != "" { - log.Warn(`warn: WECHATY_PUPPET_HOSTIE_ENDPOINT environment be deprecated -please use new environment name to avoid unnecessary bugs`) - return WechatyPuppetServiceEndpoint - } - return "" + return strings.ToLower(token[0:underscoreIndex]) } // MessageImage ... @@ -123,6 +153,7 @@ func (p *PuppetService) Start() (err error) { if err != nil { return err } + p.started <- struct{}{} return nil } @@ -224,61 +255,12 @@ func (p *PuppetService) logonoff() bool { return p.SelfID() != "" } -func (p *PuppetService) startGrpcClient() error { - endpoint := p.Endpoint - if endpoint == "" { - serviceEndPoint, err := p.discoverServiceEndPoint() - if err != nil { - return err - } - if !serviceEndPoint.IsValid() { - return ErrNoEndpoint - } - endpoint = serviceEndPoint.Target() - } - - // TODO 支持 tls - conn, err := grpc.Dial(endpoint, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithAuthority(p.Token)) - if err != nil { - return err - } - p.grpcConn = conn - - go p.autoReconnectGrpcConn() - - p.grpcClient = pbwechaty.NewPuppetClient(conn) - return nil -} - -func (p *PuppetService) autoReconnectGrpcConn() { - interval := 2 * time.Second - if p.Option.GrpcReconnectInterval > 0 { - interval = p.Option.GrpcReconnectInterval - } - ticker := time.NewTicker(interval) - isClose := false - for { - select { - case <-ticker.C: - connState := p.grpcConn.GetState() - // 重新连接成功 - if isClose && connectivity.Ready == connState { - isClose = false - log.Info("PuppetService.autoReconnectGrpcConn grpc reconnection successful") - if err := p.startGrpcStream(); err != nil { - log.Errorf("PuppetService.autoReconnectGrpcConn startGrpcStream err:%s", err.Error()) - } - } - - if p.grpcConn.GetState() == connectivity.Idle { - isClose = true - p.grpcConn.Connect() - log.Info("PuppetService.autoReconnectGrpcConn grpc reconnection...") - } - case <-p.stop: - return - } +func (p *PuppetService) createCred() (credentials.TransportCredentials, error) { + pool := x509.NewCertPool() + if ok := pool.AppendCertsFromPEM([]byte(p.caCert)); !ok { + return nil, fmt.Errorf("PuppetService.createCred failed to parse root certificate") } + return credentials.NewClientTLSFromCert(pool, p.serverName), nil } func (p *PuppetService) startGrpcStream() (err error) { diff --git a/wechaty-puppet-service/resolver.go b/wechaty-puppet-service/resolver.go new file mode 100644 index 0000000..c6d9ea0 --- /dev/null +++ b/wechaty-puppet-service/resolver.go @@ -0,0 +1,75 @@ +package puppetservice + +import ( + "encoding/json" + "fmt" + "google.golang.org/grpc/resolver" + "google.golang.org/grpc/resolver/manual" + "io/ioutil" + "net/http" + "strings" +) + +func wechatyResolver() resolver.Builder { + r := manual.NewBuilderWithScheme("wechaty") + r.BuildCallback = resolverBuildCallBack + return r +} + +func resolverBuildCallBack(target resolver.Target, conn resolver.ClientConn, options resolver.BuildOptions) { + // target.URL.Host `api.chatie.io` in `wechaty://api.chatie.io/__token__` + // target.URL.Path `__token__` in `wechaty://api.chatie.io/__token__` + log.Trace("resolverBuildCallBack()") + uri := fmt.Sprintf("https://%s/v0/hosties%s", target.URL.Host, target.URL.Path) + address, err := discoverAPI(uri) + if err != nil { + conn.ReportError(err) + return + } + if address == nil || address.Host == "" { + conn.ReportError(fmt.Errorf(`token %s does not exist`, strings.TrimLeft(target.URL.Path, "/"))) + return + } + err = conn.UpdateState(resolver.State{ + Addresses: []resolver.Address{{ + Addr: fmt.Sprintf("%s:%d", address.Host, address.Port), + }}, + }) + if err != nil { + log.Error("resolverBuildCallBack UpdateState err: ", err.Error()) + return + } +} + +type serviceAddress struct { + Host string + Port int +} + +func discoverAPI(uri string) (*serviceAddress, error) { + response, err := http.Get(uri) + if err != nil { + return nil, fmt.Errorf("discoverAPI http.Get() %w", err) + } + defer response.Body.Close() + + // 4xx + if response.StatusCode >= 400 && response.StatusCode < 500 { + return nil, nil + } + + // 2xx + if response.StatusCode < 200 || response.StatusCode >= 300 { + return nil, fmt.Errorf("discoverAPI http.Get() status:%s %w", response.Status, err) + } + data, err := ioutil.ReadAll(response.Body) + if err != nil { + return nil, fmt.Errorf("discoverAPI ioutil.ReadAll %w", err) + } + + r := &serviceAddress{} + if err := json.Unmarshal(data, r); err != nil { + return nil, fmt.Errorf("discoverAPI json.Unmarshal %w", err) + } + return r, nil +} diff --git a/wechaty-puppet-service/service_endpoint.go b/wechaty-puppet-service/service_endpoint.go index 34edd2c..e667294 100644 --- a/wechaty-puppet-service/service_endpoint.go +++ b/wechaty-puppet-service/service_endpoint.go @@ -1,19 +1,18 @@ package puppetservice import ( - "encoding/json" "errors" "fmt" - "io/ioutil" - "net/http" ) var ( // ErrNotToken token not found error + // Deprecated ErrNotToken = errors.New("wechaty-puppet-service: token not found. See: ") ) // ServiceEndPoint api.chatie.io endpoint api response +// Deprecated type ServiceEndPoint struct { IP string `json:"ip"` Port int `json:"port,omitempty"` @@ -32,38 +31,3 @@ func (p *ServiceEndPoint) Target() string { } return fmt.Sprintf("%s:%d", p.IP, port) } - -// discoverServiceEndPoint discover service endpoint ip and port -func (p *PuppetService) discoverServiceEndPoint() (endPoint ServiceEndPoint, err error) { - const serviceEndpoint = "https://api.chatie.io/v0/hosties/%s" - - if p.Token == "" { - return endPoint, ErrNotToken - } - - client := &http.Client{} - if p.Timeout > 0 { - client = &http.Client{ - Timeout: p.Timeout, - } - } - resp, err := client.Get(fmt.Sprintf(serviceEndpoint, p.Token)) - if err != nil { - return endPoint, fmt.Errorf("discoverServiceEndPoint() err: %w", err) - } - - if resp.StatusCode == http.StatusOK { - defer resp.Body.Close() - body, _ := ioutil.ReadAll(resp.Body) - err = json.Unmarshal(body, &endPoint) - if err != nil { - return endPoint, fmt.Errorf("discoverServiceEndPoint() err: %w", err) - } - return endPoint, nil - } - if resp.StatusCode == http.StatusNotFound { - log.Errorf("discoverServiceEndPoint() err: http.Status:%s\n", resp.Status) - return endPoint, nil - } - return endPoint, fmt.Errorf("discoverServiceEndPoint() err: http.Status:%s", resp.Status) -} diff --git a/wechaty-puppet/option.go b/wechaty-puppet/option.go index 6ada85c..89a571b 100644 --- a/wechaty-puppet/option.go +++ b/wechaty-puppet/option.go @@ -6,9 +6,11 @@ import ( // Option puppet option type Option struct { - Endpoint string - Timeout time.Duration - Token string + Endpoint string + Timeout time.Duration + Token string + + // Deprecated: move to wechaty-puppet-service GrpcReconnectInterval time.Duration } diff --git a/wechaty/option.go b/wechaty/option.go index 2135b25..e0267a1 100644 --- a/wechaty/option.go +++ b/wechaty/option.go @@ -1,22 +1,26 @@ package wechaty import ( - wp "github.com/wechaty/go-wechaty/wechaty-puppet" - mc "github.com/wechaty/go-wechaty/wechaty-puppet/memory-card" + wp "github.com/wechaty/go-wechaty/wechaty-puppet" + puppetservice "github.com/wechaty/go-wechaty/wechaty-puppet-service" + mc "github.com/wechaty/go-wechaty/wechaty-puppet/memory-card" ) // Option wechaty option type Option struct { - // wechaty name - name string - // puppet instance - puppet wp.IPuppetAbstract - // puppet option - puppetOption wp.Option - // io token - ioToken string - // memory card - memoryCard mc.IMemoryCard + // wechaty name + name string + // puppet instance + puppet wp.IPuppetAbstract + // puppet option + puppetOption wp.Option + // io token + ioToken string + // memory card + memoryCard mc.IMemoryCard + + // puppet-serviceOptions + puppetServiceOptions puppetservice.Options } // OptionFn func @@ -24,35 +28,42 @@ type OptionFn func(opts *Option) // WithName with name func WithName(name string) OptionFn { - return func(opt *Option) { - opt.name = name - } + return func(opt *Option) { + opt.name = name + } } // WithPuppet with puppet impl func WithPuppet(puppet wp.IPuppetAbstract) OptionFn { - return func(opt *Option) { - opt.puppet = puppet - } + return func(opt *Option) { + opt.puppet = puppet + } } // WithPuppetOption with puppet option func WithPuppetOption(puppetOption wp.Option) OptionFn { - return func(opt *Option) { - opt.puppetOption = puppetOption - } + return func(opt *Option) { + opt.puppetOption = puppetOption + } } // WithIOToken with io token func WithIOToken(ioToken string) OptionFn { - return func(opt *Option) { - opt.ioToken = ioToken - } + return func(opt *Option) { + opt.ioToken = ioToken + } } // WithMemoryCard with memory card func WithMemoryCard(memoryCard mc.IMemoryCard) OptionFn { - return func(opt *Option) { - opt.memoryCard = memoryCard - } + return func(opt *Option) { + opt.memoryCard = memoryCard + } +} + +// WithPuppetServiceOptions puppet service options +func WithPuppetServiceOptions(puppetServiceOptions puppetservice.Options) OptionFn { + return func(opts *Option) { + opts.puppetServiceOptions = puppetServiceOptions + } } diff --git a/wechaty/wechaty.go b/wechaty/wechaty.go index 84db6e4..e0d056c 100644 --- a/wechaty/wechaty.go +++ b/wechaty/wechaty.go @@ -218,7 +218,16 @@ func (w *Wechaty) initPuppet() error { // TODO: set puppet memory if w.Option.puppet == nil { - puppet, err := puppetservice.NewPuppetService(w.puppetOption) + // 之前是 puppet.Option 现在改为了 puppetservice.Options 需要兼容处理 + puppetServiceOption := w.Option.puppetServiceOptions + if w.puppetOption.Token != "" { + puppetServiceOption.Option = w.puppetOption + } + //nolint:staticcheck + if w.puppetOption.GrpcReconnectInterval != 0 { + puppetServiceOption.GrpcReconnectInterval = w.puppetOption.GrpcReconnectInterval + } + puppet, err := puppetservice.NewNewPuppetService(puppetServiceOption) if err != nil { return err } @@ -271,7 +280,7 @@ func (w *Wechaty) Start() error { err = w.initPuppet() if err != nil { - log.Error("memory card load err: ", err) + log.Error("w.initPuppet err: ", err) return err }