Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/proto/teleport/legacy/types/events/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1501,6 +1501,7 @@ enum SFTPAction {
RENAME = 16;
READLINK = 17;
SYMLINK = 18;
LINK = 19;
}

// SFTP is emitted when file operations have occurred between server and client
Expand Down
1,306 changes: 655 additions & 651 deletions api/types/events/events.pb.go

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,6 @@ replace (
github.com/julienschmidt/httprouter => github.com/gravitational/httprouter v1.3.1-0.20220408074523-c876c5e705a5
github.com/keys-pub/go-libfido2 => github.com/gravitational/go-libfido2 v1.5.3-0.20230202181331-c71192ef1c8a
github.com/microsoft/go-mssqldb => github.com/gravitational/go-mssqldb v0.11.1-0.20230209180904-0f00ae61fb60
github.com/pkg/sftp => github.com/gravitational/sftp v1.13.6-0.20220927202521-0e74d42f8055
github.com/sirupsen/logrus => github.com/gravitational/logrus v1.4.4-0.20210817004754-047e20245621
github.com/vulcand/predicate => github.com/gravitational/predicate v1.3.0
// Use our internal crypto fork, to work around the issue with OpenSSH <= 7.6 mentioned here: https://github.com/golang/go/issues/53391
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -680,8 +680,6 @@ github.com/gravitational/redis/v9 v9.0.0-teleport.3 h1:Eg/j3jiNUZ558KDXOqzF682EF
github.com/gravitational/redis/v9 v9.0.0-teleport.3/go.mod h1:8et+z03j0l8N+DvsVnclzjf3Dl/pFHgRk+2Ct1qw66A=
github.com/gravitational/roundtrip v1.0.2 h1:eOCY0NEKKaB0ksJmvhO6lPMFz1pIIef+vyPBTBROQ5c=
github.com/gravitational/roundtrip v1.0.2/go.mod h1:fuI1booM2hLRA/B/m5MRAPOU6mBZNYcNycono2UuTw0=
github.com/gravitational/sftp v1.13.6-0.20220927202521-0e74d42f8055 h1:iUQqKNaW1lywWSVZkeCQX0vygPfJ63UGx6Ng94e2t1o=
github.com/gravitational/sftp v1.13.6-0.20220927202521-0e74d42f8055/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg=
github.com/gravitational/trace v1.2.1 h1:Iaf43aqbKV5H8bdiRs1qByjEHgAfADJ0lt0JwRyu+q8=
github.com/gravitational/trace v1.2.1/go.mod h1:n0ijrq6psJY0sOI/NzLp+xdd8xl79jjwzVOFHDY6+kQ=
github.com/gravitational/ttlmap v0.0.0-20171116003245-91fd36b9004c h1:C2iWDiod8vQ3YnOiCdMP9qYeg2UifQ8KSk36r0NswSE=
Expand Down Expand Up @@ -1076,6 +1074,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go=
github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg=
github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down
77 changes: 75 additions & 2 deletions integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7282,9 +7282,12 @@ func testSFTP(t *testing.T, suite *integrationTestSuite) {
require.NoError(t, testFile.Close())
})

_, err = testFile.WriteString("This is test data.")
contents := []byte("This is test data.")
_, err = testFile.Write(contents)
require.NoError(t, err)
require.NoError(t, testFile.Sync())
_, err = testFile.Seek(0, io.SeekStart)
require.NoError(t, err)

// Test stat'ing a file.
t.Run("stat", func(t *testing.T) {
Expand All @@ -7310,6 +7313,12 @@ func testSFTP(t *testing.T, suite *integrationTestSuite) {

_, err = io.Copy(downloadFile, remoteDownloadFile)
require.NoError(t, err)

_, err = downloadFile.Seek(0, io.SeekStart)
require.NoError(t, err)
data, err := io.ReadAll(downloadFile)
require.NoError(t, err)
require.Equal(t, contents, data)
})

// Test uploading a file.
Expand All @@ -7323,11 +7332,75 @@ func testSFTP(t *testing.T, suite *integrationTestSuite) {

_, err = io.Copy(remoteUploadFile, testFile)
require.NoError(t, err)

_, err = remoteUploadFile.Seek(0, io.SeekStart)
require.NoError(t, err)
data, err := io.ReadAll(remoteUploadFile)
require.NoError(t, err)
require.Equal(t, contents, data)
})

// Test changing file permissions.
t.Run("chmod", func(t *testing.T) {
err = sftpClient.Chmod(testFilePath, 0o777)
err := sftpClient.Chmod(testFilePath, 0o777)
require.NoError(t, err)

fi, err := os.Stat(testFilePath)
require.NoError(t, err)
require.Equal(t, fs.FileMode(0o777), fi.Mode().Perm())
})

// Test operations on a directory.
t.Run("mkdir", func(t *testing.T) {
dirPath := filepath.Join(tempDir, "dir")
require.NoError(t, sftpClient.Mkdir(dirPath))

err := sftpClient.Chmod(dirPath, 0o777)
require.NoError(t, err)

fi, err := os.Stat(dirPath)
require.NoError(t, err)
require.Equal(t, fs.FileMode(0o777), fi.Mode().Perm())

f, err := sftpClient.Create(filepath.Join(dirPath, "file"))
require.NoError(t, err)
require.NoError(t, f.Close())

fileInfos, err := sftpClient.ReadDir(dirPath)
require.NoError(t, err)
require.Len(t, fileInfos, 1)
require.Equal(t, "file", fileInfos[0].Name())
})

// Test renaming a file.
t.Run("rename", func(t *testing.T) {
path := filepath.Join(tempDir, "to-be-renamed")
f, err := sftpClient.Create(path)
require.NoError(t, err)
require.NoError(t, f.Close())

newPath := path + "-done"
err = sftpClient.Rename(path, newPath)
require.NoError(t, err)

_, err = sftpClient.Stat(path)
require.ErrorIs(t, err, os.ErrNotExist)
_, err = sftpClient.Stat(newPath)
require.NoError(t, err)
})

// Test removing a file.
t.Run("remove", func(t *testing.T) {
path := filepath.Join(tempDir, "to-be-removed")
f, err := sftpClient.Create(path)
require.NoError(t, err)
require.NoError(t, f.Close())

err = sftpClient.Remove(path)
require.NoError(t, err)

_, err = sftpClient.Stat(path)
require.ErrorIs(t, err, os.ErrNotExist)
})

// Ensure SFTP audit events are present.
Expand Down
62 changes: 9 additions & 53 deletions lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ import (
"github.com/gravitational/teleport/lib/srv/alpnproxy"
alpncommon "github.com/gravitational/teleport/lib/srv/alpnproxy/common"
"github.com/gravitational/teleport/lib/sshutils"
"github.com/gravitational/teleport/lib/sshutils/scp"
"github.com/gravitational/teleport/lib/sshutils/sftp"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
Expand Down Expand Up @@ -1951,53 +1950,6 @@ func PlayFile(ctx context.Context, tarFile io.Reader, sid string) error {
return playSession(sessionEvents, stream)
}

// ExecuteSCP executes SCP command. It executes scp.Command using
// lower-level API integrations that mimic SCP CLI command behavior
func (tc *TeleportClient) ExecuteSCP(ctx context.Context, serverAddr string, cmd scp.Command) error {
ctx, span := tc.Tracer.Start(
ctx,
"teleportClient/ExecuteSCP",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
)
defer span.End()

// connect to proxy first:
if !tc.Config.ProxySpecified() {
return trace.BadParameter("proxy server is not specified")
}

proxyClient, err := tc.ConnectToProxy(ctx)
if err != nil {
return trace.Wrap(err)
}
defer proxyClient.Close()

nodeClient, err := tc.ConnectToNode(
ctx,
proxyClient,
// We append the ":0" to tell the server to figure out the port for us.
NodeDetails{Addr: serverAddr + ":0", Namespace: tc.Namespace, Cluster: tc.SiteName},
tc.Config.HostLogin,
)
if err != nil {
tc.ExitStatus = 1
return trace.Wrap(err)
}

err = nodeClient.ExecuteSCP(ctx, cmd)
if err != nil {
// converts SSH error code to tc.ExitStatus
exitError, _ := trace.Unwrap(err).(*ssh.ExitError)
if exitError != nil {
tc.ExitStatus = exitError.ExitStatus()
}
return err

}

return nil
}

// SFTP securely copies files between Nodes or SSH servers using SFTP
func (tc *TeleportClient) SFTP(ctx context.Context, args []string, port int, opts sftp.Options, quiet bool) (err error) {
ctx, span := tc.Tracer.Start(
Expand Down Expand Up @@ -2055,7 +2007,7 @@ func (tc *TeleportClient) uploadConfig(args []string, port int, opts sftp.Option
// copy everything except the last arg (the destination)
dstPath := args[len(args)-1]

dst, addr, err := getSCPDestination(dstPath, port)
dst, addr, err := getSFTPDestination(dstPath, port)
if err != nil {
return nil, trace.Wrap(err)
}
Expand All @@ -2077,7 +2029,7 @@ func (tc *TeleportClient) downloadConfig(args []string, port int, opts sftp.Opti
}

// args are guaranteed to have len(args) > 1
src, addr, err := getSCPDestination(args[0], port)
src, addr, err := getSFTPDestination(args[0], port)
if err != nil {
return nil, trace.Wrap(err)
}
Expand All @@ -2093,8 +2045,8 @@ func (tc *TeleportClient) downloadConfig(args []string, port int, opts sftp.Opti
}, nil
}

func getSCPDestination(target string, port int) (dest *scp.Destination, addr string, err error) {
dest, err = scp.ParseSCPDestination(target)
func getSFTPDestination(target string, port int) (dest *sftp.Destination, addr string, err error) {
dest, err = sftp.ParseDestination(target)
if err != nil {
return nil, "", trace.Wrap(err)
}
Expand Down Expand Up @@ -2131,7 +2083,11 @@ func (tc *TeleportClient) TransferFiles(ctx context.Context, hostLogin, nodeAddr
client, err := tc.ConnectToNode(
ctx,
proxyClient,
NodeDetails{Addr: nodeAddr, Namespace: tc.Namespace, Cluster: tc.SiteName},
NodeDetails{
Addr: nodeAddr,
Namespace: tc.Namespace,
Cluster: tc.SiteName,
},
hostLogin,
)
if err != nil {
Expand Down
94 changes: 0 additions & 94 deletions lib/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,9 @@ import (
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"os"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -54,7 +52,6 @@ import (
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/sshutils"
"github.com/gravitational/teleport/lib/sshutils/scp"
"github.com/gravitational/teleport/lib/sshutils/sftp"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/lib/utils/socks"
Expand Down Expand Up @@ -1749,97 +1746,6 @@ func (proxy *ProxyClient) Close() error {
return trace.NewAggregate(proxy.Client.Close(), proxy.currentCluster.Close())
}

// ExecuteSCP runs remote scp command(shellCmd) on the remote server and
// runs local scp handler using SCP Command
func (c *NodeClient) ExecuteSCP(ctx context.Context, cmd scp.Command) error {
ctx, span := c.Tracer.Start(
ctx,
"nodeClient/ExecuteSCP",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
)
defer span.End()

shellCmd, err := cmd.GetRemoteShellCmd()
if err != nil {
return trace.Wrap(err)
}

s, err := c.Client.NewSession(ctx)
if err != nil {
return trace.Wrap(err)
}
defer s.Close()

stdin, err := s.StdinPipe()
if err != nil {
return trace.Wrap(err)
}

stdout, err := s.StdoutPipe()
if err != nil {
return trace.Wrap(err)
}

// Stream scp's stderr so tsh gets the verbose remote error
// if the command fails
stderr, err := s.StderrPipe()
if err != nil {
return trace.Wrap(err)
}
go io.Copy(os.Stderr, stderr)

ch := utils.NewPipeNetConn(
stdout,
stdin,
utils.MultiCloser(),
&net.IPAddr{},
&net.IPAddr{},
)

execC := make(chan error, 1)
go func() {
err := cmd.Execute(ch)
if err != nil && !trace.IsEOF(err) {
log.WithError(err).Warn("Failed to execute SCP command.")
}
stdin.Close()
execC <- err
}()

runC := make(chan error, 1)
go func() {
err := s.Run(ctx, shellCmd)
if err != nil && errors.Is(err, &ssh.ExitMissingError{}) {
// TODO(dmitri): currently, if the session is aborted with (*session).Close,
// the remote side cannot send exit-status and this error results.
// To abort the session properly, Teleport needs to support `signal` request
err = nil
}
runC <- err
}()

var runErr error
select {
case <-ctx.Done():
if err := s.Close(); err != nil {
log.WithError(err).Debug("Failed to close the SSH session.")
}
err, runErr = <-execC, <-runC
case err = <-execC:
runErr = <-runC
case runErr = <-runC:
err = <-execC
}

if runErr != nil && (err == nil || trace.IsEOF(err)) {
err = runErr
}
if trace.IsEOF(err) {
err = nil
}
return trace.Wrap(err)
}

// TransferFiles transfers files over SFTP.
func (c *NodeClient) TransferFiles(ctx context.Context, cfg *sftp.Config) error {
ctx, span := c.Tracer.Start(
Expand Down
9 changes: 9 additions & 0 deletions lib/defaults/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -871,3 +871,12 @@ const (
// AzureInviteTokenName is the name of the default token to use
// when templating the script to be executed.
const AzureInviteTokenName = "azure-discovery-token"

const (
// FilePermissions are safe default permissions to use when
// creating files.
FilePermissions = 0o644
// DirectoryPermissions are safe default permissions to use when
// creating directories.
DirectoryPermissions = 0o755
)
2 changes: 2 additions & 0 deletions lib/events/codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ const (
SFTPReadlinkFailureCode = "TS017E"
SFTPSymlinkCode = "TS018I"
SFTPSymlinkFailureCode = "TS018E"
SFTPLinkCode = "TS019I"
SFTPLinkFailureCode = "TS019E"

// SessionCommandCode is a session command code.
SessionCommandCode = "T4000I"
Expand Down
2 changes: 1 addition & 1 deletion lib/srv/authhandlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ func (h *AuthHandlers) CheckX11Forward(ctx *ServerContext) error {

func (h *AuthHandlers) CheckFileCopying(ctx *ServerContext) error {
if !ctx.Identity.AccessChecker.CanCopyFiles() {
return errRoleFileCopyingNotPermitted
return trace.Wrap(errRoleFileCopyingNotPermitted)
}
return nil
}
Expand Down
Loading