Files
teleport/lib/srv/exec_test.go
T
Edoardo Spadolini bcffdcbfd6 session-helper: move the sftp subcommand to reexec.RunAndExit (#65392)
* Split the approver map away from FileTransferRequest

* Move or copy files in session/reexec/sftputils and session/reexec/reexecsftp

* Split up copied files and clean up the new session packages

* Use custom types for SFTP audit log events

* Clean up imports and run sftp in RunAndExit

* Finish renaming types and functions

* Streamline reexec in main and tests

* Add test with the legacy SFTP event implementation
2026-04-15 13:28:27 +00:00

160 lines
4.6 KiB
Go

/*
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package srv
import (
"errors"
"os"
"os/user"
"strconv"
"testing"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/ssh"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/sshutils"
"github.com/gravitational/teleport/lib/utils/log/logtest"
"github.com/gravitational/teleport/session/reexec"
"github.com/gravitational/teleport/session/reexec/reexecconstants"
)
// TestMain will re-execute Teleport to run a command if "exec" is passed to
// it as an argument. Otherwise, it will run tests as normal.
func TestMain(m *testing.M) {
reexec.MaybeReexec()
logtest.InitLogger(testing.Verbose)
modules.SetInsecureTestMode(true)
// Otherwise run tests as normal.
code := m.Run()
os.Exit(code)
}
// TestEmitExecAuditEvent make sure the full command and exit code for a
// command is always recorded.
func TestEmitExecAuditEvent(t *testing.T) {
t.Parallel()
srv := newMockServer(t)
scx := newExecServerContext(t, srv)
rec, ok := scx.session.recorder.(*mockRecorder)
require.True(t, ok)
expectedUsr, err := user.Current()
require.NoError(t, err)
expectedHostname := "testHost"
expectedMeta := apievents.UserMetadata{
User: "teleportUser",
Login: expectedUsr.Username,
Impersonator: "",
AWSRoleARN: "",
AccessRequests: []string(nil),
UserKind: apievents.UserKind_USER_KIND_HUMAN,
XXX_NoUnkeyedLiteral: struct{}{},
XXX_unrecognized: []uint8(nil),
XXX_sizecache: 0,
}
tests := []struct {
name string
inResult ExecResult
}{
{
name: "success",
inResult: ExecResult{
Command: "exit 0",
Error: nil,
Code: reexecconstants.RemoteCommandSuccess,
},
},
{
name: "exit with error",
inResult: ExecResult{
Command: "exit 255",
Error: errors.New("exit status 255"),
Code: reexecconstants.RemoteCommandFailure,
},
},
{
name: "command injection",
inResult: ExecResult{
Command: "/bin/teleport scp --remote-addr=127.0.0.1:50862 --local-addr=127.0.0.1:54895 -f ~/file.txt && touch /tmp/new.txt",
Error: errors.New("unknown error"),
Code: reexecconstants.RemoteCommandFailure,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
emitExecAuditEvent(scx, tt.inResult)
execEvent := rec.emitter.LastEvent().(*apievents.Exec)
require.Equal(t, tt.inResult.Command, execEvent.Command)
if tt.inResult.Error != nil {
require.Equal(t, tt.inResult.Error.Error(), execEvent.Error)
} else {
require.Empty(t, execEvent.Error)
}
if tt.inResult.Code == 0 {
require.Equal(t, events.ExecCode, execEvent.Code)
} else {
require.Equal(t, events.ExecFailureCode, execEvent.Code)
}
require.Equal(t, strconv.Itoa(tt.inResult.Code), execEvent.ExitCode)
require.Equal(t, expectedMeta, execEvent.UserMetadata)
require.Equal(t, "123", execEvent.ServerID)
require.Equal(t, "abc", execEvent.ForwardedBy)
require.Equal(t, expectedHostname, execEvent.ServerHostname)
require.Equal(t, "testNamespace", execEvent.ServerNamespace)
require.Equal(t, "xxx", execEvent.SessionID)
require.Equal(t, "10.0.0.5:4817", execEvent.RemoteAddr)
require.Equal(t, "127.0.0.1:3022", execEvent.LocalAddr)
require.NotEmpty(t, events.EventID)
})
}
}
func newExecServerContext(t *testing.T, srv Server) *ServerContext {
scx := newTestServerContext(t, srv, nil, nil)
term, err := newLocalTerminal(scx)
require.NoError(t, err)
term.SetTermType("xterm")
rec := &mockRecorder{done: false}
scx.session = &session{
id: "xxx",
term: term,
emitter: rec,
recorder: rec,
scx: scx,
}
err = scx.SetSSHRequest(&ssh.Request{Type: sshutils.ExecRequest})
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, scx.session.term.Close()) })
return scx
}