From b041701b745afc2164d02b6faeac88481ad39950 Mon Sep 17 00:00:00 2001 From: aneesh-n <99904+aneesh-n@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:38:24 -0600 Subject: [PATCH] Update latest fixes from rclone SMB Update documentation. Use cloudsoda fork of smb library for fixes related to size calculations and allowing to use hostnames. --- doc/030_preparing_a_new_repo.rst | 29 +++++++++++++++++++++++------ go.mod | 2 +- go.sum | 5 ++--- internal/backend/smb/config.go | 5 ++++- internal/backend/smb/conpool.go | 18 +++++++++--------- internal/backend/smb/smb.go | 3 ++- 6 files changed, 41 insertions(+), 21 deletions(-) diff --git a/doc/030_preparing_a_new_repo.rst b/doc/030_preparing_a_new_repo.rst index 8152b8b31..df1488f97 100644 --- a/doc/030_preparing_a_new_repo.rst +++ b/doc/030_preparing_a_new_repo.rst @@ -629,18 +629,21 @@ The region, where a bucket should be created, can be specified with the ``-o gs. 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`. +To backup data to SMB/CIFS, specify the host (with port if not default port `445`) as the backend. +You can set the following environment variables with the SMB credentials: .. code-block:: console $ export RESTIC_SMB_USER= $ export RESTIC_SMB_PASSWORD= $ export RESTIC_SMB_DOMAIN= + $ export RESTIC_SMB_SPN= +The domain defaults to `WORKGROUP` if not specified. +The SPN (Service Principal Name) is optional and can be used for further authentication +with some servers, particularly clusters, for example ``cifs/remotehost:1020``. -Once the server is configured, the setup of the SMB repository can -simply be achieved by changing the URL scheme in the ``init`` command: +To set up an SMB repository, use the `smb://` URL scheme in the `init` command: .. code-block:: console @@ -651,8 +654,22 @@ simply be achieved by changing the URL scheme in the ``init`` command: 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. +Optionally, you can also pass `user`, `password`, `domain`, and `spn` as options. +Options take precedence over environment variables. + +Additional SMB-specific options include: + +- `connections`: Set the number of concurrent operations (default: 5) +- `idle-timeout`: Max time in seconds before closing idle connections (default: 60) +- `require-message-signing`: Mandate message signing (default: false) +- `dialect`: Force a specific SMB dialect (default: 0, which tries dialects in order) +- `client-guid`: A 16-byte GUID to uniquely identify a client (default: random GUID) + +Example with options: + +.. code-block:: console + + $ restic -r smb://host:445/sharename/restic-repo -o smb.user=myuser -o smb.password=mypass -o smb.connections=10 -o smb.idle-timeout=120s init Other Services via rclone ************************* diff --git a/go.mod b/go.mod index bab467b27..b5e576b16 100644 --- a/go.mod +++ b/go.mod @@ -9,12 +9,12 @@ require ( github.com/anacrolix/fuse v0.3.1 github.com/cenkalti/backoff/v4 v4.3.0 github.com/cespare/xxhash/v2 v2.3.0 + github.com/cloudsoda/go-smb2 v0.0.0-20231124195312-f3ec8ae2c891 github.com/elithrar/simple-scrypt v1.3.0 github.com/go-ole/go-ole v1.3.0 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/hashicorp/golang-lru/v2 v2.0.7 - github.com/hirochachacha/go-smb2 v1.1.0 github.com/klauspost/compress v1.17.9 github.com/minio/minio-go/v7 v7.0.74 github.com/ncw/swift/v2 v2.0.2 diff --git a/go.sum b/go.sum index f34d6c249..ef7b4a7a7 100644 --- a/go.sum +++ b/go.sum @@ -47,6 +47,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudsoda/go-smb2 v0.0.0-20231124195312-f3ec8ae2c891 h1:nPP4suUiNage0vvyEBgfAnhTPwwXhNqtHmSuiCIQwKU= +github.com/cloudsoda/go-smb2 v0.0.0-20231124195312-f3ec8ae2c891/go.mod h1:xFxVVe3plxwhM+6BgTTPByEgG8hggo8+gtRUkbc5W8Q= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -125,8 +127,6 @@ github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDP github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI= -github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE= github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -225,7 +225,6 @@ go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnw golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= diff --git a/internal/backend/smb/config.go b/internal/backend/smb/config.go index d7d0ec612..bd446f84f 100644 --- a/internal/backend/smb/config.go +++ b/internal/backend/smb/config.go @@ -29,8 +29,8 @@ type Config struct { User string `option:"user" help:"specify the SMB user for NTLM authentication."` Password options.SecretString `option:"password" help:"specify the SMB password for NTLM authentication."` Domain string `option:"domain" help:"specify the domain for authentication."` + SPN string `option:"spn" help:"specify the service principal name for authentication. This name is presented to the server. Some servers use this as further authentication, and it often needs to be set for clusters. For example: cifs/remotehost:1020. Leave blank if not sure."` - Layout string `option:"layout" help:"use this backend directory layout (default: auto-detect)"` Connections uint `option:"connections" help:"set a limit for the number of concurrent operations (default: 5)"` 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)"` @@ -117,5 +117,8 @@ func (cfg *Config) ApplyEnvironment(prefix string) error { cfg.Domain = DefaultDomain } } + if cfg.SPN == "" { + cfg.SPN = os.Getenv(prefix + "RESTIC_SMB_SPN") + } return nil } diff --git a/internal/backend/smb/conpool.go b/internal/backend/smb/conpool.go index 3720933bf..a2085eb33 100644 --- a/internal/backend/smb/conpool.go +++ b/internal/backend/smb/conpool.go @@ -6,9 +6,8 @@ import ( "fmt" "net" "strconv" - "sync/atomic" - "github.com/hirochachacha/go-smb2" + "github.com/cloudsoda/go-smb2" "github.com/restic/restic/internal/debug" ) @@ -67,17 +66,17 @@ func (c *conn) isClosed() bool { // addSession increments the active session count when an SMB session needs to be used. // If this is called, we must call removeSession when we are done using the session. func (b *SMB) addSession() { - atomic.AddInt32(&b.sessions, 1) + b.sessions.Add(1) } // removeSession decrements the active session count when it is no longer in use. func (b *SMB) removeSession() { - atomic.AddInt32(&b.sessions, -1) + b.sessions.Add(-1) } // getSessionCount returns the number of active sessions. func (b *SMB) getSessionCount() int32 { - return atomic.LoadInt32(&b.sessions) + return b.sessions.Load() } // dial starts a client connection to the given SMB server. It is a @@ -99,13 +98,14 @@ func (b *SMB) dial(ctx context.Context, network, addr string) (*conn, error) { ClientGuid: clientID, }, Initiator: &smb2.NTLMInitiator{ - User: b.User, - Password: b.Password.Unwrap(), - Domain: b.Domain, + User: b.User, + Password: b.Password.Unwrap(), + Domain: b.Domain, + TargetSPN: b.SPN, }, } - session, err := d.DialContext(ctx, netConn) + session, err := d.DialConn(ctx, netConn, addr) if err != nil { return nil, fmt.Errorf("SMB session initialization failed: %w", err) } diff --git a/internal/backend/smb/smb.go b/internal/backend/smb/smb.go index 299bcd1a3..0f4383aeb 100644 --- a/internal/backend/smb/smb.go +++ b/internal/backend/smb/smb.go @@ -10,6 +10,7 @@ import ( "path" "path/filepath" "sync" + "sync/atomic" "time" "github.com/restic/restic/internal/backend" @@ -44,7 +45,7 @@ import ( // SMB is a backend which stores the data on an SMB share. type SMB struct { - sessions int32 + sessions atomic.Int32 poolMu sync.Mutex pool []*conn drain *time.Timer // used to drain the pool when we stop using the connections