diff --git a/weed/command/sftp.go b/weed/command/sftp.go index 07b5b0076..389624355 100644 --- a/weed/command/sftp.go +++ b/weed/command/sftp.go @@ -101,6 +101,12 @@ func (sftpOpt *SftpOptions) startSftpServer() bool { filerAddress := pb.ServerAddress(*sftpOpt.filer) grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client") + // Load JWT configuration for filer signing + v := util.GetViper() + filerSigningKey := v.GetString("jwt.filer_signing.key") + v.SetDefault("jwt.filer_signing.expires_after_seconds", 600) + filerSigningExpiresAfter := v.GetInt("jwt.filer_signing.expires_after_seconds") + // metrics read from the filer var metricsAddress string var metricsIntervalSec int @@ -137,19 +143,21 @@ func (sftpOpt *SftpOptions) startSftpServer() bool { // Create a new SFTP service instance with all options service := sftpd.NewSFTPService(&sftpd.SFTPServiceOptions{ - GrpcDialOption: grpcDialOption, - DataCenter: *sftpOpt.dataCenter, - FilerGroup: filerGroup, - Filer: filerAddress, - SshPrivateKey: *sftpOpt.sshPrivateKey, - HostKeysFolder: *sftpOpt.hostKeysFolder, - AuthMethods: authMethods, - MaxAuthTries: *sftpOpt.maxAuthTries, - BannerMessage: *sftpOpt.bannerMessage, - LoginGraceTime: *sftpOpt.loginGraceTime, - ClientAliveInterval: *sftpOpt.clientAliveInterval, - ClientAliveCountMax: *sftpOpt.clientAliveCountMax, - UserStoreFile: *sftpOpt.userStoreFile, + GrpcDialOption: grpcDialOption, + DataCenter: *sftpOpt.dataCenter, + FilerGroup: filerGroup, + Filer: filerAddress, + SshPrivateKey: *sftpOpt.sshPrivateKey, + HostKeysFolder: *sftpOpt.hostKeysFolder, + AuthMethods: authMethods, + MaxAuthTries: *sftpOpt.maxAuthTries, + BannerMessage: *sftpOpt.bannerMessage, + LoginGraceTime: *sftpOpt.loginGraceTime, + ClientAliveInterval: *sftpOpt.clientAliveInterval, + ClientAliveCountMax: *sftpOpt.clientAliveCountMax, + UserStoreFile: *sftpOpt.userStoreFile, + FilerSigningKey: []byte(filerSigningKey), + FilerSigningExpiresAfter: filerSigningExpiresAfter, }) // Register reload hook for HUP signal diff --git a/weed/sftpd/sftp_filer.go b/weed/sftpd/sftp_filer.go index eb196cc28..93a1d0984 100644 --- a/weed/sftpd/sftp_filer.go +++ b/weed/sftpd/sftp_filer.go @@ -17,6 +17,7 @@ import ( "github.com/seaweedfs/seaweedfs/weed/glog" "github.com/seaweedfs/seaweedfs/weed/pb" filer_pb "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" + "github.com/seaweedfs/seaweedfs/weed/security" weed_server "github.com/seaweedfs/seaweedfs/weed/server" "github.com/seaweedfs/seaweedfs/weed/sftpd/user" "github.com/seaweedfs/seaweedfs/weed/util" @@ -333,6 +334,14 @@ func (fs *SftpServer) putFile(filepath string, reader io.Reader, user *user.User } req.Header.Set("Content-Type", "application/octet-stream") + // Add JWT authorization if filer signing key is configured + if len(fs.filerSigningKey) > 0 { + jwt := security.GenJwtForFilerServer(security.SigningKey(fs.filerSigningKey), fs.filerSigningExpiresAfter) + if jwt != "" { + req.Header.Set("Authorization", "Bearer "+string(jwt)) + } + } + resp, err := http.DefaultClient.Do(req) if err != nil { return fmt.Errorf("upload to filer: %w", err) diff --git a/weed/sftpd/sftp_server.go b/weed/sftpd/sftp_server.go index e53098e6b..e3d692085 100644 --- a/weed/sftpd/sftp_server.go +++ b/weed/sftpd/sftp_server.go @@ -20,22 +20,26 @@ import ( ) type SftpServer struct { - filerAddr pb.ServerAddress - grpcDialOption grpc.DialOption - dataCenter string - filerGroup string - user *user.User + filerAddr pb.ServerAddress + grpcDialOption grpc.DialOption + dataCenter string + filerGroup string + user *user.User + filerSigningKey []byte + filerSigningExpiresAfter int } // NewSftpServer constructs the server. -func NewSftpServer(filerAddr pb.ServerAddress, grpcDialOption grpc.DialOption, dataCenter, filerGroup string, user *user.User) SftpServer { +func NewSftpServer(filerAddr pb.ServerAddress, grpcDialOption grpc.DialOption, dataCenter, filerGroup string, user *user.User, filerSigningKey []byte, filerSigningExpiresAfter int) SftpServer { return SftpServer{ - filerAddr: filerAddr, - grpcDialOption: grpcDialOption, - dataCenter: dataCenter, - filerGroup: filerGroup, - user: user, + filerAddr: filerAddr, + grpcDialOption: grpcDialOption, + dataCenter: dataCenter, + filerGroup: filerGroup, + user: user, + filerSigningKey: filerSigningKey, + filerSigningExpiresAfter: filerSigningExpiresAfter, } } diff --git a/weed/sftpd/sftp_service.go b/weed/sftpd/sftp_service.go index c03d636e4..643af99a6 100644 --- a/weed/sftpd/sftp_service.go +++ b/weed/sftpd/sftp_service.go @@ -47,6 +47,10 @@ type SFTPServiceOptions struct { // User Management UserStoreFile string // Path to user store file + + // JWT Configuration for Filer + FilerSigningKey []byte // JWT signing key for filer uploads + FilerSigningExpiresAfter int // JWT token expiration time in seconds } // NewSFTPService creates a new service instance. @@ -201,6 +205,8 @@ func (s *SFTPService) handleSSHConnection(conn net.Conn, config *ssh.ServerConfi s.options.DataCenter, s.options.FilerGroup, sftpUser, + s.options.FilerSigningKey, + s.options.FilerSigningExpiresAfter, ) // Ensure home directory exists with proper permissions