diff --git a/api/client.go b/api/client.go index edb275dea..410b9af63 100644 --- a/api/client.go +++ b/api/client.go @@ -2,6 +2,7 @@ package api import ( "context" + "crypto/tls" "net" "github.com/p4gefau1t/trojan-go/common" @@ -10,6 +11,7 @@ import ( "github.com/p4gefau1t/trojan-go/proxy" "github.com/p4gefau1t/trojan-go/stat" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" ) type ClientAPI struct { @@ -52,7 +54,17 @@ func (s *ClientAPI) GetTraffic(ctx context.Context, req *GetTrafficRequest) (*Ge } func RunClientAPI(ctx context.Context, config *conf.GlobalConfig, auth stat.Authenticator) error { - server := grpc.NewServer() + var server *grpc.Server + if config.API.APITLS { + creds := credentials.NewTLS(&tls.Config{ + ClientAuth: tls.RequireAndVerifyClientCert, + Certificates: config.TLS.KeyPair, + ClientCAs: config.TLS.ClientCertPool, + }) + server = grpc.NewServer(grpc.Creds(creds)) + } else { + server = grpc.NewServer() + } service := &ClientAPI{ ctx: ctx, auth: auth, diff --git a/api/client_test.go b/api/client_test.go index ce711b9a3..52ad63183 100644 --- a/api/client_test.go +++ b/api/client_test.go @@ -7,6 +7,7 @@ import ( "github.com/p4gefau1t/trojan-go/common" "github.com/p4gefau1t/trojan-go/conf" + _ "github.com/p4gefau1t/trojan-go/log/golog" "github.com/p4gefau1t/trojan-go/stat/memory" "google.golang.org/grpc" ) diff --git a/api/server.go b/api/server.go index 663d6ad2b..fad317abc 100644 --- a/api/server.go +++ b/api/server.go @@ -2,6 +2,7 @@ package api import ( "context" + "crypto/tls" "io" "net" @@ -11,6 +12,7 @@ import ( "github.com/p4gefau1t/trojan-go/proxy" "github.com/p4gefau1t/trojan-go/stat" grpc "google.golang.org/grpc" + "google.golang.org/grpc/credentials" ) type ServerAPI struct { @@ -152,7 +154,18 @@ func (s *ServerAPI) ListUsers(req *ListUserRequest, stream TrojanServerService_L } func RunServerAPI(ctx context.Context, config *conf.GlobalConfig, auth stat.Authenticator) error { - server := grpc.NewServer() + var server *grpc.Server + if config.API.APITLS { + creds := credentials.NewTLS(&tls.Config{ + ClientAuth: tls.RequireAndVerifyClientCert, + Certificates: config.TLS.KeyPair, + ClientCAs: config.TLS.ClientCertPool, + }) + server = grpc.NewServer(grpc.Creds(creds)) + } else { + server = grpc.NewServer() + log.Warn("Using insecure API service. Please set \"api_tls\" to enable TLS-based gRPC service.") + } service := &ServerAPI{ auth: auth, } diff --git a/api/server_test.go b/api/server_test.go index 76e30f5ae..20d8a2d55 100644 --- a/api/server_test.go +++ b/api/server_test.go @@ -8,6 +8,7 @@ import ( "github.com/p4gefau1t/trojan-go/common" "github.com/p4gefau1t/trojan-go/conf" + _ "github.com/p4gefau1t/trojan-go/log/golog" "github.com/p4gefau1t/trojan-go/stat/memory" "google.golang.org/grpc" ) diff --git a/conf/conf.go b/conf/conf.go index 5896d3d70..f9ca04934 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -31,6 +31,7 @@ type TLSConfig struct { Verify bool `json:"verify"` CertPath string `json:"cert"` KeyPath string `json:"key"` + ClientCertPath []string `json:"client_cert"` KeyPassword string `json:"key_password"` Cipher string `json:"cipher"` CipherTLS13 string `json:"cipher_tls13"` @@ -48,6 +49,7 @@ type TLSConfig struct { ClientHelloID *utls.ClientHelloID FallbackAddress *common.Address CertPool *x509.CertPool + ClientCertPool *x509.CertPool KeyPair []tls.Certificate HTTPResponse []byte CipherSuites []uint16 @@ -138,9 +140,11 @@ type WebsocketConfig struct { } type APIConfig struct { - Enabled bool `json:"enabled"` - APIHost string `json:"api_addr"` - APIPort int `json:"api_port"` + Enabled bool `json:"enabled"` + APIHost string `json:"api_addr"` + APIPort int `json:"api_port"` + APITLS bool `json:"api_tls"` + TLS TLSConfig `json:"ssl"` APIAddress *common.Address } diff --git a/conf/parse.go b/conf/parse.go index 058cdeed4..04aa53e0d 100644 --- a/conf/parse.go +++ b/conf/parse.go @@ -26,15 +26,16 @@ func loadCert(tlsConfig *TLSConfig) error { if err != nil { return common.NewError("failed to load cert file").Base(err) } - pool := x509.NewCertPool() - ok := pool.AppendCertsFromPEM(caCertByte) + tlsConfig.CertPool = x509.NewCertPool() + ok := tlsConfig.CertPool.AppendCertsFromPEM(caCertByte) if !ok { log.Warn("Invalid CA cert list") } log.Info("Using custom CA list") + + //show info abount the cert pemCerts := caCertByte for len(pemCerts) > 0 { - tlsConfig.CertPool = pool var block *pem.Block block, pemCerts = pem.Decode(pemCerts) if block == nil { @@ -87,6 +88,19 @@ func loadCertAndKey(tlsConfig *TLSConfig) error { } tlsConfig.KeyPair = []tls.Certificate{keyPair} } + + tlsConfig.ClientCertPool = x509.NewCertPool() + for _, path := range tlsConfig.ClientCertPath { + log.Debug("Loading client cert: " + path) + certBytes, err := ioutil.ReadFile(path) + if err != nil { + return common.NewError("Failed to load cert file").Base(err) + } + ok := tlsConfig.ClientCertPool.AppendCertsFromPEM(certBytes) + if !ok { + return common.NewError("Invalid client cert") + } + } return nil } @@ -139,6 +153,11 @@ func loadCommonConfig(config *GlobalConfig) error { //api settings if config.API.Enabled { config.API.APIAddress = common.NewAddress(config.API.APIHost, config.API.APIPort, "tcp") + if config.API.APITLS { + if err := loadCertAndKey(&config.API.TLS); err != nil { + return err + } + } } //tls settings diff --git a/docs/content/basic/full-config.md b/docs/content/basic/full-config.md index 87c74e240..f92cf2b52 100644 --- a/docs/content/basic/full-config.md +++ b/docs/content/basic/full-config.md @@ -120,7 +120,14 @@ weight: 30 "api": { "enabled": false, "api_addr": "", - "api_port": 0 + "api_port": 0, + "api_tls": false, + "ssl": { + "cert": "", + "key": "", + "key_password": "", + "client_cert": [] + }, } } ``` @@ -320,4 +327,8 @@ trojan-go基于gRPC提供了API,以支持服务端和客户端的管理和统 ```api_port```gRPC监听的端口。 -警告:**不要将API直接暴露在互联网上,否则可能导致各类安全问题** +```api_tls```gRPC是否启用TLS传输(双向认证)。 + +```ssl``` TLS相关设置,如果开启TLS传输和双向认证,所有选项为必填。其中```key```, ```cert```为API服务器使用的密钥和证书文件,```client_cert```为客户端使用的证书文件路径,用于客户端认证。 + +警告:**不要将未开启TLS双向认证的API服务直接暴露在互联网上,否则可能导致各类安全问题。** diff --git a/docs/content/developer/api.md b/docs/content/developer/api.md index 137eda111..846526109 100644 --- a/docs/content/developer/api.md +++ b/docs/content/developer/api.md @@ -9,8 +9,18 @@ Trojan-Go基于gRPC实现了API,使用protobuf交换数据。客户端可获 ```json "api": { "enabled": true, - "api_addr": "127.0.0.1", - "api_port": 10000 + "api_addr": "0.0.0.0", + "api_port": 10000, + "api_tls": true, + "ssl": { + "cert": "api_cert.crt", + "key": "api_key.crt", + "key_password": "", + "client_cert": [ + "api_client_cert1.crt", + "api_client_cert2.crt" + ] + }, } ```