Skip to content

Commit 28daf8e

Browse files
Upgrade ELK stack to v8.x (#75)
* Upgrade ELK stack to v8.x Upgrades to v7.17 before v8.x * Fix agent installer, add python container, secure curl requests * Fix snapshot perms, image tagging, post-install message * Fix version check --------- Co-authored-by: naomi <[email protected]>
1 parent 0d6ba7a commit 28daf8e

31 files changed

+21279
-97
lines changed

ELK_VERSIONS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
7.17.9
2+
8.7.0

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ After Sysmon starts sending data to ElasticSearch, Kibana will be ready to go. F
2323
## Installation
2424

2525
### BeaKer Server System Requirements
26-
* Operating System: The preferred platform is x86 64-bit Ubuntu 16.04 LTS. The system should be patched and up to date using apt-get.
26+
* Operating System: The preferred platform is x86 64-bit Ubuntu 20.04 LTS. The system should be patched and up to date using apt-get.
2727
* The automated installer will also support CentOS 7.
2828
* Processor: Two or more cores. Elasticsearch uses parallel processing and benefits from more CPU cores.
2929
* Memory: 8-64GB. Monitoring more hosts requires more RAM.
@@ -32,18 +32,23 @@ After Sysmon starts sending data to ElasticSearch, Kibana will be ready to go. F
3232
### BeaKer Agent System Requirements
3333
* Operating System: Windows x86-64 bit OS
3434
* Powershell Version: 3+
35+
* Installed WinLogBeats version must be <= the Elasticsearch version installed on the BeaKer server, but at least the minimum supported wire version for the Elasticsearch version
36+
* Elasticsearch v8.6.2 supports WinLogBeats 7.17.0 through 8.6.2
37+
* Elasticsearch v7.17.9 supports WinLogBeats 6.8.0 through 7.17.9
3538

3639
### Automated Install: BeaKer Server
3740

3841
Download the [latest release](https://github.com/activecm/BeaKer/releases/latest) tar file, extract it, and inside the `BeaKer` directory,
3942
run `./install_beaker.sh` on the Linux machine that will aggregate your Sysmon data and host Kibana.
4043

44+
** Note that existing BeaKer installations must be upgraded to v7.17 before they can be upgraded to v8.x.
4145
The automated installer will:
4246
- Install Docker and Docker-Compose
4347
- Create a configuration directory in `/etc/BeaKer`
4448
- Install Elasticsearch, Kibana, and load the dashboards
4549
- Set the Elasticsearch superuser password for the `elastic` account
4650
- Set the `sysmon-ingest` user password for connecting WinLogBeats
51+
- Set up index templates, ILM policy, data streams and ingest pipelines
4752

4853
The `beaker` script installed to `/usr/local/bin/beaker` is a wrapper around `docker-compose` and can be used to manage BeaKer.
4954
- To stop BeaKer, run `beaker down`
@@ -55,7 +60,11 @@ After running `./install_beaker.sh` you should be able to access Kibana at `loca
5560

5661
Use the `elastic` account to perform your initial login to Kibana. Additional user accounts can be created using the Kibana interface. The `sysmon-ingest` user account is not allowed to access Kibana.
5762

58-
The Elasticsearch server will begin listening for connections on port 9200 using HTTPS. It expects Sysmon ID 3 Network Events to be published to the ES index `sysmon-%{+YYYY.MM.dd}` using the WinLogBeat schema. See the embedded `winlogbeat.yml` file in `./agent/install-sysmon-beats.ps1` for more info.
63+
The Elasticsearch server will begin listening for connections on port 9200 using HTTPS. It expects Sysmon ID 3 Network Events to be published to:
64+
- WinLogBeats less than v7.17.9: ES index `sysmon-%{+YYYY.MM.dd}`
65+
- WinLogBeats v7.17.9: ES index `winlogbeat-%{[agent.version]}` via data stream
66+
- WinLogBeats v8.6.2: Ingest Pipeline `winlogbeat-%{[agent.version]}-routing`
67+
See the embedded `winlogbeat.yml` file in `./agent/install-sysmon-beats.ps1` for more info.
5968

6069
The easiest way to begin sending data to the server is to use the automated BeaKer agent installer.
6170

agent/install-sysmon-beats.ps1

Lines changed: 168 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,20 @@ the script will ask for the password at runtime. In order to avoid recording the
2929
password, consider editing this file. Change the line `[string]$ESPassword="",` to
3030
`[string]$ESPassword="YOUR_ELASTIC_PASSWORD_HERE",.
3131
32+
.PARAMETER BeatsVersion
33+
The version of Winlogbeat to install. This will override any logic that handles upgrading to an
34+
intermediate version of Winlogbeat before upgrading to a higher major version.
35+
3236
.EXAMPLE
3337
# Asks for Elasticsearch authentication details at runtime
3438
.\install-sysmon-beats.ps1 my-es-host.com 9200
3539
3640
# Reads Elasticsearch authentication details from the command line aguments
3741
.\install-sysmon-beats.ps1 my-es-host.com 9200 elastic elastic_password
3842
43+
# Overrides the version of Winlogbeat to install
44+
.\install-sysmon-beats.ps1 my-es-host.com 9200 elastic elastic_password 8.6.2
45+
3946
.NOTES
4047
The Elasticsearch credentials are stored locally using Elastic Winlogbeat's secure
4148
storage facilities. The ESUsername and ESPassword parameters should not be passed
@@ -44,38 +51,67 @@ enter the credentials during the installation process, or edit the parameters' d
4451
#>
4552

4653
param (
47-
[Parameter(Mandatory=$true)][string]$ESHost,
48-
[string]$ESPort="9200",
49-
[string]$ESUsername="",
50-
[string]$ESPassword=""
54+
[Parameter(Mandatory = $true)][string]$ESHost,
55+
[string]$ESPort = "9200",
56+
[string]$ESUsername = "",
57+
[string]$ESPassword = "",
58+
[string]$BeatsVersion = ""
5159
)
5260

53-
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
54-
{
61+
$ELK_STACK_VERSION = "8.7.0"
62+
63+
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
5564
# Use param values instead of $args because $args doesn't appear to get populated if param values are specified
5665
# Also set the ExecutionPolicy to Bypass otherwise this will likely fail as script
5766
# execution is disabled by default.
5867
$arguments = "-ExecutionPolicy", "Bypass", "-File", $myinvocation.mycommand.definition, $ESHost, $ESPort
59-
if($ESUsername)
60-
{
68+
if ($ESUsername) {
6169
# Only add this argument if the user provided it, otherwise it will be blank and will cause an error
6270
$arguments += $ESUsername
6371
}
64-
if($ESPassword)
65-
{
72+
if ($ESPassword) {
6673
# Only add this argument if the user provided it, otherwise it will be blank and will cause an error
6774
$arguments += $ESPassword
6875
}
76+
if ($BeatsVersion) {
77+
# Only add this argument if the user provided it, otherwise it will be blank and will cause an error
78+
$arguments += $BeatsVersion
79+
}
6980

7081
Start-Process -FilePath powershell -Verb runAs -ArgumentList $arguments
7182
Break
7283
}
7384

85+
86+
[bool] $OverrideBeatsVersion = $false
87+
if ([string]::IsNullOrWhiteSpace("$BeatsVersion")) {
88+
$BeatsVersion = "$ELK_STACK_VERSION"
89+
}
90+
else {
91+
if ($null -eq ("$BeatsVersion" -as [System.Version])) {
92+
throw "Beats version $BeatsVersion is not a valid version, please provide a valid version number."
93+
}
94+
if ([System.Version]$BeatsVersion -lt [System.Version]"7.17.9") {
95+
throw "Minimum supported Beats version is 7.17.9, exiting"
96+
}
97+
$OverrideBeatsVersion = $true
98+
}
99+
100+
# Check for existing winlogbeat installation via Espy
101+
if (Test-Path "$Env:programfiles\Winlogbeat-Espy" -PathType Container) {
102+
Write-Output "Detected existing winlogbeat installation performed by Espy. Continuing the install may result in a partially working Sysmon/winlogbeat setup."
103+
$installAnyway = Read-Host -Prompt "Are you sure you want to continue? [y/n]"
104+
if (($installAnyway -eq 'n') -or ($installAnyway -eq 'N')) {
105+
Exit
106+
}
107+
}
108+
74109
if (-not (Test-Path "$Env:programfiles\Sysmon" -PathType Container)) {
75-
Invoke-WebRequest -OutFile Sysmon.zip https://download.sysinternals.com/files/Sysmon.zip
76-
Expand-Archive .\Sysmon.zip
77-
rm .\Sysmon.zip
78-
mv .\Sysmon\ "$Env:programfiles"
110+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
111+
Invoke-WebRequest -OutFile Sysmon.zip https://download.sysinternals.com/files/Sysmon.zip
112+
Expand-Archive .\Sysmon.zip
113+
rm .\Sysmon.zip
114+
mv .\Sysmon\ "$Env:programfiles"
79115
}
80116

81117
echo @"
@@ -154,32 +190,109 @@ echo @"
154190

155191
& "$Env:programfiles\Sysmon\Sysmon64.exe" -accepteula -i "$Env:programfiles\Sysmon\sysmon-net-only.xml"
156192

157-
if (-not (Test-Path "$Env:programfiles\winlogbeat*" -PathType Container)) {
158-
Invoke-WebRequest -OutFile WinLogBeat.zip https://artifacts.elastic.co/downloads/beats/winlogbeat/winlogbeat-7.5.2-windows-x86_64.zip
159-
Expand-Archive .\WinLogBeat.zip
160-
rm .\WinLogBeat.zip
161-
mv .\WinLogBeat\winlogbeat* "$Env:programfiles"
193+
$InstalledBeatsVersion = ""
194+
[bool] $DownloadWinlogbeat = $false
195+
196+
# Check for fresh install or pre-7.17 install
197+
if (-not (Test-Path "$Env:programfiles\Winlogbeat-BeaKer\winlogbeat.exe" -PathType Leaf)) {
198+
$DownloadWinlogbeat = $true
199+
200+
# Create install directory if it doesn't exist
201+
if (-not (Test-Path "$Env:programfiles\Winlogbeat-BeaKer" -PathType Container)) {
202+
mkdir "$Env:programfiles\Winlogbeat-BeaKer" > $null
203+
}
204+
205+
# Check if this is a pre-7.17 upgrade install
206+
if ((Test-Path "$Env:programfiles\winlogbeat-7*" -PathType Container)) {
207+
### Make sure that Beats is upgraded to 7.17 before installing v8.x
208+
# Install winlogbeat 7.17.9 if the current version is less than 8.x
209+
if (!$OverrideBeatsVersion) {
210+
$BeatsVersion = "7.17.9"
211+
}
212+
Copy-Item "$Env:programfiles\winlogbeat-7*\winlogbeat.yml" "$Env:programfiles\Winlogbeat-BeaKer"
213+
}
214+
}
215+
else {
216+
# Check if currently installed version is outdated
217+
$InstalledBeatsVersion = (& "$Env:programfiles\Winlogbeat-BeaKer\winlogbeat.exe" version | Select-String -Pattern "(?<=winlogbeat version )(\d+\.\d+\.\d+)").Matches.Value
218+
if ($null -eq ("$InstalledBeatsVersion" -as [System.Version])) {
219+
220+
if (!$OverrideBeatsVersion) {
221+
throw "Unable to retrieve installed winlogbeat version"
222+
}
223+
else {
224+
Write-Output "Unable to retrieve installed winlogbeat version, continuing anyway"
225+
$DownloadWinlogbeat = $true
226+
}
227+
}
228+
else {
229+
if ([System.Version]"$InstalledBeatsVersion" -lt [System.Version]"$BeatsVersion") {
230+
$DownloadWinlogbeat = $true
231+
}
232+
}
233+
}
234+
235+
# Download winlogbeat and move it to install directory
236+
if ($DownloadWinlogbeat) {
237+
Write-Output "######## Downloading winlogbeat version $BeatsVersion ########"
238+
239+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
240+
Invoke-WebRequest -OutFile WinLogBeat.zip https://artifacts.elastic.co/downloads/beats/winlogbeat/winlogbeat-"$BeatsVersion"-windows-x86_64.zip
241+
Expand-Archive .\WinLogBeat.zip
242+
rm .\WinLogBeat.zip
243+
rm .\WinLogBeat\winlogbeat*\winlogbeat.yml
244+
245+
# Stop winlogbeat service if it exists
246+
if (Get-Service winlogbeat -ErrorAction SilentlyContinue) {
247+
Stop-Service winlogbeat
248+
(Get-Service winlogbeat).WaitForStatus('Stopped')
249+
Start-Sleep -s 1
250+
}
251+
Copy-Item -Path .\WinLogBeat\winlogbeat*\* -Destination "$Env:programfiles\Winlogbeat-BeaKer\" -Recurse -Force
252+
rm .\Winlogbeat -Recurse
253+
}
254+
255+
Write-Output "######## Installing winlogbeat version $BeatsVersion ########"
256+
257+
258+
# Begin winlogbeat configuration
259+
Set-Location "$Env:programfiles\Winlogbeat-BeaKer\"
260+
261+
# Backup winlogbeat config if it exists
262+
if (Test-Path -PathType Leaf .\winlogbeat.yml) {
263+
if ($DownloadWinlogbeat) {
264+
# Backup config with its version in the name if upgrading to a new Beats version
265+
# so that the config isn't overwritten by subsequent upgrades. This is useful in case
266+
# breaking changes between configurations need to be referenced in the future for troubleshooting
267+
Copy-Item .\winlogbeat.yml .\winlogbeat-$InstalledBeatsVersion-old.yml.bak
268+
}
269+
else {
270+
Copy-Item .\winlogbeat.yml .\winlogbeat.yml.bak
271+
}
162272
}
163273

164-
cd "$Env:programfiles\winlogbeat*\"
165274
.\winlogbeat.exe --path.data "C:\ProgramData\winlogbeat" keystore create
166-
if($ESUsername) {
167-
Write-Output "$ESUsername" | .\winlogbeat.exe --path.data "C:\ProgramData\winlogbeat" keystore add ES_USERNAME --stdin
168-
} else {
169-
.\winlogbeat.exe --path.data "C:\ProgramData\winlogbeat" keystore add ES_USERNAME
275+
if ($ESUsername) {
276+
Write-Output "$ESUsername" | .\winlogbeat.exe --path.data "C:\ProgramData\winlogbeat" keystore add ES_USERNAME --stdin
170277
}
171-
if($ESPassword) {
172-
Write-Output "$ESPassword" | .\winlogbeat.exe --path.data "C:\ProgramData\winlogbeat" keystore add ES_PASSWORD --stdin
173-
} else {
174-
.\winlogbeat.exe --path.data "C:\ProgramData\winlogbeat" keystore add ES_PASSWORD
278+
else {
279+
.\winlogbeat.exe --path.data "C:\ProgramData\winlogbeat" keystore add ES_USERNAME
280+
}
281+
if ($ESPassword) {
282+
Write-Output "$ESPassword" | .\winlogbeat.exe --path.data "C:\ProgramData\winlogbeat" keystore add ES_PASSWORD --stdin
283+
}
284+
else {
285+
.\winlogbeat.exe --path.data "C:\ProgramData\winlogbeat" keystore add ES_PASSWORD
175286
}
176287

177288
# Set ACL's of the $Env:ProgramData\winlogbeat folder to be the same as $Env:ProgramFiles\winlogbeat* (the main install path)
178289
# This helps ensure that "normal" users aren't able to access the $Env:ProgramData\winlogbeat folder
179-
Get-ACL -Path "$Env:ProgramFiles\winlogbeat*" | Set-ACL -Path "$Env:ProgramData\winlogbeat"
290+
Get-ACL -Path "$Env:ProgramFiles\\Winlogbeat-BeaKer*" | Set-ACL -Path "$Env:ProgramData\\winlogbeat"
180291

181292
rm .\winlogbeat.yml
182-
echo @"
293+
294+
if ([System.Version]$BeatsVersion -lt [System.Version]"8.0.0") {
295+
Write-Output @"
183296
winlogbeat.event_logs:
184297
- name: Microsoft-Windows-Sysmon/Operational
185298
event_id: 3
@@ -190,19 +303,40 @@ winlogbeat.event_logs:
190303
file: ${path.home}/module/sysmon/config/winlogbeat-sysmon.js
191304
192305
setup.ilm.enabled: false
193-
setup.template.enabled: true
194-
setup.template.name: `"sysmon`"
195-
setup.template.pattern: `"sysmon-*`"
306+
setup.template.name: `"winlogbeat-%{[agent.version]}`"
307+
setup.template.pattern: `"winlogbeat-%{[agent.version]}`"
196308
197309
output.elasticsearch:
198310
hosts:
199311
- https://${ESHost}:${ESPort}
200-
index: `"sysmon-%{+YYYY.MM.dd}`"
312+
index: `"winlogbeat-%{[agent.version]}`"
201313
username: `"`${ES_USERNAME}`"
202314
password: `"`${ES_PASSWORD}`"
203315
ssl:
204316
enabled: true
205317
verification_mode: none
206318
"@ > winlogbeat.yml
319+
}
320+
else {
321+
Write-Output @"
322+
winlogbeat.event_logs:
323+
- name: Microsoft-Windows-Sysmon/Operational
324+
event_id: 3
325+
326+
setup.ilm.enabled: false
327+
328+
output.elasticsearch:
329+
hosts:
330+
- https://${ESHost}:${ESPort}
331+
pipeline: winlogbeat-%{[agent.version]}-routing
332+
username: `"`${ES_USERNAME}`"
333+
password: `"`${ES_PASSWORD}`"
334+
ssl:
335+
enabled: true
336+
verification_mode: none
337+
"@ > winlogbeat.yml
338+
}
339+
207340
PowerShell.exe -ExecutionPolicy UnRestricted -File .\install-service-winlogbeat.ps1
341+
208342
Start-Service winlogbeat

check_kibana/Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM python:3.9
2+
ADD check_kibana.py .
3+
CMD ["python3", "./check_kibana.py"]

check_kibana/check_kibana.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import sys
2+
import json
3+
4+
# check kibana status for v8.x
5+
6+
7+
def check_kibana_migrations_v8(status):
8+
if "core" in status:
9+
if "savedObjects" in status["core"]:
10+
if "summary" in status["core"]["savedObjects"]:
11+
if "completed migrations" in status["core"]["savedObjects"]["summary"] \
12+
and "available" in status["core"]["savedObjects"]["summary"]:
13+
sys.exit(0)
14+
15+
sys.exit(2)
16+
17+
18+
def check_kibana_status_v8(status):
19+
if "overall" in status:
20+
if "level" in status["overall"]:
21+
if status["overall"]["level"] == "available":
22+
check_kibana_migrations_v8(status)
23+
sys.exit(1)
24+
25+
# check kibana status for v7.17
26+
27+
28+
def check_kibana_status(status):
29+
if "overall" in status:
30+
if "state" in status["overall"]:
31+
if status["overall"]["state"] == "green":
32+
check_migration_status(status)
33+
check_kibana_status_v8(status)
34+
35+
36+
def check_migration_status(status):
37+
if "statuses" in status:
38+
for plugin in status["statuses"]:
39+
if "core:savedObjects" in plugin["id"]:
40+
if "message" in plugin:
41+
if "completed migrations" in plugin["message"] \
42+
and "available" in plugin["message"]:
43+
sys.exit(0)
44+
sys.exit(2)
45+
46+
47+
# takes piped in curl output from https://localhost:5601/api/status
48+
# and checks whether or not kibana is online and finished with data migrations (upgrades)
49+
try:
50+
response = json.load(sys.stdin)
51+
except ValueError:
52+
sys.exit(4)
53+
54+
if "status" in response:
55+
status = response["status"]
56+
check_kibana_status(status)
57+
58+
sys.exit(3)

0 commit comments

Comments
 (0)