Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix scp regressions for 6.x #5729

Merged
merged 9 commits into from
Mar 4, 2021
Merged

Fix scp regressions for 6.x #5729

merged 9 commits into from
Mar 4, 2021

Conversation

a-palchikov
Copy link
Contributor

Use correct target directory path in error message.
Handle target directory/file renames properly.

I've only tested these changes manually and I was working on automatic test coverage for the rest of the use-cases but I'm not sure I can make it in time for rc.2.

Fixes #5695.

@xacrimon xacrimon added the test-plan-problem Issues which have been surfaced by running the manual release test plan label Feb 25, 2021
@russjones
Copy link
Contributor

@andrejtokarcik Can you review this?

@russjones
Copy link
Contributor

@a-palchikov If we can't make it to rc.2, let's aim for rc.3?

@a-palchikov
Copy link
Contributor Author

Ok - I'll work on the tests to be on the safe side.

Comment on lines +537 to +544
if cmd.FileSystem.IsDir(cmd.Flags.Target[0]) {
// Copying into an existing directory? append to it:
st.push(fc.Name, st.stat)
} else {
// If target specifies a new directory, we need to reset
// state with it
st.path = newPathFromDirAndTimes(cmd.Flags.Target[0], st.stat)
}
Copy link
Contributor

@andrejtokarcik andrejtokarcik Feb 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm mentioning this here just so we're aware of the current behaviour not that it's necessary to change it, feel free to close this note with no action:

This could cause unexpected data duplication, especially in automated scripts which may simply rely on the fact that tsh scp always overwrites its target. For example:

$ stat /tmp/zz
stat: cannot stat '/tmp/zz': No such file or directory
$ tsh scp -r ./large-tree/ /tmp/zz/
[...]
$ diff ./large-tree/ /tmp/zz/ && echo same tree
same tree
$ tsh scp -r ./large-tree/ /tmp/zz/   # re-run the same tsh scp command
[...]
$ diff ./large-tree/ /tmp/zz/
Only in /tmp/zz/: large-tree

The files of large-tree would end up in two copies under /tmp/zz: once directly under /tmp/zz, once under /tmp/zz/large-tree.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is consistent with OpenSSH client's behavior - initial attempt to copy a directory into a new non-existing location causes a rename, but existing location appends to it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't test it right now but IIRC, OpenSSH scp changes its dir-copying behaviour based on the trailing slash which I think tsh scp would not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That could be possible - I'll test that as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

client (ubuntu):

$ ssh -V
OpenSSH_8.4p1, OpenSSL 1.1.1f  31 Mar 2020

server (centos):

$ ssh -V
OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017

Trailing slash does not change the behavior as described in the linked issue.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@a-palchikov I this code is always run on the client? Server process is sshd?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the server is sshd. It does not have a version flag but outputs the version anyways:

$ sshd -V
unknown option -- V
OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017

@andrejtokarcik
Copy link
Contributor

BTW I had to apply the following to make tsh scp work with zsh:

diff --git a/lib/srv/exec.go b/lib/srv/exec.go
index e8cf6232d..a822b0293 100644
--- a/lib/srv/exec.go
+++ b/lib/srv/exec.go
@@ -247,7 +247,7 @@ func (e *localExec) transformSecureCopy() error {
        if err != nil {
                return trace.Wrap(err)
        }
-       e.Command = fmt.Sprintf("%s scp --remote-addr=%s --local-addr=%s %v",
+       e.Command = fmt.Sprintf("%s scp --remote-addr=%q --local-addr=%q %v",
                teleportBin,
                e.Ctx.ServerConn.RemoteAddr().String(),
                e.Ctx.ServerConn.LocalAddr().String(),

The encountered error was:

zsh:1: no matches found: --remote-addr=[::1]:59236

@a-palchikov a-palchikov force-pushed the dmitri/5695-scp branch 2 times, most recently from 3a02ae8 to 3f8e3fb Compare February 26, 2021 16:34
args := append(tt.args, source)

// Source is missing, expect an error.
err = runSCP(cmd, args...)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I factored this into a dedicated test - no need to test this for each local testcase.

@@ -49,7 +49,7 @@ func (l *localFileSystem) Chtimes(path string, atime, mtime time.Time) error {
// MkDir creates a directory
func (l *localFileSystem) MkDir(path string, mode int) error {
fileMode := os.FileMode(mode & int(os.ModePerm))
err := os.MkdirAll(path, fileMode)
err := os.Mkdir(path, fileMode)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This differed from OpenSSH's behavior which does not automatically create intermediate directories. If that's a concern, we can revert this change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adhering to OpenSSH behaviour seems more logical to me. When people type scp they expect it to behave just like scp does when it comes to things like this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, we should be as consistent as possible with OpenSSH.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alex-kovoy Any reason you made this MkdirAll? #2055

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@russjones That was original implementation, it appeared in my PR because I move some code around while working on HTTP transfers. See the original (deleted) scp.go file in that PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm, interesting the original commit seems to indicate MkdirAll lines up with OpenSSH behavior: 80df1bb

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original commit that introduced this changes is 2ad2fb4 and it seems to be fixing this problem from #274.
Since the solution caused another issue, it was changed in c6c77a1 but os.MkdirAll remained.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for the behavior of OpenSSH (7.4p1 from above) and the following source layout:

$ tree foo
foo
├── foo1
└── foo-dir
    └── foo-file

Copying to a non-existing directory (zz, 1 level), renames the source into it (or creates it remotely and copies the contents of the source directory):

$ scp -r ./foo [email protected]:zz
foo1                                                                                     100%    0     0.0KB/s   00:00    
foo-file                                                                                 100%    0     0.0KB/s   00:00

with the resulting remote layout:

# tree zz
zz
├── foo1
└── foo-dir
    └── foo-file

while opening into a directory with an intermediate non-existing directory:

$ scp -r ./foo [email protected]:non-existing/zz
scp: non-existing/zz: No such file or directory

but it works with tsh in 5.2.1.

@russjones
Copy link
Contributor

@xacrimon @andrejtokarcik Can you to re-review?

@@ -49,7 +49,7 @@ func (l *localFileSystem) Chtimes(path string, atime, mtime time.Time) error {
// MkDir creates a directory
func (l *localFileSystem) MkDir(path string, mode int) error {
fileMode := os.FileMode(mode & int(os.ModePerm))
err := os.MkdirAll(path, fileMode)
err := os.Mkdir(path, fileMode)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adhering to OpenSSH behaviour seems more logical to me. When people type scp they expect it to behave just like scp does when it comes to things like this.

a-palchikov added a commit that referenced this pull request Feb 26, 2021
Copy link
Contributor

@russjones russjones left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bot.

@russjones
Copy link
Contributor

@a-palchikov Just to clarify, we now match OpenSSH behavior right? I agree, I think that's what users expectation here is.

@a-palchikov
Copy link
Contributor Author

@a-palchikov Just to clarify, we now match OpenSSH behavior right? I agree, I think that's what users expectation here is.

Yes, w.r.t to copying a directory to a remote path with intermediates that do not exist.

a-palchikov added a commit that referenced this pull request Mar 2, 2021
russjones pushed a commit that referenced this pull request Mar 2, 2021
@russjones russjones merged commit ae4e5bf into master Mar 4, 2021
@russjones russjones deleted the dmitri/5695-scp branch March 4, 2021 01:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
test-plan-problem Issues which have been surfaced by running the manual release test plan
Projects
None yet
Development

Successfully merging this pull request may close these issues.

scp regression on 6.0.0-rc.1
6 participants