Repository: minio/minio (language: go)

Issue:
The SFTP connection does not apply to the restricted source IP address
Hello everyone, I have found a problem with minio, which is very upsetting to me. The problem is as follows: I want to use minio to replace our sftp server, which I think is very good, but for security reasons, I need to limit the source IP to connect to the minio server. I have developed a response IMA policy to implement it
```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::test"
            ],
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "127.0.0.1/32",
                        "192.168.161.222/32",
                        "172.0.7.57/32"
                    ]
                },
                "StringLike": {
                    "s3:prefix": [
                        "testshare",
                        "testshare/*"
                    ]
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:AbortMultipartUpload",
                "s3:DeleteObject",
                "s3:GetObject",
                "s3:ListBucket",
                "s3:ListBucketMultipartUploads"
            ],
            "Resource": [
                "arn:aws:s3:::test/testshare/",
                "arn:aws:s3:::test/testshare/*"
            ],
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "172.0.7.57/32",
                        "192.168.161.222/32",
                        "127.0.0.1/32"
                    ]
                }
            }
        }
    ]
}
```
I use the WEB console operation access is normal to get the effect I want, but when I use the SFTP GUI/SFTP command to log in, I find that all IP is restricted, which makes me very frustrated, I capture the packet on the SFTP port on the minio server, the source IP is actually 127.0.0.1, It was found that it did not transparently pass the real source IP to the minio server, so the restriction failed.
```
127.0.0.1.8974 > 127.0.0.1.9000: Flags [P.], cksum 0x01cb (incorrect -> 0x12eb), seq 804393:805323, ack 36653, win 909, options [nop,nop,TS val 1293723497 ecr 1293723496], length 930
E.....@.@.ZB........#.#(G....G!............
M..iM..hPOST /test/testshare/am.pak?uploadId=YWJlZTk2OTEtOWExOC00OThkLTk4YmQtOTI2MjBhMTlmZTQxLmEwMTM2MjE3LWIzYzUtNDUxNC04ZmE0LWZjNDlkYjI1OTU3YngxNzMyODYyMDY0MTg5Mjg2Nzg5 HTTP/1.1
Host: 127.0.0.1:9000
User-Agent: MinIO (linux; amd64) minio-go/v7.0.77
Content-Length: 224
Authorization: AWS4-HMAC-SHA256 Credential=test/20241129/us-east-1/s3/aws4_request, SignedHeaders=content-type;host;x-amz-checksum-crc32c;x-amz-content-sha256;x-amz-date, Signature=41dd89e99611b0a12af1447821f19b9fbd82f1f90e30f710932fd9bfa871efd7
Content-Type: application/octet-stream
X-Amz-Checksum-Crc32c: EIvZkw==
X-Amz-Content-Sha256: fc5d59d4e19360d91b27da96be1916abff9e73107b9e9eba87a3d40244bd10e0
X-Amz-Date: 20241129T063425Z
```
In order to better extend the use of minio, can you add the function of transparent transmission of source IP? Or are there any good solutions/parameters to solve the problem I am currently facing
This is really important for data security, hope to get help, thank you very much



Discussion / hints:
As we clearly note in our documentation you should not use SourceIP for anything but secondary validation.

SFTP requests are converted to S3 calls internally - as such the Source will be 127.0.0.1

I will see if there is a trivial fix for this, otherwise this will just remain as it is.

Fix patch:
diff --git a/cmd/ftp-server-driver.go b/cmd/ftp-server-driver.go
index 14df480eb546f..c2418f3eb95e1 100644
--- a/cmd/ftp-server-driver.go
+++ b/cmd/ftp-server-driver.go
@@ -24,6 +24,8 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"net"
+	"net/http"
 	"os"
 	"path"
 	"strings"
@@ -286,6 +288,10 @@ func (driver *ftpDriver) CheckPasswd(c *ftp.Context, username, password string)
 }
 
 func (driver *ftpDriver) getMinIOClient(ctx *ftp.Context) (*minio.Client, error) {
+	tr := http.RoundTripper(globalRemoteFTPClientTransport)
+	if host, _, err := net.SplitHostPort(ctx.Sess.RemoteAddr().String()); err == nil {
+		tr = forwardForTransport{tr: tr, fwd: host}
+	}
 	ui, ok := globalIAMSys.GetUser(context.Background(), ctx.Sess.LoginUser())
 	if !ok && !globalIAMSys.LDAPConfig.Enabled() {
 		return nil, errNoSuchUser
@@ -363,7 +369,7 @@ func (driver *ftpDriver) getMinIOClient(ctx *ftp.Context) (*minio.Client, error)
 		return minio.New(driver.endpoint, &minio.Options{
 			Creds:     mcreds,
 			Secure:    globalIsTLS,
-			Transport: globalRemoteFTPClientTransport,
+			Transport: tr,
 		})
 	}
 
@@ -377,7 +383,7 @@ func (driver *ftpDriver) getMinIOClient(ctx *ftp.Context) (*minio.Client, error)
 	return minio.New(driver.endpoint, &minio.Options{
 		Creds:     credentials.NewStaticV4(ui.Credentials.AccessKey, ui.Credentials.SecretKey, ""),
 		Secure:    globalIsTLS,
-		Transport: globalRemoteFTPClientTransport,
+		Transport: tr,
 	})
 }
 
diff --git a/cmd/sftp-server-driver.go b/cmd/sftp-server-driver.go
index c4c506fe08dee..33ddf6b66f867 100644
--- a/cmd/sftp-server-driver.go
+++ b/cmd/sftp-server-driver.go
@@ -23,6 +23,7 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"net/http"
 	"os"
 	"path"
 	"strings"
@@ -45,6 +46,7 @@ const ftpMaxWriteOffset = 100 << 20
 type sftpDriver struct {
 	permissions *ssh.Permissions
 	endpoint    string
+	remoteIP    string
 }
 
 //msgp:ignore sftpMetrics
@@ -89,8 +91,12 @@ func (m *sftpMetrics) log(s *sftp.Request, user string) func(sz int64, err error
 // - sftp.Filewrite
 // - sftp.Filelist
 // - sftp.Filecmd
-func NewSFTPDriver(perms *ssh.Permissions) sftp.Handlers {
-	handler := &sftpDriver{endpoint: fmt.Sprintf("127.0.0.1:%s", globalMinioPort), permissions: perms}
+func NewSFTPDriver(perms *ssh.Permissions, remoteIP string) sftp.Handlers {
+	handler := &sftpDriver{
+		endpoint:    fmt.Sprintf("127.0.0.1:%s", globalMinioPort),
+		permissions: perms,
+		remoteIP:    remoteIP,
+	}
 	return sftp.Handlers{
 		FileGet:  handler,
 		FilePut:  handler,
@@ -99,16 +105,31 @@ func NewSFTPDriver(perms *ssh.Permissions) sftp.Handlers {
 	}
 }
 
+type forwardForTransport struct {
+	tr  http.RoundTripper
+	fwd string
+}
+
+func (f forwardForTransport) RoundTrip(r *http.Request) (*http.Response, error) {
+	r.Header.Set("X-Forwarded-For", f.fwd)
+	return f.tr.RoundTrip(r)
+}
+
 func (f *sftpDriver) getMinIOClient() (*minio.Client, error) {
 	mcreds := credentials.NewStaticV4(
 		f.permissions.CriticalOptions["AccessKey"],
 		f.permissions.CriticalOptions["SecretKey"],
 		f.permissions.CriticalOptions["SessionToken"],
 	)
+	// Set X-Forwarded-For on all requests.
+	tr := http.RoundTripper(globalRemoteFTPClientTransport)
+	if f.remoteIP != "" {
+		tr = forwardForTransport{tr: tr, fwd: f.remoteIP}
+	}
 	return minio.New(f.endpoint, &minio.Options{
 		Creds:     mcreds,
 		Secure:    globalIsTLS,
-		Transport: globalRemoteFTPClientTransport,
+		Transport: tr,
 	})
 }
 
diff --git a/cmd/sftp-server.go b/cmd/sftp-server.go
index 95a1482a998ea..640caf6057300 100644
--- a/cmd/sftp-server.go
+++ b/cmd/sftp-server.go
@@ -488,7 +488,12 @@ func startSFTPServer(args []string) {
 	sshConfig.AddHostKey(private)
 
 	handleSFTPSession := func(channel ssh.Channel, sconn *ssh.ServerConn) {
-		server := sftp.NewRequestServer(channel, NewSFTPDriver(sconn.Permissions), sftp.WithRSAllocator())
+		var remoteIP string
+
+		if host, _, err := net.SplitHostPort(sconn.RemoteAddr().String()); err == nil {
+			remoteIP = host
+		}
+		server := sftp.NewRequestServer(channel, NewSFTPDriver(sconn.Permissions, remoteIP), sftp.WithRSAllocator())
 		defer server.Close()
 		server.Serve()
 	}


Test patch: