Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(WIP) Enable TLS #126

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codeclimate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ checks:
threshold: 1000
method-complexity: # 函数和方法的逻辑复杂度
config:
threshold: 12
threshold: 14
method-count: # 结构体的方法限制
config:
threshold: 60
Expand Down
68 changes: 68 additions & 0 deletions wechaty-puppet-service/ca.go
Original file line number Diff line number Diff line change
@@ -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-----
MIIFxTCCA62gAwIBAgIUYddLAoa8JnLzJ80l2u5vGuFsaEIwDQYJKoZIhvcNAQEL
BQAwcjELMAkGA1UEBhMCVVMxFjAUBgNVBAgMDVNhbiBGcmFuY2lzY28xEjAQBgNV
BAcMCVBhbG8gQWx0bzEQMA4GA1UECgwHV2VjaGF0eTELMAkGA1UECwwCQ0ExGDAW
BgNVBAMMD3dlY2hhdHktcm9vdC1jYTAeFw0yMTA4MDkxNTQ4NTJaFw0zMTA4MDcx
NTQ4NTJaMHIxCzAJBgNVBAYTAlVTMRYwFAYDVQQIDA1TYW4gRnJhbmNpc2NvMRIw
EAYDVQQHDAlQYWxvIEFsdG8xEDAOBgNVBAoMB1dlY2hhdHkxCzAJBgNVBAsMAkNB
MRgwFgYDVQQDDA93ZWNoYXR5LXJvb3QtY2EwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQDulLjOZhzQ58TSQ7TfWNYgdtWhlc+5L9MnKb1nznVRhzAkZo3Q
rPLRW/HDjlv2OEbt4nFLaQgaMmc1oJTUVGDBDlrzesI/lJh7z4eA/B0z8eW7f6Cw
/TGc8lgzHvq7UIE507QYPhvfSejfW4Prw+90HJnuodriPdMGS0n9AR37JPdQm6sD
iMFeEvhHmM2SXRo/o7bll8UDZi81DoFu0XuTCx0esfCX1W5QWEmAJ5oAdjWxJ23C
lxI1+EjwBQKXGqp147VP9+pwpYW5Xxpy870kctPBHKjCAti8Bfo+Y6dyWz2UAd4w
4BFRD+18C/TgX+ECl1s9fsHMY15JitcSGgAIz8gQX1OelECaTMRTQfNaSnNW4LdS
sXMQEI9WxAU/W47GCQFmwcJeZvimqDF1QtflHSaARD3O8tlbduYqTR81LJ63bPoy
9e1pdB6w2bVOTlHunE0YaGSJERALVc1xz40QpPGcZ52mNCb3PBg462RQc77yv/QB
x/P2RC1y0zDUF2tP9J29gTatWq6+D4MhfEk2flZNyzAgJbDuT6KAIJGzOB1ZJ/MG
o1gS13eTuZYw24LElrhd1PrR6OHK+lkyYzqUPYMulUg4HzaZIDclfHKwAC4lecKm
zC5q9jJB4m4SKMKdzxvpIOfdahoqsZMg34l4AavWRqPTpwEU0C0dboNA/QIDAQAB
o1MwUTAdBgNVHQ4EFgQU0rey3QPklTOgdhMJ9VIA6KbZ5bAwHwYDVR0jBBgwFoAU
0rey3QPklTOgdhMJ9VIA6KbZ5bAwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
AQsFAAOCAgEAx2uyShx9kLoB1AJ8x7Vf95v6PX95L/4JkJ1WwzJ9Dlf3BcCI7VH7
Fp1dnQ6Ig7mFqSBDBAUUBWAptAnuqIDcgehI6XAEKxW8ZZRxD877pUNwZ/45tSC4
b5U5y9uaiNK7oC3LlDCsB0291b3KSOtevMeDFoh12LcliXAkdIGGTccUxrH+Cyij
cBOc+EKGJFBdLqcjLDU4M6QdMMMFOdfXyAOSpYuWGYqrxqvxQjAjvianEyMpNZWM
lajggJqiPhfF67sZTB2yzvRTmtHdUq7x+iNOVonOBcCHu31aGxa9Py91XEr9jaIQ
EBdl6sycLxKo8mxF/5tyUOns9+919aWNqTOUBmI15D68bqhhOVNyvsb7aVURIt5y
6A7Sj4gSBR9P22Ba6iFZgbvfLn0zKLzjlBonUGlSPf3rSIYUkawICtDyYPvK5mi3
mANgIChMiOw6LYCPmmUVVAWU/tDy36kr9ZV9YTIZRYAkWswsJB340whjuzvZUVaG
DgW45GPR6bGIwlFZeqCwXLput8Z3C8Sw9bE9vjlB2ZCpjPLmWV/WbDlH3J3uDjgt
9PoALW0sOPhHfYklH4/rrmsSWMYTUuGS/HqxrEER1vpIOOb0hIiAWENDT/mruq22
VqO8MHX9ebjInSxPmhYOlrSZrOgEcogyMB4Z0SOtKVqPnkWmdR5hatU=
-----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
}
2 changes: 2 additions & 0 deletions wechaty-puppet-service/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ var (
WechatyPuppetHostieEndpoint string

// WechatyPuppetServiceToken ...
// Deprecated
WechatyPuppetServiceToken string

// WechatyPuppetServiceEndpoint ...
// Deprecated
WechatyPuppetServiceEndpoint string
)

Expand Down
85 changes: 85 additions & 0 deletions wechaty-puppet-service/envvars.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package puppetservice

import (
"errors"
"log"
"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.Println("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
}
86 changes: 86 additions & 0 deletions wechaty-puppet-service/grpc.go
Original file line number Diff line number Diff line change
@@ -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"
"log"
"time"
)

func (p *PuppetService) startGrpcClient() error {
var err error
var creds credentials.TransportCredentials
var callOptions []grpc.CallOption
if p.disableTLS {
log.Println("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.Printf("PuppetService.autoReconnectGrpcConn grpc reconnection successful")
if err := p.startGrpcStream(); err != nil {
log.Printf("PuppetService.autoReconnectGrpcConn startGrpcStream err:%s", err.Error())
}
}

if p.grpcConn.GetState() == connectivity.Idle {
isClose = true
p.grpcConn.Connect()
log.Printf("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)
}
23 changes: 23 additions & 0 deletions wechaty-puppet-service/options.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading