Skip to content

Commit

Permalink
Merge pull request #13 from Icinga/feature/rename
Browse files Browse the repository at this point in the history
  • Loading branch information
lazyfrosch authored Sep 7, 2020
2 parents d2d8086 + 805ae26 commit 76361b4
Show file tree
Hide file tree
Showing 9 changed files with 913 additions and 125 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ jobs:
- name: Upload artifact
uses: actions/upload-artifact@v1.0.0
with:
name: check_by_powershell
name: check_by_winrm-go${{ matrix.go }}
path: build
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.*.sw[opa]
*~
/build/
check_by_powershell_for_*
check_by_*
.env*
674 changes: 674 additions & 0 deletions COPYING

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
GIT_COMMIT := $(shell git rev-list -1 HEAD)
GO_BUILD := go build -v -ldflags "-X main.GitCommit=$(GIT_COMMIT)"

NAME = check_by_powershell
NAME = check_by_winrm

.PHONY: all clean build test

Expand Down
206 changes: 143 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,67 +1,23 @@
# check_by_powershell
# check_by_winrm

check_by_powershell is a check plugin for Icinga 2 written in GO. It's build on the go-library [winrm](https://github.com/masterzen/winrm), which can execute remote commands on Windows machines through
the use of WinRM/WinRS.
<!-- NOTE: Update this description also in main.go -->

## Preparing the remote Windows machine for Basic authentication
This project supports only basic authentication for local accounts (domain users are not supported). The remote windows system must be prepared for winrm:
Icinga check plugin to run checks and other commands directly on any Windows system using WinRM (Windows Remote Management).

### HTTP
winrm quickconfig
winrm set winrm/config/service/Auth '@{Basic="true"}'
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}'
Main use case would be to call one of the [plugins](https://github.com/Icinga/icinga-powershell-plugins)
from the [Icinga Powershell Framework](https://github.com/Icinga/icinga-powershell-framework). This will avoid the
requirement of installing an Icinga 2 agent on every Windows system.

### HTTPS
By default WinRM uses Kerberos for authentication so Windows never sends the password to the system requesting validation.
WinRM HTTPS requires a local computer "Server Authentication" certificate with a CN matching the hostname, that is not expired, revoked, or self-signed to be installed.
On the remote host, a PowerShell prompt, using the __Run as Administrator__ :
The plugin will require WinRM to be preconfigured for access with a HTTPs or HTTP connection.

$CertThumbprint = 'cert_thumbprint';
Supported authentication methods:

$certificate = Get-ChildItem -Path cert:\ -Recurse | Where-Object Thumbprint -eq $CertThumbprint;
* Basic with local users
* NTLM with local or AD accounts
* TLS client certificate
* (SSH connection)

if ($null -eq $certificate) {
throw 'The provided thumbprint was not found in any certificate stores';
}

# Allow PS-Remote configuration
Enable-PSRemoting -SkipNetworkProfileCheck -Force;

# Disable HTTP transport for PS-Remoting to ensure encryption
Get-ChildItem WSMan:\Localhost\listener | Where-Object Keys -eq "Transport=HTTP" | Remove-Item -Recurse;

# Set the HTTPS Transport with our provided Thumbprint for the SSL certificate
New-Item -Path WSMan:\LocalHost\Listener -Transport HTTPS -Address * -CertificateThumbPrint $CertThumbprint -Force;

# Set Firewall Rule for allowing communication
New-NetFirewallRule -DisplayName "Windows Remote Management (HTTPS-In)" -Name "Windows Remote Management (HTTPS-In)" -Profile Any -LocalPort 5986 -Protocol TCP;

# Enable the HTTPS lisener
Set-Item WSMan:\localhost\Service\EnableCompatibilityHttpsListener -Value true;

# Disable possible old HTTP firewall rules
Disable-NetFirewallRule -DisplayName "Windows Remote Management (HTTP-In)";
Disable-NetFirewallRule -DisplayName "Windows-Remoteverwaltung (HTTP eingehend)";

winrm set winrm/config/service/auth '@{Basic="true"}';
winrm set winrm/config/client '@{TrustedHosts="*"}';

Restart-Service winrm;

If it's necessary to use a self-signed-certificate, you can follow this [guide](https://www.visualstudiogeeks.com/devops/how-to-configure-winrm-for-https-manually)

## GO build example
To compile for a specific platform, you have to set the GOOS and GOARCH environment variables. For more [information](https://golang.org/pkg/go/build/)

| **Platform** | **GOOS** | **GOARCH** |
| ------------- |:-------------:| -----:|
| Mac | darwin | amd64 |
| Linux | linux | amd64 |
| Windows | windows | amd64 |

### Example - Linux
GOOS=linux GOARCH=amd64 go build -o check_by_powershell main.go
Not supported at the moment is Kerberos.

## Usage

Expand All @@ -88,16 +44,140 @@ Arguments:
-V, --version Print version and exit
```

### Execute a script over http
./check_by_powershell -H 192.168.172.217 -p 5985 --cmd "cscript.exe /T:30 /NoLogo C:\Windows\system32\check_time.vbs 1.de.pool.ntp.org 20 240" --user "windowsuser" --password 'secret!pw'
Also, see the Icinga 2 examples in the [icinga2/ directory](icinga2/).

OK - NTP OK: Offset +0.0556797 secs|'offset'=+0.0556797s;20;240;
## Examples

It is necessary that the PowerShell script exits with an exitcode like *exit 2*, otherwise the plugin could exit with an unexpected exitcode.
Calling a PowerShell plugin from the framework is easy:

### Execute a Icinga PowerShell Framework Commandlet over https
./check_by_powerhsell -H "example.local.de" --icingacmd "Invoke-IcingaCheckCPU" --user "windowsuser" --password '!secret!pw' --tls --unsecure -t 30
./check_by_winrm -H example.local.de --user 'ad\user' --password '!secret!pw' \
--icingacmd 'Invoke-IcingaCheckCPU -Warning 80 -Critical 90'

[OK] Check package "CPU Load"
| 'core_23_10'=2.31%;;;0;100 'core_23_3'=2.54%;;;0;100 'core_23_15'=2.12%;;;0;100 'core_23_5'=2.39%;;;0;100
'core_23_1'=2.04%;;;0;100 'core_23'=1.93%;;;0;100 'core_2_15'=2.78%;;;0;100 'core_2_10'=2.89%;;;0;100 [...]
Notes:
* You can use `--insecure` to skip CA trust and certificate checks - be careful!
* You can use `--no-tls` to use a HTTP connection

Executing any other Windows program or script, could be another Icinga plugin:

./check_by_winrm -H 192.168.172.217 \
--user 'windowsuser' --password 'secret!pw' \
--cmd "cscript.exe /T:30 /NoLogo C:\Windows\system32\check_time.vbs 1.de.pool.ntp.org 20 240"

OK - NTP OK: Offset +0.0556797 secs|'offset'=+0.0556797s;20;240;

If you run a program or script like this, you need to make sure to exit the script with a proper exit code, to reflect
the correct status for Icinga.

## Preparing the Windows machine

By default, WinRM is not enabled, and if enabled, will only allow Kerberos authentication. WinRM can be configured in
many ways, to allow connections by HTTP or HTTPs.

Best practice would be to configure WinRM with a TLS certificate, signed by the PKI of the Active Directory domain,
and using NTLM auth to access the systems.

Anything you configure via cmd or powershell needs to be run from an administrative shell.

We start with the minimal setup of enabling WinRM and raising the memory limit:

```
winrm quickconfig
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}'
```

### Setting up a HTTPS / TLS listener

Make sure to install the certificate in the local machine cert store. This example is using PowerShell.

WinRM HTTPS requires a local computer "Server Authentication" certificate with a CN matching the hostname, that is not
expired, revoked, or self-signed to be installed.

```powershell
# Find the cert
Get-ChildItem -Path cert:\LocalMachine\My -Recurse;
# Put the thumbprint here or script it otherwise
$CertThumbprint = 'cert_thumbprint';
# Allow PS-Remote configuration
Enable-PSRemoting -SkipNetworkProfileCheck -Force;
# (optional) Disable HTTP transport for PS-Remoting to ensure encryption
Get-ChildItem WSMan:\Localhost\listener | Where-Object Keys -eq "Transport=HTTP" | Remove-Item -Recurse;
# Set the HTTPS Transport with our provided Thumbprint for the SSL certificate
New-Item -Path WSMan:\LocalHost\Listener -Transport HTTPS -Address * -CertificateThumbPrint $CertThumbprint -Force;
# Set Firewall Rule for allowing communication
New-NetFirewallRule -DisplayName "Windows Remote Management (HTTPS-In)" `
-Name "Windows Remote Management (HTTPS-In)" -Profile Any -LocalPort 5986 -Protocol TCP;
# Enable the HTTPS lisener
Set-Item WSMan:\localhost\Service\EnableCompatibilityHttpsListener -Value true;
# Disable possible old HTTP firewall rules (names language specific)
Disable-NetFirewallRule -DisplayName "Windows Remote Management (HTTP-In)";
Disable-NetFirewallRule -DisplayName "Windows-Remoteverwaltung (HTTP eingehend)";
# (optional) You can configure hosts that are allowed to connect to WinRM
winrm set winrm/config/client '@{TrustedHosts="*"}';
Restart-Service winrm;
```

If it's necessary to use a self-signed-certificate, you can follow the
[guide on visualstudiogeeks.com](https://www.visualstudiogeeks.com/devops/how-to-configure-winrm-for-https-manually).

### Enabling Basic Auth

Basic auth can be used as fallback for NTLM, but will require a local account on each machine.

```
winrm set winrm/config/service/Auth '@{Basic="true"}'
```

### Enabling unencrypted HTTP or basic auth

**Warning:** This is insecure, and should only be done during testing!

This will allow credentials and data transmitted over an **unencrypted** connection like HTTP.

```
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
```

## Manually building the program

The plugin is written in Golang and can easily be compiled from source, see the [documentation](https://golang.org/doc/)
for further details.

```
GOOS=linux GOARCH=amd64 go build -o check_by_winrm .
GOOS=windows GOARCH=amd64 go build -o check_by_winrm.exe .
```

## Acknowledgements

To Brice Figureau [@masterzen](https://github.com/masterzen), who built a
[WinRM client for golang](https://github.com/masterzen/winrm).

## License

Copyright (c) 2020 [NETWAYS GmbH](mailto:info@netways.de)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see [gnu.org/licenses](https://www.gnu.org/licenses/).
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module check_by_powershell
module github.com/NETWAYS/check_by_winrm

go 1.14

Expand Down
76 changes: 20 additions & 56 deletions check_by_powershell_icinga.conf → icinga2/command.conf
Original file line number Diff line number Diff line change
@@ -1,59 +1,46 @@
object Host "windowshost" {
import "generic-host"

address = "192.168.172.217"
vars.powershell_port = 5985
vars.powershell_user = "windowsuser"
vars.powershell_password = "secret!pw"
vars.powershell_tls = true
vars.powershell_unsecure = true

vars.server_type = "windows"
}

object CheckCommand "powershell" {
command = [ PluginDir + "/check_by_powershell" ]
object CheckCommand "winrm" {
command = [ PluginDir + "/check_by_winrm" ]

arguments = {
"-H" = {
value = "$address$"
description = "Host name, IP Address of the remote host"
}
"-p" = {
value = "$powershell_port$"
value = "$winrm_port$"
description = "Port number WinRM"
}
"--user" = {
value = "$powershell_user$"
value = "$winrm_user$"
description = "Username of the remote host"
}
"--password" = {
value = "$powershell_password$"
value = "$winrm_password$"
description = "Password of the user"
}
"--tls" = {
value = "$powershell_tls$"
description = "Use TLS connection"
"--no-tls" = {
value = "$winrm_no_tls$"
description = "Don't use a TLS connection"
}
"--unsecure" = {
value = "$powershell_unsecure$"
"--insecure" = {
value = "$winrm_insecure$"
description = "Verify the hostname on the returned certificate"
}
"--ca" = {
value = "$powershell_ca$"
value = "$winrm_ca$"
description = "CA certificate"
}
"--cert" = {
value = "$powershell_cert$"
value = "$winrm_cert$"
description = "Client certificate"
}
"--key" = {
value = "$powershell_key$"
value = "$winrm_key$"
description = "Client key"
}
"--icingacmd" = {{
var command = macro("$by_powershell_command$")
var arguments = macro("$by_powershell_arguments$")
var command = macro("$by_winrm_command$")
var arguments = macro("$by_winrm_arguments$")
if (typeof(command) == String && !arguments) {
return command
}
Expand All @@ -66,23 +53,23 @@ object CheckCommand "powershell" {
description = "Executes commands of Icinga PowerShell Framework"
}}
"--cmd" = {
value = "$powershell_cmd$"
value = "$winrm_cmd$"
description = "Command to execute on the remote machine"
}
"--auth" = {
value = "$powershell_auth$"
value = "$winrm_auth$"
description = "Authentication mechanism - NTLM | SSH"
}
"--sshhost" = {
value = "$powershell_sshhost$"
value = "$winrm_sshhost$"
description = "SSH Host"
}
"--sshuser" = {
value = "$powershell_sshuser$"
value = "$winrm_sshuser$"
description = "SSH Username"
}
"--sshpassword" = {
value = "$powershell_sshpassword$"
value = "$winrm_sshpassword$"
description = "SSH Password"
}
}
Expand Down Expand Up @@ -135,26 +122,3 @@ object CheckCommand "powershell-checkTime-sync" {
}
}

template Service "powershell-template-service" {
import "generic-service"

vars.original_check_command = check_command
check_command = "powershell"

vars.by_powershell_command = {{ get_check_command(service.vars.original_check_command).command }}
vars.by_powershell_arguments = {{ get_check_command(service.vars.original_check_command).arguments }}
}

apply Service "powershell_check_ntp" {
check_command = "powershell-checkTime-sync"

import "powershell-template-service"

vars.powershell_ntp_server = "de.pool.ntp.org"
vars.powershell_timeoffset = "10ms"
vars.powershell_warning = "10ms"
vars.powershell_critical = "20ms"
vars.powershell_verbosity = "0"

assign where host.vars.server_type == "windows"
}
Loading

0 comments on commit 76361b4

Please sign in to comment.