go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/_motor/providers/winrm/provider.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package winrm 5 6 import ( 7 "bytes" 8 "errors" 9 "os" 10 "time" 11 12 "github.com/masterzen/winrm" 13 "github.com/rs/zerolog/log" 14 "github.com/spf13/afero" 15 "go.mondoo.com/cnquery/motor/providers" 16 os_provider "go.mondoo.com/cnquery/motor/providers/os" 17 "go.mondoo.com/cnquery/motor/providers/winrm/cat" 18 "go.mondoo.com/cnquery/motor/vault" 19 ) 20 21 var _ providers.Instance = (*Provider)(nil) 22 23 func VerifyConfig(pCfg *providers.Config) (*winrm.Endpoint, error) { 24 if pCfg.Backend != providers.ProviderType_WINRM { 25 return nil, errors.New("only winrm backend for winrm transport supported") 26 } 27 28 winrmEndpoint := &winrm.Endpoint{ 29 Host: pCfg.Host, 30 Port: int(pCfg.Port), 31 Insecure: pCfg.Insecure, 32 HTTPS: true, 33 Timeout: time.Duration(0), 34 } 35 36 return winrmEndpoint, nil 37 } 38 39 func DefaultConfig(endpoint *winrm.Endpoint) *winrm.Endpoint { 40 // use default port if port is 0 41 if endpoint.Port <= 0 { 42 endpoint.Port = 5986 43 } 44 45 if endpoint.Port == 5985 { 46 log.Warn().Msg("winrm port 5985 is using http communication instead of https, passwords are not encrypted") 47 endpoint.HTTPS = false 48 } 49 50 if os.Getenv("WINRM_DISABLE_HTTPS") == "true" { 51 log.Warn().Msg("WINRM_DISABLE_HTTPS is set, winrm is using http communication instead of https, passwords are not encrypted") 52 endpoint.HTTPS = false 53 } 54 55 return endpoint 56 } 57 58 // New creates a winrm client and establishes a connection to verify the connection 59 func New(pCfg *providers.Config) (*Provider, error) { 60 // ensure all required configs are set 61 winrmEndpoint, err := VerifyConfig(pCfg) 62 if err != nil { 63 return nil, err 64 } 65 66 // set default config if required 67 winrmEndpoint = DefaultConfig(winrmEndpoint) 68 69 params := winrm.DefaultParameters 70 params.TransportDecorator = func() winrm.Transporter { return &winrm.ClientNTLM{} } 71 72 // search for password secret 73 c, err := vault.GetPassword(pCfg.Credentials) 74 if err != nil { 75 return nil, errors.New("missing password for winrm transport") 76 } 77 78 client, err := winrm.NewClientWithParameters(winrmEndpoint, c.User, string(c.Secret), params) 79 if err != nil { 80 return nil, err 81 } 82 83 // test connection 84 log.Debug().Str("user", c.User).Str("host", pCfg.Host).Msg("winrm> connecting to remote shell via WinRM") 85 shell, err := client.CreateShell() 86 if err != nil { 87 return nil, err 88 } 89 90 err = shell.Close() 91 if err != nil { 92 return nil, err 93 } 94 95 log.Debug().Msg("winrm> connection established") 96 return &Provider{ 97 Endpoint: winrmEndpoint, 98 Client: client, 99 kind: pCfg.Kind, 100 runtime: pCfg.Runtime, 101 }, nil 102 } 103 104 type Provider struct { 105 Endpoint *winrm.Endpoint 106 Client *winrm.Client 107 kind providers.Kind 108 runtime string 109 fs afero.Fs 110 } 111 112 func (p *Provider) RunCommand(command string) (*os_provider.Command, error) { 113 log.Debug().Str("command", command).Str("provider", "winrm").Msg("winrm> run command") 114 115 stdoutBuffer := &bytes.Buffer{} 116 stderrBuffer := &bytes.Buffer{} 117 118 mcmd := &os_provider.Command{ 119 Command: command, 120 Stdout: stdoutBuffer, 121 Stderr: stderrBuffer, 122 } 123 124 // Note: winrm does not return err of the command was executed with a non-zero exit code 125 exitCode, err := p.Client.Run(command, stdoutBuffer, stderrBuffer) 126 if err != nil { 127 log.Error().Err(err).Str("command", command).Msg("could not execute winrm command") 128 return mcmd, err 129 } 130 131 mcmd.ExitStatus = exitCode 132 return mcmd, nil 133 } 134 135 func (p *Provider) FileInfo(path string) (os_provider.FileInfoDetails, error) { 136 fs := p.FS() 137 afs := &afero.Afero{Fs: fs} 138 stat, err := afs.Stat(path) 139 if err != nil { 140 return os_provider.FileInfoDetails{}, err 141 } 142 143 uid := int64(-1) 144 gid := int64(-1) 145 mode := stat.Mode() 146 147 return os_provider.FileInfoDetails{ 148 Mode: os_provider.FileModeDetails{mode}, 149 Size: stat.Size(), 150 Uid: uid, 151 Gid: gid, 152 }, nil 153 } 154 155 func (p *Provider) FS() afero.Fs { 156 if p.fs == nil { 157 p.fs = cat.New(p) 158 } 159 return p.fs 160 } 161 162 func (p *Provider) Close() { 163 // nothing to do yet 164 } 165 166 func (p *Provider) Capabilities() providers.Capabilities { 167 return providers.Capabilities{ 168 providers.Capability_RunCommand, 169 providers.Capability_File, 170 } 171 } 172 173 func (p *Provider) Kind() providers.Kind { 174 return p.kind 175 } 176 177 func (p *Provider) Runtime() string { 178 return p.runtime 179 } 180 181 func (p *Provider) PlatformIdDetectors() []providers.PlatformIdDetector { 182 return []providers.PlatformIdDetector{ 183 providers.HostnameDetector, 184 providers.CloudDetector, 185 } 186 }