diff --git a/docs/content/docs.md b/docs/content/docs.md index 8947cbe11..449331de0 100644 --- a/docs/content/docs.md +++ b/docs/content/docs.md @@ -991,6 +991,47 @@ with this setting. Prints the version number +SSL/TLS options +--------------- + +The outoing SSL/TLS connections rclone makes can be controlled with +these options. For example this can be very useful with the HTTP or +WebDAV backends. Rclone HTTP servers have their own set of +configuration for SSL/TLS which you can find in their documentation. + +### --ca-cert string + +This loads the PEM encoded certificate authority certificate and uses +it to verify the certificates of the servers rclone connects to. + +If you have generated certificates signed with a local CA then you +will need this flag to connect to servers using those certificates. + +### --client-cert string + +This loads the PEM encoded client side certificate. + +This is used for [mutual TLS authentication](https://en.wikipedia.org/wiki/Mutual_authentication). + +The `--client-key` flag is required too when using this. + +### --client-key string + +This loads the PEM encoded client side private key used for mutual TLS +authentication. Used in conjunction with `--client-cert`. + +### --no-check-certificate=true/false ### + +`--no-check-certificate` controls whether a client verifies the +server's certificate chain and host name. +If `--no-check-certificate` is true, TLS accepts any certificate +presented by the server and any host name in that certificate. +In this mode, TLS is susceptible to man-in-the-middle attacks. + +This option defaults to `false`. + +**This should be used only for testing.** + Configuration Encryption ------------------------ Your configuration file contains information for logging in to @@ -1147,18 +1188,6 @@ use it. Write memory profile to file. This can be analysed with `go tool pprof`. -### --no-check-certificate=true/false ### - -`--no-check-certificate` controls whether a client verifies the -server's certificate chain and host name. -If `--no-check-certificate` is true, TLS accepts any certificate -presented by the server and any host name in that certificate. -In this mode, TLS is susceptible to man-in-the-middle attacks. - -This option defaults to `false`. - -**This should be used only for testing.** - ### --no-traverse ### The `--no-traverse` flag controls whether the destination file system diff --git a/fs/config.go b/fs/config.go index b45c4e18b..6f91e72f2 100644 --- a/fs/config.go +++ b/fs/config.go @@ -87,6 +87,9 @@ type ConfigInfo struct { Progress bool Cookie bool UseMmap bool + CaCert string // Client Side CA + ClientCert string // Client Side Cert + ClientKey string // Client Side Key } // NewConfig creates a new config with everything set to the default diff --git a/fs/config/configflags/configflags.go b/fs/config/configflags/configflags.go index 2e22785c4..47c572f78 100644 --- a/fs/config/configflags/configflags.go +++ b/fs/config/configflags/configflags.go @@ -89,6 +89,9 @@ func AddFlags(flagSet *pflag.FlagSet) { flags.BoolVarP(flagSet, &fs.Config.Progress, "progress", "P", fs.Config.Progress, "Show progress during transfer.") flags.BoolVarP(flagSet, &fs.Config.Cookie, "use-cookies", "", fs.Config.Cookie, "Enable session cookiejar.") flags.BoolVarP(flagSet, &fs.Config.UseMmap, "use-mmap", "", fs.Config.UseMmap, "Use mmap allocator (see docs).") + flags.StringVarP(flagSet, &fs.Config.CaCert, "ca-cert", "", fs.Config.CaCert, "CA certificate used to verify servers") + flags.StringVarP(flagSet, &fs.Config.ClientCert, "client-cert", "", fs.Config.ClientCert, "Client SSL certificate (PEM) for mutual TLS auth") + flags.StringVarP(flagSet, &fs.Config.ClientKey, "client-key", "", fs.Config.ClientKey, "Client SSL private key (PEM) for mutual TLS auth") } // SetFlags converts any flags into config which weren't straight foward diff --git a/fs/fshttp/http.go b/fs/fshttp/http.go index a11dcc0fc..fd44648fe 100644 --- a/fs/fshttp/http.go +++ b/fs/fshttp/http.go @@ -5,6 +5,9 @@ import ( "bytes" "context" "crypto/tls" + "crypto/x509" + "io/ioutil" + "log" "net" "net/http" "net/http/cookiejar" @@ -130,7 +133,39 @@ func NewTransport(ci *fs.ConfigInfo) http.RoundTripper { t.MaxIdleConns = 2 * t.MaxIdleConnsPerHost t.TLSHandshakeTimeout = ci.ConnectTimeout t.ResponseHeaderTimeout = ci.Timeout - t.TLSClientConfig = &tls.Config{InsecureSkipVerify: ci.InsecureSkipVerify} + + // TLS Config + t.TLSClientConfig = &tls.Config{ + InsecureSkipVerify: ci.InsecureSkipVerify, + } + + // Load client certs + if ci.ClientCert != "" || ci.ClientKey != "" { + if ci.ClientCert == "" || ci.ClientKey == "" { + log.Fatalf("Both --client-cert and --client-key must be set") + } + cert, err := tls.LoadX509KeyPair(ci.ClientCert, ci.ClientKey) + if err != nil { + log.Fatalf("Failed to load --client-cert/--client-key pair: %v", err) + } + t.TLSClientConfig.Certificates = []tls.Certificate{cert} + t.TLSClientConfig.BuildNameToCertificate() + } + + // Load CA cert + if ci.CaCert != "" { + caCert, err := ioutil.ReadFile(ci.CaCert) + if err != nil { + log.Fatalf("Failed to read --ca-cert: %v", err) + } + caCertPool := x509.NewCertPool() + ok := caCertPool.AppendCertsFromPEM(caCert) + if !ok { + log.Fatalf("Failed to add certificates from --ca-cert") + } + t.TLSClientConfig.RootCAs = caCertPool + } + t.DisableCompression = ci.NoGzip t.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { return dialContextTimeout(ctx, network, addr, ci)