diff --git a/README.md b/README.md index 2ad2dcd9..45a20fd5 100644 --- a/README.md +++ b/README.md @@ -258,7 +258,9 @@ Configure `frps` same as above. 2. Visit `http://x.x.x.x:6000/static/` from your browser and specify correct user and password to view files in `/tmp/files` on the `frpc` machine. -### Enable HTTPS for local HTTP service +### Enable HTTPS for local HTTP(S) service + +You may substitute `https2https` for the plugin, and point the `plugin_local_addr` to a HTTPS endpoint. 1. Start `frpc` with configuration: diff --git a/conf/frpc_full.ini b/conf/frpc_full.ini index 1b5c700e..12b92b6c 100644 --- a/conf/frpc_full.ini +++ b/conf/frpc_full.ini @@ -246,6 +246,16 @@ plugin_key_path = ./server.key plugin_host_header_rewrite = 127.0.0.1 plugin_header_X-From-Where = frp +[plugin_https2https] +type = https +custom_domains = test.yourdomain.com +plugin = https2https +plugin_local_addr = 127.0.0.1:443 +plugin_crt_path = ./server.crt +plugin_key_path = ./server.key +plugin_host_header_rewrite = 127.0.0.1 +plugin_header_X-From-Where = frp + [plugin_http2https] type = http custom_domains = test.yourdomain.com diff --git a/pkg/plugin/client/https2https.go b/pkg/plugin/client/https2https.go new file mode 100644 index 00000000..159ed398 --- /dev/null +++ b/pkg/plugin/client/https2https.go @@ -0,0 +1,138 @@ +// Copyright 2019 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "crypto/tls" + "fmt" + "io" + "net" + "net/http" + "net/http/httputil" + "strings" + + frpNet "github.com/fatedier/frp/pkg/util/net" +) + +const PluginHTTPS2HTTPS = "https2https" + +func init() { + Register(PluginHTTPS2HTTPS, NewHTTPS2HTTPSPlugin) +} + +type HTTPS2HTTPSPlugin struct { + crtPath string + keyPath string + hostHeaderRewrite string + localAddr string + headers map[string]string + + l *Listener + s *http.Server +} + +func NewHTTPS2HTTPSPlugin(params map[string]string) (Plugin, error) { + crtPath := params["plugin_crt_path"] + keyPath := params["plugin_key_path"] + localAddr := params["plugin_local_addr"] + hostHeaderRewrite := params["plugin_host_header_rewrite"] + headers := make(map[string]string) + for k, v := range params { + if !strings.HasPrefix(k, "plugin_header_") { + continue + } + if k = strings.TrimPrefix(k, "plugin_header_"); k != "" { + headers[k] = v + } + } + + if crtPath == "" { + return nil, fmt.Errorf("plugin_crt_path is required") + } + if keyPath == "" { + return nil, fmt.Errorf("plugin_key_path is required") + } + if localAddr == "" { + return nil, fmt.Errorf("plugin_local_addr is required") + } + + listener := NewProxyListener() + + p := &HTTPS2HTTPSPlugin{ + crtPath: crtPath, + keyPath: keyPath, + localAddr: localAddr, + hostHeaderRewrite: hostHeaderRewrite, + headers: headers, + l: listener, + } + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + + rp := &httputil.ReverseProxy{ + Director: func(req *http.Request) { + req.URL.Scheme = "https" + req.URL.Host = p.localAddr + if p.hostHeaderRewrite != "" { + req.Host = p.hostHeaderRewrite + } + for k, v := range p.headers { + req.Header.Set(k, v) + } + }, + Transport: tr, + } + + p.s = &http.Server{ + Handler: rp, + } + + tlsConfig, err := p.genTLSConfig() + if err != nil { + return nil, fmt.Errorf("gen TLS config error: %v", err) + } + ln := tls.NewListener(listener, tlsConfig) + + go p.s.Serve(ln) + return p, nil +} + +func (p *HTTPS2HTTPSPlugin) genTLSConfig() (*tls.Config, error) { + cert, err := tls.LoadX509KeyPair(p.crtPath, p.keyPath) + if err != nil { + return nil, err + } + + config := &tls.Config{Certificates: []tls.Certificate{cert}} + return config, nil +} + +func (p *HTTPS2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) { + wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn) + p.l.PutConn(wrapConn) +} + +func (p *HTTPS2HTTPSPlugin) Name() string { + return PluginHTTPS2HTTP +} + +func (p *HTTPS2HTTPSPlugin) Close() error { + if err := p.s.Close(); err != nil { + return err + } + return nil +}