mirror of
https://github.com/restic/restic.git
synced 2025-03-16 00:00:05 +01:00
Add document and cleanup config
Add documentation for configuring smb repository. Clean up configuration for smb. Renamed address to host. Add option to configure user in smb repo url as well. Options take highest precendence.
This commit is contained in:
parent
bc0327c884
commit
5ff9f58fbb
6 changed files with 118 additions and 34 deletions
|
@ -686,9 +686,6 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro
|
|||
return cfg, nil
|
||||
case "smb":
|
||||
cfg := loc.Config.(smb.Config)
|
||||
if err := opts.Apply(loc.Scheme, &cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cfg.User == "" {
|
||||
cfg.User = os.Getenv("RESTIC_SMB_USER")
|
||||
}
|
||||
|
@ -703,6 +700,9 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro
|
|||
if cfg.Domain == "" {
|
||||
cfg.Domain = smb.DefaultDomain
|
||||
}
|
||||
if err := opts.Apply(loc.Scheme, &cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debug.Log("opening smb repository at %#v", cfg)
|
||||
return cfg, nil
|
||||
|
|
|
@ -620,6 +620,34 @@ established.
|
|||
|
||||
.. _other-services:
|
||||
|
||||
SMB/CIFS
|
||||
********
|
||||
|
||||
In order to backup data to SMB/CIFS, you must specify the host (with port if not default port `445`) as the backend.
|
||||
You must first setup the following environment variables with the SMB credentials and the domain if it is not the default `WORKGROUP`.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ export RESTIC_SMB_USER=<MY_SMB_USER>
|
||||
$ export RESTIC_SMB_PASSWORD=<MY_SMB_PASSWORD>
|
||||
$ export RESTIC_SMB_DOMAIN=<MY_SMB_DOMAIN>
|
||||
|
||||
|
||||
Once the server is configured, the setup of the SFTP repository can
|
||||
simply be achieved by changing the URL scheme in the ``init`` command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ restic -r smb://user@host:445/sharename/restic-repo init
|
||||
enter password for new repository:
|
||||
enter password again:
|
||||
created restic repository c7s8ffs329 at smb://host:445/sharename/restic-repo
|
||||
Please note that knowledge of your password is required to access the repository.
|
||||
Losing your password means that your data is irrecoverably lost.
|
||||
|
||||
Optionally, you can also pass the ``user``, ``password`` and ``domain`` as options. Configurations specified as options take highest precendence.
|
||||
You can also specify other smb specific optional configurations like ``dialect``, ``client-guid``, ``require-message-signing``, ``idle-timeout`` and ``connections`` as options.
|
||||
|
||||
Other Services via rclone
|
||||
*************************
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package smb
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -12,7 +13,7 @@ import (
|
|||
|
||||
// Config contains all configuration necessary to connect to an SMB server
|
||||
type Config struct {
|
||||
Address string
|
||||
Host string
|
||||
Port int
|
||||
ShareName string
|
||||
Path string
|
||||
|
@ -25,7 +26,7 @@ type Config struct {
|
|||
Connections uint `option:"connections" help:"set a limit for the number of concurrent operations (default: 2)"`
|
||||
IdleTimeout time.Duration `option:"idle-timeout" help:"Max time in seconds before closing idle connections. If no connections have been returned to the connection pool in the time given, the connection pool will be emptied. Set to 0 to keep connections indefinitely.(default: 60)"`
|
||||
RequireMessageSigning bool `option:"require-message-signing" help:"Mandates message signing otherwise does not allow the connection. If this is false, messaging signing is just enabled and not enforced. (default: false)"`
|
||||
Dialect uint16 `option:"dialect" help:"Force a specific dialect to be used. SMB311:785, SMB302:770, SMB300:768, SMB210:528, SMB202:514, SMB2:767. If unspecfied (0), following dialects are tried in order - SMB311, SMB302, SMB300, SMB210, SMB202 (default: 0)"`
|
||||
Dialect uint16 `option:"dialect" help:"Force a specific dialect to be used. For SMB311 use '785', for SMB302 use '770', for SMB300 use '768', for SMB210 use '528', for SMB202 use '514', for SMB2 use '767'. If unspecfied (0), following dialects are tried in order - SMB311, SMB302, SMB300, SMB210, SMB202 (default: 0)"`
|
||||
ClientGuid string `option:"client-guid" help:"A 16-byte GUID to uniquely identify a client. If not specific a random GUID is used. (default: \"\")"`
|
||||
}
|
||||
|
||||
|
@ -50,40 +51,64 @@ func init() {
|
|||
options.Register("smb", Config{})
|
||||
}
|
||||
|
||||
// ParseConfig parses the string s and extracts the s3 config. The two
|
||||
// supported configuration formats are smb://address:port/sharename/directory and
|
||||
// smb://address/sharename/directory in which case default port 445 is used.
|
||||
// If no prefix is given the prefix "restic" will be used.
|
||||
// ParseConfig parses the string s and extracts the s3 config. The
|
||||
// supported configuration format is smb://[user@]host[:port]/sharename/directory.
|
||||
// User and port are optional. Default port is 445.
|
||||
func ParseConfig(s string) (interface{}, error) {
|
||||
var repo string
|
||||
switch {
|
||||
case strings.HasPrefix(s, "smb://"):
|
||||
s = s[6:]
|
||||
repo = s
|
||||
case strings.HasPrefix(s, "smb:"):
|
||||
s = s[4:]
|
||||
repo = "smb://" + s[4:]
|
||||
default:
|
||||
return nil, errors.New("smb: invalid format")
|
||||
}
|
||||
// use the first entry of the path as the endpoint and the
|
||||
// remainder as bucket name and prefix
|
||||
fullAddress, rest, _ := strings.Cut(s, "/")
|
||||
address, portString, hasPort := strings.Cut(fullAddress, ":")
|
||||
var port int
|
||||
if !hasPort {
|
||||
port = DefaultSmbPort
|
||||
var user, host, port, dir string
|
||||
|
||||
// parse the "smb://user@host/sharename/directory." url format
|
||||
url, err := url.Parse(repo)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
if url.User != nil {
|
||||
user = url.User.Username()
|
||||
//Intentionally not allowing passwords to be set in url as
|
||||
//it can cause issues when passwords have special characters
|
||||
//like '@' and it is not recommended to pass passwords in the url.
|
||||
}
|
||||
|
||||
host = url.Hostname()
|
||||
if host == "" {
|
||||
return nil, errors.New("smb: invalid format, host name not found")
|
||||
}
|
||||
port = url.Port()
|
||||
dir = url.Path
|
||||
if dir == "" {
|
||||
return nil, errors.Errorf("smb: invalid format, sharename/directory not found")
|
||||
}
|
||||
|
||||
dir = dir[1:]
|
||||
|
||||
var portNum int
|
||||
if port == "" {
|
||||
portNum = DefaultSmbPort
|
||||
} else {
|
||||
var err error
|
||||
port, err = strconv.Atoi(portString)
|
||||
portNum, err = strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
sharename, directory, _ := strings.Cut(rest, "/")
|
||||
return createConfig(address, port, sharename, directory)
|
||||
|
||||
sharename, directory, _ := strings.Cut(dir, "/")
|
||||
|
||||
return createConfig(user, host, portNum, sharename, directory)
|
||||
}
|
||||
|
||||
func createConfig(address string, port int, sharename string, directory string) (interface{}, error) {
|
||||
if address == "" {
|
||||
return nil, errors.New("smb: invalid format, address not found")
|
||||
func createConfig(user string, host string, port int, sharename, directory string) (interface{}, error) {
|
||||
if host == "" {
|
||||
return nil, errors.New("smb: invalid format, Host not found")
|
||||
}
|
||||
|
||||
if directory != "" {
|
||||
|
@ -91,7 +116,8 @@ func createConfig(address string, port int, sharename string, directory string)
|
|||
}
|
||||
|
||||
cfg := NewConfig()
|
||||
cfg.Address = address
|
||||
cfg.User = user
|
||||
cfg.Host = host
|
||||
cfg.Port = port
|
||||
cfg.ShareName = sharename
|
||||
cfg.Path = directory
|
||||
|
|
|
@ -9,21 +9,51 @@ var configTests = []struct {
|
|||
s string
|
||||
cfg Config
|
||||
}{
|
||||
{"smb://shareaddress/sharename/directory", Config{
|
||||
Address: "shareaddress",
|
||||
{"smb://user@host/sharename/directory", Config{
|
||||
Host: "host",
|
||||
Port: DefaultSmbPort,
|
||||
User: "user",
|
||||
Domain: DefaultDomain,
|
||||
ShareName: "sharename",
|
||||
Path: "directory",
|
||||
Domain: DefaultDomain,
|
||||
Connections: DefaultConnections,
|
||||
IdleTimeout: DefaultIdleTimeout,
|
||||
}},
|
||||
{"smb://shareaddress:456/sharename/directory", Config{
|
||||
Address: "shareaddress",
|
||||
{"smb://user@host:456/sharename/directory", Config{
|
||||
Host: "host",
|
||||
Port: 456,
|
||||
User: "user",
|
||||
Domain: DefaultDomain,
|
||||
ShareName: "sharename",
|
||||
Path: "directory",
|
||||
Connections: DefaultConnections,
|
||||
IdleTimeout: DefaultIdleTimeout,
|
||||
}},
|
||||
{"smb://host/sharename/directory", Config{
|
||||
Host: "host",
|
||||
Port: DefaultSmbPort,
|
||||
Domain: DefaultDomain,
|
||||
ShareName: "sharename",
|
||||
Path: "directory",
|
||||
Connections: DefaultConnections,
|
||||
IdleTimeout: DefaultIdleTimeout,
|
||||
}},
|
||||
{"smb://host:446/sharename/directory", Config{
|
||||
Host: "host",
|
||||
Port: 446,
|
||||
Domain: DefaultDomain,
|
||||
ShareName: "sharename",
|
||||
Path: "directory",
|
||||
Connections: DefaultConnections,
|
||||
IdleTimeout: DefaultIdleTimeout,
|
||||
}},
|
||||
{"smb:user@host:466/sharename/directory", Config{
|
||||
Host: "host",
|
||||
Port: 466,
|
||||
User: "user",
|
||||
Domain: DefaultDomain,
|
||||
ShareName: "sharename",
|
||||
Path: "directory",
|
||||
Connections: DefaultConnections,
|
||||
IdleTimeout: DefaultIdleTimeout,
|
||||
}},
|
||||
|
@ -48,7 +78,7 @@ func TestParseConfig(t *testing.T) {
|
|||
func TestParseError(t *testing.T) {
|
||||
const prefix = "smb: invalid format,"
|
||||
|
||||
for _, s := range []string{"", "/", "//", "/sharename/directory"} {
|
||||
for _, s := range []string{"", "/", "//", "host", "user@host", "user@host:445", "/sharename/directory"} {
|
||||
_, err := ParseConfig("smb://" + s)
|
||||
if err == nil || !strings.HasPrefix(err.Error(), prefix) {
|
||||
t.Errorf("expected %q, got %q", prefix, err)
|
||||
|
|
|
@ -105,7 +105,7 @@ func (b *Backend) newConnection(share string) (c *conn, err error) {
|
|||
// them from the current context
|
||||
ctx := context.Background()
|
||||
|
||||
c, err = b.dial(ctx, "tcp", b.Address+":"+strconv.Itoa(b.Port))
|
||||
c, err = b.dial(ctx, "tcp", b.Host+":"+strconv.Itoa(b.Port))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't connect SMB: %w", err)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ func newTestSuite(t testing.TB) *test.Suite {
|
|||
NewConfig: func() (interface{}, error) {
|
||||
|
||||
cfg := smb.NewConfig()
|
||||
cfg.Address = "127.0.0.1"
|
||||
cfg.Host = "127.0.0.1"
|
||||
cfg.User = "smbuser"
|
||||
cfg.ShareName = cfg.User
|
||||
cfg.Path = "Repo-" + uuid.New().String()
|
||||
|
@ -28,7 +28,7 @@ func newTestSuite(t testing.TB) *test.Suite {
|
|||
cfg.IdleTimeout = timeout
|
||||
cfg.Domain = smb.DefaultDomain
|
||||
|
||||
t.Logf("create new backend at %v", cfg.Address+"/"+cfg.ShareName)
|
||||
t.Logf("create new backend at %v", cfg.Host+"/"+cfg.ShareName)
|
||||
|
||||
return cfg, nil
|
||||
},
|
||||
|
|
Loading…
Add table
Reference in a new issue