github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/proxy.go (about) 1 // Copyright (c) 2017 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 package virtcontainers 7 8 import ( 9 "bufio" 10 "fmt" 11 "io" 12 "net" 13 "path/filepath" 14 "strings" 15 16 kataclient "github.com/kata-containers/agent/protocols/client" 17 "github.com/kata-containers/runtime/virtcontainers/persist" 18 "github.com/sirupsen/logrus" 19 ) 20 21 var buildinProxyConsoleProto = consoleProtoUnix 22 23 type proxyBuiltin struct { 24 sandboxID string 25 conn net.Conn 26 } 27 28 // ProxyConfig is a structure storing information needed from any 29 // proxy in order to be properly initialized. 30 type ProxyConfig struct { 31 Path string 32 Debug bool 33 } 34 35 // proxyParams is the structure providing specific parameters needed 36 // for the execution of the proxy binary. 37 type proxyParams struct { 38 id string 39 path string 40 agentURL string 41 consoleURL string 42 logger *logrus.Entry 43 hid int 44 debug bool 45 } 46 47 // ProxyType describes a proxy type. 48 type ProxyType string 49 50 const ( 51 // NoopProxyType is the noopProxy. 52 NoopProxyType ProxyType = "noopProxy" 53 54 // NoProxyType is the noProxy. 55 NoProxyType ProxyType = "noProxy" 56 57 // KataProxyType is the kataProxy. 58 KataProxyType ProxyType = "kataProxy" 59 60 // KataBuiltInProxyType is the kataBuiltInProxy. 61 KataBuiltInProxyType ProxyType = "kataBuiltInProxy" 62 ) 63 64 const ( 65 // unix socket type of console 66 consoleProtoUnix = "unix" 67 68 // pty type of console. Used mostly by kvmtools. 69 consoleProtoPty = "pty" 70 ) 71 72 // Set sets a proxy type based on the input string. 73 func (pType *ProxyType) Set(value string) error { 74 switch value { 75 case "noopProxy": 76 *pType = NoopProxyType 77 return nil 78 case "noProxy": 79 *pType = NoProxyType 80 return nil 81 case "kataProxy": 82 *pType = KataProxyType 83 return nil 84 case "kataBuiltInProxy": 85 *pType = KataBuiltInProxyType 86 return nil 87 default: 88 return fmt.Errorf("Unknown proxy type %s", value) 89 } 90 } 91 92 // String converts a proxy type to a string. 93 func (pType *ProxyType) String() string { 94 switch *pType { 95 case NoopProxyType: 96 return string(NoopProxyType) 97 case NoProxyType: 98 return string(NoProxyType) 99 case KataProxyType: 100 return string(KataProxyType) 101 case KataBuiltInProxyType: 102 return string(KataBuiltInProxyType) 103 default: 104 return "" 105 } 106 } 107 108 // newProxy returns a proxy from a proxy type. 109 func newProxy(pType ProxyType) (proxy, error) { 110 switch pType { 111 case "": 112 return &kataBuiltInProxy{}, nil 113 case NoopProxyType: 114 return &noopProxy{}, nil 115 case NoProxyType: 116 return &noProxy{}, nil 117 case KataProxyType: 118 return &kataProxy{}, nil 119 case KataBuiltInProxyType: 120 return &kataBuiltInProxy{}, nil 121 default: 122 return &noopProxy{}, fmt.Errorf("Invalid proxy type: %s", pType) 123 } 124 } 125 126 func validateProxyParams(p proxyParams) error { 127 if len(p.path) == 0 || len(p.id) == 0 || len(p.agentURL) == 0 || len(p.consoleURL) == 0 { 128 return fmt.Errorf("Invalid proxy parameters %+v", p) 129 } 130 131 if p.logger == nil { 132 return fmt.Errorf("Invalid proxy parameter: proxy logger is not set") 133 } 134 135 return nil 136 } 137 138 func validateProxyConfig(proxyConfig ProxyConfig) error { 139 if len(proxyConfig.Path) == 0 { 140 return fmt.Errorf("Proxy path cannot be empty") 141 } 142 143 return nil 144 } 145 146 func defaultProxyURL(id, socketType string) (string, error) { 147 switch socketType { 148 case SocketTypeUNIX: 149 store, err := persist.GetDriver() 150 if err != nil { 151 return "", err 152 } 153 socketPath := filepath.Join(filepath.Join(store.RunStoragePath(), id), "proxy.sock") 154 return fmt.Sprintf("unix://%s", socketPath), nil 155 case SocketTypeVSOCK: 156 // TODO Build the VSOCK default URL 157 return "", nil 158 default: 159 return "", fmt.Errorf("Unknown socket type: %s", socketType) 160 } 161 } 162 163 func isProxyBuiltIn(pType ProxyType) bool { 164 return pType == KataBuiltInProxyType 165 } 166 167 // proxy is the virtcontainers proxy interface. 168 type proxy interface { 169 // start launches a proxy instance with specified parameters, returning 170 // the PID of the process and the URL used to connect to it. 171 start(params proxyParams) (int, string, error) 172 173 // stop terminates a proxy instance after all communications with the 174 // agent inside the VM have been properly stopped. 175 stop(pid int) error 176 177 //check if the proxy has watched the vm console. 178 consoleWatched() bool 179 } 180 181 func (p *proxyBuiltin) watchConsole(proto, console string, logger *logrus.Entry) (err error) { 182 var ( 183 scanner *bufio.Scanner 184 conn net.Conn 185 ) 186 187 switch proto { 188 case consoleProtoUnix: 189 conn, err = net.Dial("unix", console) 190 if err != nil { 191 return err 192 } 193 // TODO: please see 194 // https://github.com/kata-containers/runtime/issues/1940. 195 case consoleProtoPty: 196 fallthrough 197 default: 198 return fmt.Errorf("unknown console proto %s", proto) 199 } 200 201 p.conn = conn 202 203 go func() { 204 scanner = bufio.NewScanner(conn) 205 for scanner.Scan() { 206 logger.WithFields(logrus.Fields{ 207 "sandbox": p.sandboxID, 208 "vmconsole": scanner.Text(), 209 }).Debug("reading guest console") 210 } 211 212 if err := scanner.Err(); err != nil { 213 if err == io.EOF { 214 logger.Info("console watcher quits") 215 } else { 216 logger.WithError(err).WithFields(logrus.Fields{ 217 "console-protocol": proto, 218 "console-socket": console, 219 }).Error("Failed to read agent logs") 220 } 221 } 222 }() 223 224 return nil 225 } 226 227 // check if the proxy has watched the vm console. 228 func (p *proxyBuiltin) consoleWatched() bool { 229 return p.conn != nil 230 } 231 232 // start is the proxy start implementation for builtin proxy. 233 // It starts the console watcher for the guest. 234 // It returns agentURL to let agent connect directly. 235 func (p *proxyBuiltin) start(params proxyParams) (int, string, error) { 236 if params.logger == nil { 237 return -1, "", fmt.Errorf("Invalid proxy parameter: proxy logger is not set") 238 } 239 240 if p.consoleWatched() { 241 return -1, "", fmt.Errorf("The console has been watched for sandbox %s", params.id) 242 } 243 244 params.logger.Debug("Start to watch the console") 245 246 p.sandboxID = params.id 247 248 // For firecracker, it hasn't support the console watching and it's consoleURL 249 // will be set empty. 250 // TODO: add support for hybrid vsocks, see https://github.com/kata-containers/runtime/issues/2098 251 if params.debug && params.consoleURL != "" && !strings.HasPrefix(params.consoleURL, kataclient.HybridVSockScheme) { 252 err := p.watchConsole(buildinProxyConsoleProto, params.consoleURL, params.logger) 253 if err != nil { 254 p.sandboxID = "" 255 return -1, "", err 256 } 257 } 258 259 return params.hid, params.agentURL, nil 260 } 261 262 // stop is the proxy stop implementation for builtin proxy. 263 func (p *proxyBuiltin) stop(pid int) error { 264 if p.conn != nil { 265 p.conn.Close() 266 p.conn = nil 267 p.sandboxID = "" 268 } 269 return nil 270 }