refactor SFTP backend to use upstream dep, not our fork (#23786)

* refactor SFTP backend to use upstream dep, not our fork

This change also greatly reduces the number of SFTP audit logs.
Now SFTP events are only sent when files are opened or modified
in any way, instead of for *every* SFTP request.

* added to SFTP integration test

* fix error when handling setstat on dirs

* fix linter warning

* move file/dir permission constants to lib/defaults package
This commit is contained in:
Andrew LeFevre 2023-04-06 21:51:22 -04:00 committed by GitHub
parent ae66157f3e
commit 40c113b8da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 598 additions and 242 deletions

View file

@ -1504,6 +1504,7 @@ enum SFTPAction {
RENAME = 16;
READLINK = 17;
SYMLINK = 18;
LINK = 19;
}
// SFTP is emitted when file operations have occurred between server and client

View file

@ -82,6 +82,7 @@ const (
SFTPAction_RENAME SFTPAction = 16
SFTPAction_READLINK SFTPAction = 17
SFTPAction_SYMLINK SFTPAction = 18
SFTPAction_LINK SFTPAction = 19
)
var SFTPAction_name = map[int32]string{
@ -104,6 +105,7 @@ var SFTPAction_name = map[int32]string{
16: "RENAME",
17: "READLINK",
18: "SYMLINK",
19: "LINK",
}
var SFTPAction_value = map[string]int32{
@ -126,6 +128,7 @@ var SFTPAction_value = map[string]int32{
"RENAME": 16,
"READLINK": 17,
"SYMLINK": 18,
"LINK": 19,
}
func (x SFTPAction) String() string {
@ -9490,7 +9493,7 @@ func init() {
}
var fileDescriptor_007ba1c3d6266d56 = []byte{
// 10534 bytes of a gzipped FileDescriptorProto
// 10538 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x6b, 0x8c, 0x1c, 0xc9,
0x79, 0xd8, 0xce, 0x63, 0xe7, 0x51, 0xb3, 0x8f, 0xd9, 0x22, 0x79, 0xec, 0x23, 0x79, 0x9c, 0xbb,
0x3e, 0x89, 0x47, 0x9e, 0x78, 0x4b, 0xf1, 0xa1, 0xa3, 0xee, 0xa4, 0xd3, 0xdd, 0xec, 0xcc, 0x2c,
@ -9498,7 +9501,7 @@ var fileDescriptor_007ba1c3d6266d56 = []byte{
0x67, 0xa6, 0x47, 0xdd, 0x3d, 0x5c, 0xee, 0xfd, 0x8a, 0x10, 0x27, 0x51, 0x02, 0xc5, 0x08, 0x1c,
0x18, 0x0e, 0x90, 0x1f, 0xb6, 0x03, 0x04, 0x48, 0x10, 0xc3, 0x82, 0x13, 0xc3, 0x8e, 0x91, 0x18,
0x79, 0x48, 0x49, 0xce, 0x51, 0x2c, 0x5b, 0x71, 0xe0, 0x00, 0xf9, 0x31, 0x4a, 0x14, 0x04, 0x01,
0x16, 0x09, 0x22, 0x24, 0x02, 0x62, 0x24, 0xc8, 0x03, 0xf5, 0x55, 0x75, 0x77, 0x55, 0x77, 0xcf,
0x16, 0x09, 0x20, 0x24, 0x02, 0xe2, 0x24, 0xc8, 0x03, 0xf5, 0x55, 0x75, 0x77, 0x55, 0x77, 0xcf,
0x3e, 0xb8, 0x3c, 0xaf, 0xf6, 0x76, 0xff, 0x90, 0x3b, 0xdf, 0xab, 0xba, 0xbf, 0xfa, 0xba, 0xea,
0xab, 0xaa, 0xef, 0xfb, 0x0a, 0x5d, 0x71, 0x49, 0x87, 0xf4, 0x2d, 0xdb, 0xbd, 0xd6, 0x21, 0xeb,
0x7a, 0x7b, 0xeb, 0x9a, 0xbb, 0xd5, 0x27, 0xce, 0x35, 0xf2, 0x88, 0xf4, 0x5c, 0xef, 0xbf, 0xd9,
@ -9622,7 +9625,7 @@ var fileDescriptor_007ba1c3d6266d56 = []byte{
0xd9, 0x25, 0xfd, 0xb1, 0xd9, 0x1d, 0x74, 0xb9, 0x97, 0x0e, 0x47, 0x6d, 0x5d, 0xfd, 0xb1, 0xe6,
0xc1, 0xd5, 0x7f, 0x93, 0x40, 0x53, 0x5c, 0xa9, 0x5c, 0xf8, 0x81, 0xb4, 0x1a, 0x68, 0x27, 0x79,
0x60, 0xed, 0xa4, 0x9e, 0x5c, 0x3b, 0xea, 0xb7, 0xd3, 0x54, 0x3d, 0x8e, 0xf9, 0xe1, 0xb1, 0x1f,
0x78, 0x82, 0x1e, 0x19, 0x7f, 0x82, 0x1e, 0x39, 0x36, 0x3b, 0x42, 0xea, 0xff, 0xcc, 0x22, 0xc4,
0x78, 0x82, 0x1e, 0x19, 0x7f, 0x82, 0x1e, 0x39, 0x36, 0x3b, 0x42, 0xea, 0xff, 0xc8, 0x22, 0xc4,
0xb5, 0x5f, 0x3b, 0x59, 0x7d, 0x1c, 0xcc, 0x6a, 0xaa, 0x68, 0xa6, 0xd6, 0xdb, 0xd0, 0x7b, 0x6d,
0x62, 0x04, 0xfb, 0x62, 0xd4, 0x74, 0x72, 0xec, 0x1c, 0x9f, 0x70, 0x64, 0xb0, 0x31, 0xa6, 0x45,
0x19, 0xf0, 0x75, 0x54, 0xa8, 0xf7, 0x5c, 0x62, 0xeb, 0x6d, 0xd7, 0x7c, 0xc4, 0xa6, 0xb1, 0xdc,
@ -9682,7 +9685,7 @@ var fileDescriptor_007ba1c3d6266d56 = []byte{
0x34, 0x06, 0xc7, 0x9f, 0x41, 0x79, 0xde, 0xc9, 0x96, 0x77, 0x06, 0xcb, 0x96, 0x7c, 0x1e, 0x50,
0x0b, 0xf0, 0xea, 0x77, 0x13, 0x4c, 0x29, 0x55, 0xd2, 0x21, 0x47, 0x57, 0x29, 0xea, 0x37, 0x13,
0x08, 0x53, 0x61, 0x0d, 0xdd, 0x71, 0x36, 0x2d, 0xdb, 0xa8, 0x6c, 0xe8, 0xbd, 0xf5, 0x43, 0x79,
0x1d, 0xf5, 0xbf, 0x8f, 0xa3, 0x53, 0x52, 0x3c, 0xd4, 0x11, 0xb7, 0xb7, 0x2b, 0xb2, 0xbd, 0xc1,
0x1d, 0xf5, 0xbf, 0x8d, 0xa3, 0x53, 0x52, 0x3c, 0xd4, 0x11, 0xb7, 0xb7, 0x2b, 0xb2, 0xbd, 0xc1,
0xe2, 0x1d, 0xec, 0x4d, 0x5c, 0xbc, 0x33, 0xcb, 0xfb, 0x14, 0xca, 0xf3, 0x77, 0xae, 0x57, 0xb9,
0xe5, 0xc1, 0xb4, 0x6f, 0x1a, 0x5a, 0x80, 0xc0, 0xaf, 0xa0, 0x09, 0xfe, 0x83, 0x8e, 0xfe, 0xde,
0xfe, 0x2c, 0xd8, 0xb1, 0x43, 0x01, 0x9a, 0x84, 0xc6, 0x9f, 0x43, 0x79, 0x6a, 0x9c, 0xeb, 0x90,
@ -9806,7 +9809,7 @@ var fileDescriptor_007ba1c3d6266d56 = []byte{
0x50, 0xc3, 0xb2, 0x5d, 0xbd, 0x23, 0xdc, 0x36, 0x05, 0x4b, 0x86, 0x3e, 0x40, 0x19, 0x8f, 0x40,
0x82, 0x67, 0x11, 0x12, 0x3e, 0xb0, 0x2c, 0x7c, 0x60, 0x53, 0xdb, 0xc3, 0x12, 0x0a, 0xbe, 0x2b,
0x4d, 0xa0, 0x50, 0xff, 0x41, 0x12, 0x4d, 0x7b, 0x9d, 0x54, 0x7b, 0x4c, 0xda, 0x03, 0xf7, 0x18,
0x7f, 0x0c, 0xb2, 0xb6, 0xc7, 0x77, 0xd5, 0xb6, 0xfa, 0x3f, 0x84, 0x81, 0xa4, 0xd2, 0xb1, 0x4e,
0x7f, 0x0c, 0xb2, 0xb6, 0xc7, 0x77, 0xd5, 0xb6, 0xfa, 0xdf, 0x85, 0x81, 0xa4, 0xd2, 0xb1, 0x4e,
0x06, 0x92, 0x3f, 0x0d, 0x1b, 0x57, 0x7f, 0x2e, 0x85, 0x4e, 0x7b, 0x5a, 0x9f, 0x1f, 0xf4, 0xc0,
0x4d, 0xa8, 0xe8, 0x9d, 0xce, 0x71, 0x9e, 0x97, 0x0b, 0x9e, 0x22, 0x56, 0x78, 0x76, 0x2c, 0x2f,
0x54, 0xfa, 0x80, 0x83, 0x5b, 0x96, 0x69, 0x68, 0x22, 0x11, 0x7e, 0x13, 0x4d, 0x78, 0x3f, 0xcb,
@ -10130,7 +10133,7 @@ var fileDescriptor_007ba1c3d6266d56 = []byte{
0x0a, 0xf8, 0x29, 0x19, 0x3c, 0x3e, 0x6e, 0x05, 0xfc, 0x6a, 0x62, 0xd7, 0xf4, 0xf6, 0xc3, 0xd6,
0xc1, 0xcb, 0x2f, 0xa1, 0x02, 0x4c, 0x57, 0x65, 0x76, 0x65, 0xeb, 0x04, 0xca, 0xad, 0xcc, 0x35,
0x6b, 0xda, 0x3b, 0xb5, 0x6a, 0x71, 0x0c, 0x23, 0x94, 0xa9, 0xd6, 0x96, 0xeb, 0xb5, 0x6a, 0x31,
0xf1, 0xf2, 0x7f, 0x4b, 0x20, 0xd4, 0x9c, 0x5f, 0x6d, 0x70, 0xc2, 0x02, 0xca, 0xd6, 0x97, 0xdf,
0xf1, 0xf2, 0xff, 0x4c, 0x20, 0xd4, 0x9c, 0x5f, 0x6d, 0x70, 0xc2, 0x02, 0xca, 0xd6, 0x97, 0xdf,
0x29, 0x2f, 0xd6, 0x29, 0x5d, 0x0e, 0xa5, 0x57, 0x1a, 0xb5, 0xe5, 0x62, 0x02, 0xe7, 0xd1, 0x78,
0x65, 0x71, 0xa5, 0x59, 0x2b, 0x26, 0x29, 0x50, 0xab, 0x95, 0xab, 0xc5, 0x14, 0x05, 0xde, 0xd7,
0xea, 0xab, 0xb5, 0x62, 0x9a, 0xfe, 0xb9, 0xd8, 0x5c, 0x2d, 0xaf, 0x16, 0xc7, 0xe9, 0x9f, 0xf3,
@ -10139,17 +10142,17 @@ var fileDescriptor_007ba1c3d6266d56 = []byte{
0x6d, 0x69, 0xe5, 0x9d, 0x5a, 0xb1, 0x40, 0x65, 0x2d, 0xdd, 0xa5, 0xe0, 0x09, 0xfa, 0xa7, 0xb6,
0x44, 0xff, 0x9c, 0xa4, 0x92, 0xb4, 0x5a, 0x79, 0xb1, 0x51, 0x5e, 0x5d, 0x28, 0x4e, 0xd1, 0xe7,
0x01, 0x99, 0xd3, 0x8c, 0x73, 0xb9, 0xbc, 0x54, 0x2b, 0x16, 0x39, 0x4d, 0x75, 0xb1, 0xbe, 0x7c,
0xb7, 0x38, 0x03, 0x0f, 0xf2, 0xde, 0x12, 0xfc, 0xc0, 0x2f, 0xff, 0x0c, 0xca, 0xb0, 0x4b, 0x31,
0xf0, 0x59, 0x74, 0x6a, 0xa5, 0xd9, 0x5a, 0x7d, 0xaf, 0x51, 0x6b, 0xdd, 0x5b, 0x6e, 0x36, 0x6a,
0x95, 0xfa, 0x7c, 0x1d, 0x14, 0x34, 0x83, 0x26, 0x3d, 0xc4, 0x62, 0x7d, 0xf9, 0xde, 0xbb, 0xc5,
0x84, 0x08, 0x5a, 0x2a, 0x57, 0x56, 0x9a, 0xc5, 0x24, 0x3e, 0x85, 0xa6, 0x3d, 0xd0, 0xfd, 0xfa,
0x72, 0x75, 0xe5, 0x7e, 0xb3, 0x98, 0x7a, 0xf9, 0x0e, 0x3a, 0x13, 0x7b, 0x7c, 0x47, 0x9f, 0xe1,
0x4e, 0x6d, 0xb9, 0xa6, 0x95, 0x17, 0x8b, 0x63, 0xf4, 0xf1, 0x9a, 0xb5, 0xca, 0x3d, 0xad, 0xbe,
0xfa, 0x5e, 0x31, 0x41, 0x1f, 0xbc, 0x59, 0x2b, 0x6b, 0x95, 0x85, 0x62, 0x12, 0x67, 0x51, 0xaa,
0xf9, 0xf6, 0x62, 0x31, 0x35, 0x57, 0xfd, 0xe8, 0x3f, 0x5c, 0x1c, 0xfb, 0xe8, 0x47, 0x17, 0x13,
0x7f, 0xf8, 0xa3, 0x8b, 0x89, 0x7f, 0xff, 0xa3, 0x8b, 0x89, 0xaf, 0xdc, 0xd8, 0xcf, 0x61, 0x24,
0x33, 0x98, 0xb5, 0x0c, 0x6c, 0xd2, 0xdf, 0xfc, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x53, 0x7b,
0x10, 0xc1, 0xae, 0xe9, 0x00, 0x00,
0xb7, 0x38, 0x03, 0x0f, 0xf2, 0xde, 0x12, 0xfc, 0xc0, 0x94, 0x01, 0xfe, 0x3a, 0xf5, 0xf2, 0xcf,
0xa0, 0x0c, 0xbb, 0x1e, 0x03, 0x9f, 0x45, 0xa7, 0x56, 0x9a, 0xad, 0xd5, 0xf7, 0x1a, 0xb5, 0xd6,
0xbd, 0xe5, 0x66, 0xa3, 0x56, 0xa9, 0xcf, 0xd7, 0x41, 0x55, 0x33, 0x68, 0xd2, 0x43, 0x2c, 0xd6,
0x97, 0xef, 0xbd, 0x5b, 0x4c, 0x88, 0xa0, 0xa5, 0x72, 0x65, 0xa5, 0x59, 0x4c, 0xe2, 0x53, 0x68,
0xda, 0x03, 0xdd, 0xaf, 0x2f, 0x57, 0x57, 0xee, 0x37, 0x8b, 0xa9, 0x97, 0xef, 0xa0, 0x33, 0xb1,
0x07, 0x79, 0xf4, 0x69, 0xee, 0xd4, 0x96, 0x6b, 0x5a, 0x79, 0xb1, 0x38, 0x46, 0x1f, 0xb4, 0x59,
0xab, 0xdc, 0xd3, 0xea, 0xab, 0xef, 0x15, 0x13, 0xf4, 0x15, 0x9a, 0xb5, 0xb2, 0x56, 0x59, 0x28,
0x26, 0x71, 0x16, 0xa5, 0x9a, 0x6f, 0x2f, 0x16, 0x53, 0x73, 0xd5, 0x8f, 0xfe, 0xc3, 0xc5, 0xb1,
0x8f, 0x7e, 0x74, 0x31, 0xf1, 0x87, 0x3f, 0xba, 0x98, 0xf8, 0xf7, 0x3f, 0xba, 0x98, 0xf8, 0xca,
0x8d, 0xfd, 0x1c, 0x4b, 0x32, 0xd3, 0x59, 0xcb, 0xc0, 0x76, 0xfd, 0xcd, 0xff, 0x1f, 0x00, 0x00,
0xff, 0xff, 0x4d, 0xc1, 0x0e, 0xb8, 0xb8, 0xe9, 0x00, 0x00,
}
func (m *Metadata) Marshal() (dAtA []byte, err error) {

1
go.mod
View file

@ -380,7 +380,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.20230331180905-0f76f1751cd3
github.com/pkg/sftp => github.com/gravitational/sftp v1.13.6-0.20230328150159-dfe4e0d94419
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

4
go.sum
View file

@ -646,8 +646,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.20230328150159-dfe4e0d94419 h1:520SBVjHrTsfTPs7Udp+aXJV4WXOlKexKoIyiN0BJys=
github.com/gravitational/sftp v1.13.6-0.20230328150159-dfe4e0d94419/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=
@ -1045,6 +1043,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=

View file

@ -7313,9 +7313,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) {
@ -7341,6 +7344,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.
@ -7354,11 +7363,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.

View file

@ -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
)

View file

@ -307,6 +307,8 @@ const (
SFTPReadlinkFailureCode = "TS017E"
SFTPSymlinkCode = "TS018I"
SFTPSymlinkFailureCode = "TS018E"
SFTPLinkCode = "TS019I"
SFTPLinkFailureCode = "TS019E"
// SessionCommandCode is a session command code.
SessionCommandCode = "T4000I"

View file

@ -24,6 +24,8 @@ import (
"time"
"github.com/gravitational/trace"
"github.com/gravitational/teleport/lib/defaults"
)
// localFS provides API for accessing the files on
@ -81,12 +83,12 @@ func (l localFS) Open(ctx context.Context, path string) (fs.File, error) {
return &fileWrapper{file: f}, nil
}
func (l localFS) Create(ctx context.Context, path string, mode os.FileMode) (io.WriteCloser, error) {
func (l localFS) Create(ctx context.Context, path string) (io.WriteCloser, error) {
if err := ctx.Err(); err != nil {
return nil, err
}
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, defaults.FilePermissions)
if err != nil {
return nil, trace.Wrap(err)
}
@ -94,12 +96,12 @@ func (l localFS) Create(ctx context.Context, path string, mode os.FileMode) (io.
return f, nil
}
func (l localFS) Mkdir(ctx context.Context, path string, mode os.FileMode) error {
func (l localFS) Mkdir(ctx context.Context, path string) error {
if err := ctx.Err(); err != nil {
return err
}
err := os.MkdirAll(path, mode)
err := os.MkdirAll(path, defaults.DirectoryPermissions)
if err != nil && !os.IsExist(err) {
return trace.ConvertSystemError(err)
}

View file

@ -76,12 +76,12 @@ func (r *remoteFS) Open(ctx context.Context, path string) (fs.File, error) {
return f, nil
}
func (r *remoteFS) Create(ctx context.Context, path string, mode os.FileMode) (io.WriteCloser, error) {
func (r *remoteFS) Create(ctx context.Context, path string) (io.WriteCloser, error) {
if err := ctx.Err(); err != nil {
return nil, err
}
f, err := r.c.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
f, err := r.c.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)
if err != nil {
return nil, trace.Wrap(err)
}
@ -89,12 +89,12 @@ func (r *remoteFS) Create(ctx context.Context, path string, mode os.FileMode) (i
return f, nil
}
func (r *remoteFS) Mkdir(ctx context.Context, path string, mode os.FileMode) error {
func (r *remoteFS) Mkdir(ctx context.Context, path string) error {
if err := ctx.Err(); err != nil {
return err
}
err := r.c.MkdirAll(path, mode)
err := r.c.MkdirAll(path)
if err != nil {
return trace.Wrap(err)
}

View file

@ -37,6 +37,7 @@ import (
"golang.org/x/crypto/ssh"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/sshutils/scp"
)
@ -81,9 +82,9 @@ type FileSystem interface {
// Open opens a file
Open(ctx context.Context, path string) (fs.File, error)
// Create creates a new file
Create(ctx context.Context, path string, mode os.FileMode) (io.WriteCloser, error)
Create(ctx context.Context, path string) (io.WriteCloser, error)
// Mkdir creates a directory
Mkdir(ctx context.Context, path string, mode os.FileMode) error
Mkdir(ctx context.Context, path string) error
// Chmod sets file permissions
Chmod(ctx context.Context, path string, mode os.FileMode) error
// Chtimes sets file access and modification time
@ -283,9 +284,12 @@ func (c *Config) transfer(ctx context.Context) error {
// if there are multiple source paths and the destination path
// doesn't exist, create it as a directory
if len(c.srcPaths) > 1 {
if err := c.dstFS.Mkdir(ctx, c.dstPath, teleport.SharedDirMode); err != nil {
if err := c.dstFS.Mkdir(ctx, c.dstPath); err != nil {
return trace.Errorf("error creating %s directory %q: %w", c.dstFS.Type(), c.dstPath, err)
}
if err := c.dstFS.Chmod(ctx, c.dstPath, defaults.DirectoryPermissions); err != nil {
return trace.Errorf("error setting permissions of %s directory %q: %w", c.dstFS.Type(), c.dstPath, err)
}
dstIsDir = true
}
} else if len(c.srcPaths) > 1 && !dstInfo.IsDir() {
@ -337,10 +341,13 @@ func (c *Config) transfer(ctx context.Context) error {
// transferDir transfers a directory
func (c *Config) transferDir(ctx context.Context, dstPath, srcPath string, srcFileInfo os.FileInfo) error {
err := c.dstFS.Mkdir(ctx, dstPath, srcFileInfo.Mode())
err := c.dstFS.Mkdir(ctx, dstPath)
if err != nil && !errors.Is(err, os.ErrExist) {
return trace.Errorf("error creating %s directory %q: %w", c.dstFS.Type(), dstPath, err)
}
if err := c.dstFS.Chmod(ctx, dstPath, srcFileInfo.Mode()); err != nil {
return trace.Errorf("error setting permissions of %s directory %q: %w", c.dstFS.Type(), dstPath, err)
}
infos, err := c.srcFS.ReadDir(ctx, srcPath)
if err != nil {
@ -382,12 +389,16 @@ func (c *Config) transferFile(ctx context.Context, dstPath, srcPath string, srcF
}
defer srcFile.Close()
dstFile, err := c.dstFS.Create(ctx, dstPath, srcFileInfo.Mode())
dstFile, err := c.dstFS.Create(ctx, dstPath)
if err != nil {
return trace.Errorf("error creating %s file %q: %w", c.dstFS.Type(), dstPath, err)
}
defer dstFile.Close()
if err := c.dstFS.Chmod(ctx, dstPath, srcFileInfo.Mode()); err != nil {
return trace.Errorf("error setting permissions of %s file %q: %w", c.dstFS.Type(), dstPath, err)
}
var progressBar io.ReadWriter
if c.ProgressStream != nil {
progressBar = c.ProgressStream(srcFileInfo)

View file

@ -335,7 +335,9 @@ func createFile(t *testing.T, rootDir, path string) {
createDir(t, rootDir, dir)
}
f, err := os.OpenFile(filepath.Join(rootDir, path), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o664)
// use non-standard permissions to verify that transferred files
// permissions match the originals
f, err := os.OpenFile(filepath.Join(rootDir, path), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o654)
require.NoError(t, err)
defer func() {
require.NoError(t, f.Close())
@ -349,7 +351,9 @@ func createFile(t *testing.T, rootDir, path string) {
}
func createDir(t *testing.T, rootDir, path string) {
err := os.MkdirAll(filepath.Join(rootDir, path), 0o775)
// use non-standard permissions to verify that transferred dirs
// permissions match the originals
err := os.MkdirAll(filepath.Join(rootDir, path), 0o765)
require.NoError(t, err)
}

View file

@ -19,9 +19,11 @@ package common
import (
"bytes"
"errors"
"fmt"
"io"
"io/fs"
"os"
"os/user"
"time"
"github.com/gogo/protobuf/jsonpb"
@ -30,11 +32,30 @@ import (
log "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
"github.com/gravitational/teleport"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/utils"
)
const (
methodGet = "Get"
methodPut = "Put"
methodOpen = "Open"
methodSetStat = "Setstat"
methodRename = "Rename"
methodRmdir = "Rmdir"
methodMkdir = "Mkdir"
methodLink = "Link"
methodSymlink = "Symlink"
methodRemove = "Remove"
methodList = "List"
methodStat = "Stat"
methodLstat = "Lstat"
methodReadlink = "Readlink"
)
type compositeCh struct {
r io.ReadCloser
w io.WriteCloser
@ -52,58 +73,473 @@ func (c compositeCh) Close() error {
return trace.NewAggregate(c.r.Close(), c.w.Close())
}
// sftpHandler provides handlers for a SFTP server.
type sftpHandler struct {
logger *log.Entry
events chan<- *apievents.SFTP
}
func newSFTPHandler(logger *log.Entry, events chan<- *apievents.SFTP) *sftpHandler {
return &sftpHandler{
logger: logger,
events: events,
}
}
// OpenFile handles 'open' requests when opening a file for reading
// and writing is desired.
func (s *sftpHandler) OpenFile(req *sftp.Request) (_ sftp.WriterAtReaderAt, retErr error) {
defer s.sendSFTPEvent(req, retErr)
if req.Filepath == "" {
return nil, os.ErrInvalid
}
return s.openFile(req)
}
// Fileread handles 'open' requests when opening a file for reading
// is desired.
func (s *sftpHandler) Fileread(req *sftp.Request) (_ io.ReaderAt, retErr error) {
defer s.sendSFTPEvent(req, retErr)
if req.Filepath == "" {
return nil, os.ErrInvalid
}
if !req.Pflags().Read {
return nil, os.ErrInvalid
}
return s.openFile(req)
}
// Filewrite handles 'open' requests when opening a file for writing
// is desired.
func (s *sftpHandler) Filewrite(req *sftp.Request) (_ io.WriterAt, retErr error) {
defer s.sendSFTPEvent(req, retErr)
if req.Filepath == "" {
return nil, os.ErrInvalid
}
if !req.Pflags().Write {
return nil, os.ErrInvalid
}
return s.openFile(req)
}
func (s *sftpHandler) openFile(req *sftp.Request) (*os.File, error) {
var flags int
pflags := req.Pflags()
if pflags.Append {
flags |= os.O_APPEND
}
if pflags.Creat {
flags |= os.O_CREATE
}
if pflags.Excl {
flags |= os.O_EXCL
}
if pflags.Trunc {
flags |= os.O_TRUNC
}
if pflags.Read && pflags.Write {
flags |= os.O_RDWR
} else if pflags.Read {
flags |= os.O_RDONLY
} else if pflags.Write {
flags |= os.O_WRONLY
}
f, err := os.OpenFile(req.Filepath, flags, defaults.FilePermissions)
if err != nil {
return nil, err
}
return f, nil
}
// Filecmd handles file modification requests.
func (s *sftpHandler) Filecmd(req *sftp.Request) (retErr error) {
defer func() {
if retErr == sftp.ErrSSHFxOpUnsupported {
return
}
s.sendSFTPEvent(req, retErr)
}()
if req.Filepath == "" {
return os.ErrInvalid
}
switch req.Method {
case methodSetStat:
return s.setstat(req)
case methodRename:
if req.Target == "" {
return os.ErrInvalid
}
return os.Rename(req.Filepath, req.Target)
case methodRmdir:
fi, err := os.Lstat(req.Filepath)
if err != nil {
return err
}
if !fi.IsDir() {
return fmt.Errorf("%q is not a directory", req.Filepath)
}
return os.RemoveAll(req.Filepath)
case methodMkdir:
return os.MkdirAll(req.Filepath, defaults.DirectoryPermissions)
case methodLink:
if req.Target == "" {
return os.ErrInvalid
}
return os.Link(req.Target, req.Filepath)
case methodSymlink:
if req.Target == "" {
return os.ErrInvalid
}
return os.Symlink(req.Target, req.Filepath)
case methodRemove:
fi, err := os.Lstat(req.Filepath)
if err != nil {
return err
}
if fi.IsDir() {
return fmt.Errorf("%q is a directory", req.Filepath)
}
return os.Remove(req.Filepath)
default:
return sftp.ErrSSHFxOpUnsupported
}
}
func (s *sftpHandler) setstat(req *sftp.Request) error {
attrFlags := req.AttrFlags()
attrs := req.Attributes()
if attrFlags.Acmodtime {
atime := time.Unix(int64(attrs.Atime), 0)
mtime := time.Unix(int64(attrs.Mtime), 0)
err := os.Chtimes(req.Filepath, atime, mtime)
if err != nil {
return err
}
}
if attrFlags.Permissions {
err := os.Chmod(req.Filepath, attrs.FileMode())
if err != nil {
return err
}
}
if attrFlags.UidGid {
err := os.Chown(req.Filepath, int(attrs.UID), int(attrs.GID))
if err != nil {
return err
}
}
if attrFlags.Size {
err := os.Truncate(req.Filepath, int64(attrs.Size))
if err != nil {
return err
}
}
return nil
}
// listerAt satisfies [sftp.ListerAt].
type listerAt []fs.FileInfo
func (l listerAt) ListAt(ls []fs.FileInfo, offset int64) (int, error) {
if offset >= int64(len(l)) {
return 0, io.EOF
}
n := copy(ls, l[offset:])
if n < len(ls) {
return n, io.EOF
}
return n, nil
}
// fileName satisfies [fs.FileInfo] but only knows a file's name. This
// is necessary when handling 'readlink' requests in sftpHandler.FileList,
// as only the file's name is known after a readlink call.
type fileName string
func (f fileName) Name() string {
return string(f)
}
func (f fileName) Size() int64 {
return 0
}
func (f fileName) Mode() fs.FileMode {
return 0
}
func (f fileName) ModTime() time.Time {
return time.Time{}
}
func (f fileName) IsDir() bool {
return false
}
func (f fileName) Sys() any {
return nil
}
// Filelist handles 'readdir', 'stat' and 'readlink' requests.
func (s *sftpHandler) Filelist(req *sftp.Request) (_ sftp.ListerAt, retErr error) {
defer func() {
if req.Method == methodList {
s.sendSFTPEvent(req, retErr)
}
}()
if req.Filepath == "" {
return nil, os.ErrInvalid
}
switch req.Method {
case methodList:
entries, err := os.ReadDir(req.Filepath)
if err != nil {
return nil, err
}
infos := make([]fs.FileInfo, len(entries))
for i, entry := range entries {
info, err := entry.Info()
if err != nil {
return nil, err
}
infos[i] = info
}
return listerAt(infos), nil
case methodStat:
fi, err := os.Stat(req.Filepath)
if err != nil {
return nil, err
}
return listerAt{fi}, nil
case methodReadlink:
dst, err := os.Readlink(req.Filepath)
if err != nil {
return nil, err
}
return listerAt{fileName(dst)}, nil
default:
return nil, sftp.ErrSSHFxOpUnsupported
}
}
// Lstat handles 'lstat' requests.
func (s *sftpHandler) Lstat(req *sftp.Request) (sftp.ListerAt, error) {
if req.Filepath == "" {
return nil, os.ErrInvalid
}
fi, err := os.Lstat(req.Filepath)
if err != nil {
return nil, err
}
return listerAt{fi}, nil
}
func (s *sftpHandler) sendSFTPEvent(req *sftp.Request, reqErr error) {
event := &apievents.SFTP{
Metadata: apievents.Metadata{
Type: events.SFTPEvent,
Time: time.Now(),
},
}
switch req.Method {
case methodOpen, methodGet, methodPut:
if reqErr == nil {
event.Code = events.SFTPOpenCode
} else {
event.Code = events.SFTPOpenFailureCode
}
event.Action = apievents.SFTPAction_OPEN
case methodSetStat:
if reqErr == nil {
event.Code = events.SFTPSetstatCode
} else {
event.Code = events.SFTPSetstatFailureCode
}
event.Action = apievents.SFTPAction_SETSTAT
case methodList:
if reqErr == nil {
event.Code = events.SFTPReaddirCode
} else {
event.Code = events.SFTPReaddirFailureCode
}
event.Action = apievents.SFTPAction_READDIR
case methodRemove:
if reqErr == nil {
event.Code = events.SFTPRemoveCode
} else {
event.Code = events.SFTPRemoveFailureCode
}
event.Action = apievents.SFTPAction_REMOVE
case methodMkdir:
if reqErr == nil {
event.Code = events.SFTPMkdirCode
} else {
event.Code = events.SFTPMkdirFailureCode
}
event.Action = apievents.SFTPAction_MKDIR
case methodRmdir:
if reqErr == nil {
event.Code = events.SFTPRmdirCode
} else {
event.Code = events.SFTPRmdirFailureCode
}
event.Action = apievents.SFTPAction_RMDIR
case methodRename:
if reqErr == nil {
event.Code = events.SFTPRenameCode
} else {
event.Code = events.SFTPRenameFailureCode
}
event.Action = apievents.SFTPAction_RENAME
case methodSymlink:
if reqErr == nil {
event.Code = events.SFTPSymlinkCode
} else {
event.Code = events.SFTPSymlinkFailureCode
}
event.Action = apievents.SFTPAction_SYMLINK
case methodLink:
if reqErr == nil {
event.Code = events.SFTPLinkCode
} else {
event.Code = events.SFTPLinkFailureCode
}
event.Action = apievents.SFTPAction_LINK
default:
s.logger.Warnf("Unknown SFTP request %q", req.Method)
return
}
wd, err := os.Getwd()
if err != nil {
s.logger.WithError(err).Warn("Failed to get working dir.")
}
event.WorkingDirectory = wd
event.Path = req.Filepath
event.TargetPath = req.Target
event.Flags = req.Flags
if req.Method == methodSetStat {
attrFlags := req.AttrFlags()
attrs := req.Attributes()
event.Attributes = new(apievents.SFTPAttributes)
if attrFlags.Acmodtime {
atime := time.Unix(int64(attrs.Atime), 0)
mtime := time.Unix(int64(attrs.Mtime), 0)
event.Attributes.AccessTime = &atime
event.Attributes.ModificationTime = &mtime
}
if attrFlags.Permissions {
perms := uint32(attrs.FileMode().Perm())
event.Attributes.Permissions = &perms
}
if attrFlags.Size {
event.Attributes.FileSize = &attrs.Size
}
if attrFlags.UidGid {
event.Attributes.UID = &attrs.UID
event.Attributes.GID = &attrs.GID
}
}
if reqErr != nil {
s.logger.Debugf("%s: %v", req.Method, reqErr)
// If possible, strip the filename from the error message. The
// path will be included in audit events already, no need to
// make the error message longer than it needs to be.
var pathErr *fs.PathError
var linkErr *os.LinkError
if errors.As(reqErr, &pathErr) {
event.Error = pathErr.Err.Error()
} else if errors.As(reqErr, &linkErr) {
event.Error = linkErr.Err.Error()
} else {
event.Error = reqErr.Error()
}
}
s.events <- event
}
func onSFTP() error {
chr, err := openFD(3, "chr")
if err != nil {
return err
return trace.Wrap(err)
}
defer chr.Close()
chw, err := openFD(4, "chw")
if err != nil {
return err
return trace.Wrap(err)
}
defer chw.Close()
ch := compositeCh{chr, chw}
auditFile, err := openFD(5, "audit")
if err != nil {
return err
return trace.Wrap(err)
}
defer auditFile.Close()
// Ensure the parent process will receive log messages from us
utils.InitLogger(utils.LoggingForDaemon, log.InfoLevel)
l := utils.NewLogger()
l.SetOutput(os.Stderr)
logger := l.WithField(trace.Component, teleport.ComponentSubsystemSFTP)
sftpEvents := make(chan *apievents.SFTP, 1)
sftpSrv, err := sftp.NewServer(ch, sftp.WithRequestCallback(func(reqPacket sftp.RequestPacket) {
event, ok := handleSFTPEvent(reqPacket)
if !ok {
// We don't care about this type of SFTP request, move on
return
}
sftpEvents <- event
}))
currentUser, err := user.Current()
if err != nil {
return trace.Wrap(err)
}
_, err = os.Stat(currentUser.HomeDir)
if err != nil {
return trace.Wrap(err)
}
sftpEvents := make(chan *apievents.SFTP, 1)
h := newSFTPHandler(logger, sftpEvents)
handler := sftp.Handlers{
FileGet: h,
FilePut: h,
FileCmd: h,
FileList: h,
}
sftpSrv := sftp.NewRequestServer(ch, handler, sftp.WithStartDirectory(currentUser.HomeDir))
// Start a goroutine to marshal and send audit events to the parent
// process to avoid blocking the SFTP connection on event handling
done := make(chan struct{})
go func() {
var m jsonpb.Marshaler
var buf bytes.Buffer
for event := range sftpEvents {
eventStr, err := m.MarshalToString(event)
if err != nil {
log.WithError(err).Warn("Failed to marshal SFTP event.")
buf.Reset()
if err := m.Marshal(&buf, event); err != nil {
logger.WithError(err).Warn("Failed to marshal SFTP event.")
} else {
// Append a NULL byte so the parent process will know where
// this event ends
eventBytes := []byte(eventStr)
eventBytes = append(eventBytes, 0x0)
_, err = io.Copy(auditFile, bytes.NewReader(eventBytes))
buf.WriteByte(0x0)
_, err = io.Copy(auditFile, &buf)
if err != nil {
log.WithError(err).Warn("Failed to send SFTP event to parent.")
logger.WithError(err).Warn("Failed to send SFTP event to parent.")
}
}
}
@ -140,187 +576,3 @@ func openFD(fd uintptr, name string) (*os.File, error) {
return file, nil
}
func handleSFTPEvent(reqPacket sftp.RequestPacket) (*apievents.SFTP, bool) {
event := &apievents.SFTP{
Metadata: apievents.Metadata{
Type: events.SFTPEvent,
Time: time.Now(),
},
}
switch reqPacket.Type {
case sftp.Open:
if reqPacket.Err == nil {
event.Code = events.SFTPOpenCode
} else {
event.Code = events.SFTPOpenFailureCode
}
event.Action = apievents.SFTPAction_OPEN
case sftp.Close:
if reqPacket.Err == nil {
event.Code = events.SFTPCloseCode
} else {
event.Code = events.SFTPCloseFailureCode
}
event.Action = apievents.SFTPAction_CLOSE
case sftp.Read:
if reqPacket.Err == nil {
event.Code = events.SFTPReadCode
} else {
event.Code = events.SFTPReadFailureCode
}
event.Action = apievents.SFTPAction_READ
case sftp.Write:
if reqPacket.Err == nil {
event.Code = events.SFTPWriteCode
} else {
event.Code = events.SFTPWriteFailureCode
}
event.Action = apievents.SFTPAction_WRITE
case sftp.Lstat:
if reqPacket.Err == nil {
event.Code = events.SFTPLstatCode
} else {
event.Code = events.SFTPLstatFailureCode
}
event.Action = apievents.SFTPAction_LSTAT
case sftp.Fstat:
if reqPacket.Err == nil {
event.Code = events.SFTPFstatCode
} else {
event.Code = events.SFTPFstatFailureCode
}
event.Action = apievents.SFTPAction_FSTAT
case sftp.Setstat:
if reqPacket.Err == nil {
event.Code = events.SFTPSetstatCode
} else {
event.Code = events.SFTPSetstatFailureCode
}
event.Action = apievents.SFTPAction_SETSTAT
case sftp.Fsetstat:
if reqPacket.Err == nil {
event.Code = events.SFTPFsetstatCode
} else {
event.Code = events.SFTPFsetstatFailureCode
}
event.Action = apievents.SFTPAction_FSETSTAT
case sftp.Opendir:
if reqPacket.Err == nil {
event.Code = events.SFTPOpendirCode
} else {
event.Code = events.SFTPOpendirFailureCode
}
event.Action = apievents.SFTPAction_OPENDIR
case sftp.Readdir:
if reqPacket.Err == nil {
event.Code = events.SFTPReaddirCode
} else {
event.Code = events.SFTPReaddirFailureCode
}
event.Action = apievents.SFTPAction_READDIR
case sftp.Remove:
if reqPacket.Err == nil {
event.Code = events.SFTPRemoveCode
} else {
event.Code = events.SFTPRemoveFailureCode
}
event.Action = apievents.SFTPAction_REMOVE
case sftp.Mkdir:
if reqPacket.Err == nil {
event.Code = events.SFTPMkdirCode
} else {
event.Code = events.SFTPMkdirFailureCode
}
event.Action = apievents.SFTPAction_MKDIR
case sftp.Rmdir:
if reqPacket.Err == nil {
event.Code = events.SFTPRmdirCode
} else {
event.Code = events.SFTPRmdirFailureCode
}
event.Action = apievents.SFTPAction_RMDIR
case sftp.Realpath:
if reqPacket.Err == nil {
event.Code = events.SFTPRealpathCode
} else {
event.Code = events.SFTPRealpathFailureCode
}
event.Action = apievents.SFTPAction_REALPATH
case sftp.Stat:
if reqPacket.Err == nil {
event.Code = events.SFTPStatCode
} else {
event.Code = events.SFTPStatFailureCode
}
event.Action = apievents.SFTPAction_STAT
case sftp.Rename:
if reqPacket.Err == nil {
event.Code = events.SFTPRenameCode
} else {
event.Code = events.SFTPRenameFailureCode
}
event.Action = apievents.SFTPAction_RENAME
case sftp.Readlink:
if reqPacket.Err == nil {
event.Code = events.SFTPReadlinkCode
} else {
event.Code = events.SFTPReadlinkFailureCode
}
event.Action = apievents.SFTPAction_READLINK
case sftp.Symlink:
if reqPacket.Err == nil {
event.Code = events.SFTPSymlinkCode
} else {
event.Code = events.SFTPSymlinkFailureCode
}
event.Action = apievents.SFTPAction_SYMLINK
default:
return nil, false
}
wd, err := os.Getwd()
if err != nil {
log.WithError(err).Warn("Failed to get working dir.")
}
event.WorkingDirectory = wd
event.Path = reqPacket.Path
event.TargetPath = reqPacket.TargetPath
event.Flags = reqPacket.Flags
if reqPacket.Attributes != nil {
event.Attributes = &apievents.SFTPAttributes{
AccessTime: reqPacket.Attributes.AccessTime,
ModificationTime: reqPacket.Attributes.ModificationTime,
}
if reqPacket.Attributes.Size != nil {
event.Attributes.FileSize = reqPacket.Attributes.Size
}
if reqPacket.Attributes.UID != nil {
event.Attributes.UID = reqPacket.Attributes.UID
}
if reqPacket.Attributes.GID != nil {
event.Attributes.GID = reqPacket.Attributes.GID
}
if reqPacket.Attributes.Permissions != nil {
event.Attributes.Permissions = (*uint32)(reqPacket.Attributes.Permissions)
}
}
if reqPacket.Err != nil {
// If possible, strip the filename from the error message. The
// path will be included in audit events already, no need to
// make the error message longer than it needs to be.
var pathErr *fs.PathError
var linkErr *os.LinkError
if errors.As(reqPacket.Err, &pathErr) {
event.Error = pathErr.Err.Error()
} else if errors.As(reqPacket.Err, &linkErr) {
event.Error = linkErr.Err.Error()
} else {
event.Error = reqPacket.Err.Error()
}
}
return event, true
}