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 }