mirror of
https://github.com/gravitational/teleport
synced 2024-10-19 08:43:58 +00:00
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:
parent
ae66157f3e
commit
40c113b8da
|
@ -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
|
||||
|
|
|
@ -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
1
go.mod
|
@ -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
4
go.sum
|
@ -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=
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -307,6 +307,8 @@ const (
|
|||
SFTPReadlinkFailureCode = "TS017E"
|
||||
SFTPSymlinkCode = "TS018I"
|
||||
SFTPSymlinkFailureCode = "TS018E"
|
||||
SFTPLinkCode = "TS019I"
|
||||
SFTPLinkFailureCode = "TS019E"
|
||||
|
||||
// SessionCommandCode is a session command code.
|
||||
SessionCommandCode = "T4000I"
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue