Skip to content

Commit

Permalink
add api tls support, remove tproxy upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
p4gefau1t committed Jun 25, 2020
1 parent 07d9a08 commit 0fa9071
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 22 deletions.
6 changes: 4 additions & 2 deletions api/service/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/p4gefau1t/trojan-go/log"
"github.com/p4gefau1t/trojan-go/statistic"
"github.com/p4gefau1t/trojan-go/tunnel/trojan"
"google.golang.org/grpc"
)

type ClientAPI struct {
Expand Down Expand Up @@ -58,7 +57,10 @@ func RunClientAPI(ctx context.Context, auth statistic.Authenticator) error {
if !cfg.API.Enabled {
return nil
}
server := grpc.NewServer()
server, err := newAPIServer(cfg)
if err != nil {
return err
}
service := &ClientAPI{
ctx: ctx,
auth: auth,
Expand Down
2 changes: 1 addition & 1 deletion api/service/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type SSLConfig struct {
Enabled bool `json,yaml:"enabled"`
CertPath string `json:"cert" yaml:"cert"`
KeyPath string `json:"key" yaml:"key"`
ClientAuth bool `json:"client_auth" yaml:"client-auth"`
VerifyClient bool `json:"verify_client" yaml:"verify-client"`
ClientCertPath []string `json:"client_cert" yaml:"client-cert"`
}

Expand Down
43 changes: 42 additions & 1 deletion api/service/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package service

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"io/ioutil"
"net"

"github.com/p4gefau1t/trojan-go/api"
Expand All @@ -13,6 +16,7 @@ import (
"github.com/p4gefau1t/trojan-go/statistic"
"github.com/p4gefau1t/trojan-go/tunnel/trojan"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)

type ServerAPI struct {
Expand Down Expand Up @@ -177,15 +181,52 @@ func (s *ServerAPI) ListUsers(req *ListUsersRequest, stream TrojanServerService_
return nil
}

func newAPIServer(cfg *Config) (*grpc.Server, error) {
var server *grpc.Server
if cfg.API.SSL.Enabled {
log.Info("api tls enabled")
keyPair, err := tls.LoadX509KeyPair(cfg.API.SSL.CertPath, cfg.API.SSL.KeyPath)
if err != nil {
return nil, common.NewError("failed to load key pair").Base(err)
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{keyPair},
}
if cfg.API.SSL.VerifyClient {
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
tlsConfig.ClientCAs = x509.NewCertPool()
for _, path := range cfg.API.SSL.ClientCertPath {
log.Debug("loading client cert: " + path)
certBytes, err := ioutil.ReadFile(path)
if err != nil {
return nil, common.NewError("failed to load cert file").Base(err)
}
ok := tlsConfig.ClientCAs.AppendCertsFromPEM(certBytes)
if !ok {
return nil, common.NewError("fnvalid client cert")
}
}
}
creds := credentials.NewTLS(tlsConfig)
server = grpc.NewServer(grpc.Creds(creds))
} else {
server = grpc.NewServer()
}
return server, nil
}

func RunServerAPI(ctx context.Context, auth statistic.Authenticator) error {
cfg := config.FromContext(ctx, Name).(*Config)
if !cfg.API.Enabled {
return nil
}
server := grpc.NewServer()
service := &ServerAPI{
auth: auth,
}
server, err := newAPIServer(cfg)
if err != nil {
return err
}
RegisterTrojanServerServiceServer(server, service)
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.API.APIHost, cfg.API.APIPort))
if err != nil {
Expand Down
16 changes: 11 additions & 5 deletions docs/content/basic/full-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,11 @@ weight: 30
"enabled": false,
"api_addr": "",
"api_port": 0,
"api_tls": false,
"ssl": {
"cert": "",
"enabled": false,
"key": "",
"key_password": "",
"cert": "",
"verify_client": false,
"client_cert": []
}
}
Expand Down Expand Up @@ -373,8 +373,14 @@ trojan-go基于gRPC提供了API,以支持服务端和客户端的管理和统

```api_port```gRPC监听的端口。

```api_tls```gRPC是否启用TLS传输(双向认证)。
```ssl``` TLS相关设置

- ```enabled```是否使用开启TLS传输

- ```key``````cert```服务器私钥和证书

- ```verify_client```是否认证客户端证书

```ssl``` TLS相关设置,如果开启TLS传输和双向认证,所有选项为必填。其中```key```, ```cert```为API服务器使用的密钥和证书文件,```client_cert```为客户端使用的证书文件路径,用于客户端认证。
- ```client_cert```客户端证书列表

警告:**不要将未开启TLS双向认证的API服务直接暴露在互联网上,否则可能导致各类安全问题。**
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module github.com/p4gefau1t/trojan-go
go 1.14

require (
github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed
github.com/go-sql-driver/mysql v1.5.0
github.com/golang/protobuf v1.4.1
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed h1:eqa6queieK8SvoszxCu0WwH7lSVeL4/N/f1JwOMw1G4=
github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed/go.mod h1:rA52xkgZwql9LRZXWb2arHEFP6qSR48KY2xOfWzEciQ=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
Expand Down
17 changes: 8 additions & 9 deletions tunnel/tproxy/server.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// +build linux,!386
// +build linux

package tproxy

Expand All @@ -9,7 +9,6 @@ import (
"sync"
"time"

"github.com/LiamHaworth/go-tproxy"
"github.com/p4gefau1t/trojan-go/common"
"github.com/p4gefau1t/trojan-go/config"
"github.com/p4gefau1t/trojan-go/log"
Expand Down Expand Up @@ -46,13 +45,13 @@ func (s *Server) AcceptConn(tunnel.Tunnel) (tunnel.Conn, error) {
}
return nil, common.NewError("tproxy failed to accept conn")
}
addr, err := getOriginalTCPDest(conn.(*tproxy.Conn).TCPConn)
dst, err := getOriginalTCPDest(conn.(*net.TCPConn))
if err != nil {
return nil, common.NewError("tproxy failed to obtain original address of tcp socket").Base(err)
}
address, err := tunnel.NewAddressFromAddr("tcp", addr.String())
address, err := tunnel.NewAddressFromAddr("tcp", dst.String())
common.Must(err)
log.Info("tproxy connection from", conn.RemoteAddr().String(), "metadata", addr.String())
log.Info("tproxy connection from", conn.RemoteAddr().String(), "metadata", dst.String())
return &Conn{
metadata: &tunnel.Metadata{
Address: address,
Expand All @@ -72,7 +71,7 @@ func (s *Server) packetDispatchLoop() {
go func() {
for {
buf := make([]byte, MaxPacketSize)
n, src, dst, err := tproxy.ReadFromUDP(s.udpListener, buf)
n, src, dst, err := ReadFromUDP(s.udpListener, buf)
if err != nil {
select {
case <-s.ctx.Done():
Expand Down Expand Up @@ -129,7 +128,7 @@ func (s *Server) packetDispatchLoop() {

go func(conn *dokodemo.PacketConn) {
defer conn.Close()
back, err := tproxy.DialUDP(
back, err := DialUDP(
"udp",
&net.UDPAddr{
IP: conn.M.IP,
Expand Down Expand Up @@ -194,15 +193,15 @@ func NewServer(ctx context.Context, _ tunnel.Server) (*Server, error) {
if err != nil {
return nil, common.NewError("invalid tproxy local address").Base(err)
}
tcpListener, err := tproxy.ListenTCP("tcp", &net.TCPAddr{
tcpListener, err := ListenTCP("tcp", &net.TCPAddr{
IP: ip,
Port: cfg.LocalPort,
})
if err != nil {
return nil, common.NewError("tproxy failed to listen tcp").Base(err)
}

udpListener, err := tproxy.ListenUDP("udp", &net.UDPAddr{
udpListener, err := ListenUDP("udp", &net.UDPAddr{
IP: ip,
Port: cfg.LocalPort,
})
Expand Down
60 changes: 59 additions & 1 deletion tunnel/tproxy/tproxy.go → tunnel/tproxy/tcp.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,72 @@
// +build linux,!386
// +build linux

package tproxy

import (
"fmt"
"net"
"os"
"syscall"
"unsafe"
)

// Listener describes a TCP Listener
// with the Linux IP_TRANSPARENT option defined
// on the listening socket
type Listener struct {
base net.Listener
}

// Accept waits for and returns
// the next connection to the listener.
//
// This command wraps the AcceptTProxy
// method of the Listener
func (listener *Listener) Accept() (net.Conn, error) {
tcpConn, err := listener.base.(*net.TCPListener).AcceptTCP()
if err != nil {
return nil, err
}

return tcpConn, nil
}

// Addr returns the network address
// the listener is accepting connections
// from
func (listener *Listener) Addr() net.Addr {
return listener.base.Addr()
}

// Close will close the listener from accepting
// any more connections. Any blocked connections
// will unblock and close
func (listener *Listener) Close() error {
return listener.base.Close()
}

// ListenTCP will construct a new TCP listener
// socket with the Linux IP_TRANSPARENT option
// set on the underlying socket
func ListenTCP(network string, laddr *net.TCPAddr) (net.Listener, error) {
listener, err := net.ListenTCP(network, laddr)
if err != nil {
return nil, err
}

fileDescriptorSource, err := listener.File()
if err != nil {
return nil, &net.OpError{Op: "listen", Net: network, Source: nil, Addr: laddr, Err: fmt.Errorf("get file descriptor: %s", err)}
}
defer fileDescriptorSource.Close()

if err = syscall.SetsockoptInt(int(fileDescriptorSource.Fd()), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
return nil, &net.OpError{Op: "listen", Net: network, Source: nil, Addr: laddr, Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %s", err)}
}

return &Listener{listener}, nil
}

const (
IP6T_SO_ORIGINAL_DST = 80
SO_ORIGINAL_DST = 80
Expand Down
Loading

0 comments on commit 0fa9071

Please sign in to comment.