github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/ftp-server.go (about)

     1  // Copyright (c) 2015-2023 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"fmt"
    22  	"net"
    23  	"strconv"
    24  	"strings"
    25  
    26  	"github.com/minio/minio/internal/logger"
    27  	ftp "goftp.io/server/v2"
    28  )
    29  
    30  var globalRemoteFTPClientTransport = NewRemoteTargetHTTPTransport(true)()
    31  
    32  // minioLogger use an instance of this to log in a standard format
    33  type minioLogger struct{}
    34  
    35  // Print implement Logger
    36  func (log *minioLogger) Print(sessionID string, message interface{}) {
    37  	if serverDebugLog {
    38  		fmt.Printf("%s %s\n", sessionID, message)
    39  	}
    40  }
    41  
    42  // Printf implement Logger
    43  func (log *minioLogger) Printf(sessionID string, format string, v ...interface{}) {
    44  	if serverDebugLog {
    45  		if sessionID != "" {
    46  			fmt.Printf("%s %s\n", sessionID, fmt.Sprintf(format, v...))
    47  		} else {
    48  			fmt.Printf(format+"\n", v...)
    49  		}
    50  	}
    51  }
    52  
    53  // PrintCommand implement Logger
    54  func (log *minioLogger) PrintCommand(sessionID string, command string, params string) {
    55  	if serverDebugLog {
    56  		if command == "PASS" {
    57  			fmt.Printf("%s > PASS ****\n", sessionID)
    58  		} else {
    59  			fmt.Printf("%s > %s %s\n", sessionID, command, params)
    60  		}
    61  	}
    62  }
    63  
    64  // PrintResponse implement Logger
    65  func (log *minioLogger) PrintResponse(sessionID string, code int, message string) {
    66  	if serverDebugLog {
    67  		fmt.Printf("%s < %d %s\n", sessionID, code, message)
    68  	}
    69  }
    70  
    71  func startFTPServer(args []string) {
    72  	var (
    73  		port          int
    74  		publicIP      string
    75  		portRange     string
    76  		tlsPrivateKey string
    77  		tlsPublicCert string
    78  	)
    79  
    80  	var err error
    81  	for _, arg := range args {
    82  		tokens := strings.SplitN(arg, "=", 2)
    83  		if len(tokens) != 2 {
    84  			logger.Fatal(fmt.Errorf("invalid arguments passed to --ftp=%s", arg), "unable to start FTP server")
    85  		}
    86  		switch tokens[0] {
    87  		case "address":
    88  			host, portStr, err := net.SplitHostPort(tokens[1])
    89  			if err != nil {
    90  				logger.Fatal(fmt.Errorf("invalid arguments passed to --ftp=%s (%v)", arg, err), "unable to start FTP server")
    91  			}
    92  			port, err = strconv.Atoi(portStr)
    93  			if err != nil {
    94  				logger.Fatal(fmt.Errorf("invalid arguments passed to --ftp=%s (%v)", arg, err), "unable to start FTP server")
    95  			}
    96  			if port < 1 || port > 65535 {
    97  				logger.Fatal(fmt.Errorf("invalid arguments passed to --ftp=%s, (port number must be between 1 to 65535)", arg), "unable to start FTP server")
    98  			}
    99  			publicIP = host
   100  		case "passive-port-range":
   101  			portRange = tokens[1]
   102  		case "tls-private-key":
   103  			tlsPrivateKey = tokens[1]
   104  		case "tls-public-cert":
   105  			tlsPublicCert = tokens[1]
   106  		}
   107  	}
   108  
   109  	// Verify if only partial inputs are given for FTP(secure)
   110  	{
   111  		if tlsPrivateKey == "" && tlsPublicCert != "" {
   112  			logger.Fatal(fmt.Errorf("invalid TLS arguments provided missing private key --ftp=\"tls-private-key=path/to/private.key\""), "unable to start FTP server")
   113  		}
   114  
   115  		if tlsPrivateKey != "" && tlsPublicCert == "" {
   116  			logger.Fatal(fmt.Errorf("invalid TLS arguments provided missing public cert --ftp=\"tls-public-cert=path/to/public.crt\""), "unable to start FTP server")
   117  		}
   118  		if port == 0 {
   119  			port = 8021 // Default FTP port, since no port was given.
   120  		}
   121  	}
   122  
   123  	// If no TLS certs were provided, server is running in TLS for S3 API
   124  	// we automatically make FTP also run under TLS mode.
   125  	if globalIsTLS && tlsPrivateKey == "" && tlsPublicCert == "" {
   126  		tlsPrivateKey = getPrivateKeyFile()
   127  		tlsPublicCert = getPublicCertFile()
   128  	}
   129  
   130  	tls := tlsPrivateKey != "" && tlsPublicCert != ""
   131  
   132  	name := "MinIO FTP Server"
   133  	if tls {
   134  		name = "MinIO FTP(Secure) Server"
   135  	}
   136  
   137  	ftpServer, err := ftp.NewServer(&ftp.Options{
   138  		Name:           name,
   139  		WelcomeMessage: fmt.Sprintf("Welcome to '%s' FTP Server Version='%s' License='%s'", MinioStoreName, MinioLicense, Version),
   140  		Driver:         NewFTPDriver(),
   141  		Port:           port,
   142  		Perm:           ftp.NewSimplePerm("nobody", "nobody"),
   143  		TLS:            tls,
   144  		KeyFile:        tlsPrivateKey,
   145  		CertFile:       tlsPublicCert,
   146  		ExplicitFTPS:   tls,
   147  		Logger:         &minioLogger{},
   148  		PassivePorts:   portRange,
   149  		PublicIP:       publicIP,
   150  	})
   151  	if err != nil {
   152  		logger.Fatal(err, "unable to initialize FTP server")
   153  	}
   154  
   155  	logger.Info(fmt.Sprintf("%s listening on %s", name, net.JoinHostPort(publicIP, strconv.Itoa(port))))
   156  
   157  	if err = ftpServer.ListenAndServe(); err != nil {
   158  		logger.Fatal(err, "unable to start FTP server")
   159  	}
   160  }