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  }