-
Notifications
You must be signed in to change notification settings - Fork 97
Expand file tree
/
Copy pathelastic-container.sh
More file actions
executable file
·384 lines (320 loc) · 12.1 KB
/
elastic-container.sh
File metadata and controls
executable file
·384 lines (320 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
#!/bin/bash -eu
set -o pipefail
ipvar="0.0.0.0"
# These should be set in the .env file
declare LinuxDR
declare WindowsDR
declare MacOSDR
declare COMPOSE
# Ignore following warning
# shellcheck disable=SC1091
. .env
HEADERS=(
-H "kbn-version: ${STACK_VERSION}"
-H "kbn-xsrf: kibana"
-H 'Content-Type: application/json'
)
passphrase_reset() {
if grep -Fq "changeme" .env; then
echo "Sorry, looks like you haven't updated the passphrase from the default"
echo "Please update the changeme passphrases in the .env file."
exit 1
else
echo "Passphrase has been reset. Proceeding."
fi
}
check_required_apps() {
apps=("jq" "curl")
for app in "${apps[@]}"; do
if ! command -v "$app" &>/dev/null; then
echo "The application '$app' is not installed."
exit 1
fi
done
echo "All required applications are installed."
}
# Create the script usage menu
usage() {
cat <<EOF | sed -e 's/^ //'
usage: ./elastic-container.sh [-v] [-u] (stage|start|stop|restart|status|update-version|help)
actions:
stage downloads all necessary images to local storage
start creates a container network and starts containers
stop stops running containers without removing them
destroy stops and removes the containers, the network, and volumes created
restart restarts all the stack containers
status check the status of the stack containers
clear clear all documents in logs and metrics indexes
update-version set STACK_VERSION in .env to the newest stable x.y.z tag from Docker Hub (elastic/elasticsearch)
help print this message
flags:
-v enable verbose output
-u same as update-version (refreshes STACK_VERSION in .env), then exit
EOF
}
# Set STACK_VERSION in .env to the highest stable semver tag listed for elastic/elasticsearch on Docker Hub.
refresh_stack_version() {
check_required_apps
local hub_repo="https://hub.docker.com/v2/repositories/elastic/elasticsearch/tags"
local page=1
local curl_opts=(-fsSL)
if [ "${verbose:-0}" -eq 1 ]; then
curl_opts=(-fSL)
fi
local all_names=""
echo "Querying Docker Hub for elastic/elasticsearch tags..."
while [ "${page}" -le 40 ]; do
local json next
if ! json=$(curl "${curl_opts[@]}" "${hub_repo}?page_size=100&page=${page}"); then
echo "Failed to fetch Docker Hub tags (page ${page})." >&2
exit 1
fi
all_names+=$(jq -r '.results[].name' <<<"${json}")
all_names+=$'\n'
next=$(jq -r '.next // empty' <<<"${json}")
[ -z "${next}" ] && break
page=$((page + 1))
done
local latest
latest=$(grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' <<<"${all_names}" | sort -V | tail -1)
if [ -z "${latest}" ]; then
echo "Could not find a stable semver tag (x.y.z) on Docker Hub." >&2
exit 1
fi
local current
current=$(grep -E '^STACK_VERSION=' .env | head -1 | cut -d= -f2- | tr -d '\r')
if [ -z "${current}" ]; then
echo "No active STACK_VERSION= line found in .env." >&2
exit 1
fi
if [ "${current}" = "${latest}" ]; then
echo "STACK_VERSION is already ${latest} (newest stable tag found on Docker Hub)."
return 0
fi
case "$(uname -s)" in
Darwin)
sed -i '' "s/^STACK_VERSION=.*/STACK_VERSION=${latest}/" .env
;;
*)
sed -i "s/^STACK_VERSION=.*/STACK_VERSION=${latest}/" .env
;;
esac
echo "Updated STACK_VERSION in .env: ${current} -> ${latest}"
echo "Images pull from docker.elastic.co; tags match Docker Hub elastic/elasticsearch releases."
}
# Create a function to enable the Detection Engine and load prebuilt rules in Kibana
configure_kbn() {
MAXTRIES=15
i=${MAXTRIES}
while [ $i -gt 0 ]; do
STATUS=$(curl -I -k --silent "${LOCAL_KBN_URL}" | head -n 1 | cut -d ' ' -f2)
echo
echo "Attempting to enable the Detection Engine and install prebuilt Detection Rules."
if [ "${STATUS}" == "302" ]; then
echo
echo "Kibana is up. Proceeding."
echo
output=$(curl -k --silent "${HEADERS[@]}" --user "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -XPOST "${LOCAL_KBN_URL}/api/detection_engine/index")
[[ ${output} =~ '"acknowledged":true' ]] || (
echo
echo "Detection Engine setup failed :-("
exit 1
)
echo "Detection engine enabled. Installing prepackaged rules."
curl -k --silent "${HEADERS[@]}" --user "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -XPUT "${LOCAL_KBN_URL}/api/detection_engine/rules/prepackaged" 1>&2
echo
echo "Prepackaged rules installed!"
echo
if [[ "${LinuxDR}" -eq 0 && "${WindowsDR}" -eq 0 && "${MacOSDR}" -eq 0 ]]; then
echo "No detection rules enabled in the .env file, skipping detection rules enablement."
echo
break
else
echo "Enabling detection rules"
if [ "${LinuxDR}" -eq 1 ]; then
curl -k --silent "${HEADERS[@]}" --user "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -X POST "${LOCAL_KBN_URL}/api/detection_engine/rules/_bulk_action" -d'
{
"query": "alert.attributes.tags: (\"Linux\" OR \"OS: Linux\")",
"action": "enable"
}
' 1>&2
echo
echo "Successfully enabled Linux detection rules"
fi
if [ "${WindowsDR}" -eq 1 ]; then
curl -k --silent "${HEADERS[@]}" --user "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -X POST "${LOCAL_KBN_URL}/api/detection_engine/rules/_bulk_action" -d'
{
"query": "alert.attributes.tags: (\"Windows\" OR \"OS: Windows\")",
"action": "enable"
}
' 1>&2
echo
echo "Successfully enabled Windows detection rules"
fi
if [ "${MacOSDR}" -eq 1 ]; then
curl -k --silent "${HEADERS[@]}" --user "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -X POST "${LOCAL_KBN_URL}/api/detection_engine/rules/_bulk_action" -d'
{
"query": "alert.attributes.tags: (\"macOS\" OR \"OS: macOS\")",
"action": "enable"
}
' 1>&2
echo
echo "Successfully enabled MacOS detection rules"
fi
fi
echo
break
else
echo
echo "Kibana still loading. Trying again in 40 seconds"
fi
sleep 40
i=$((i - 1))
done
[ $i -eq 0 ] && echo "Exceeded MAXTRIES (${MAXTRIES}) to setup detection engine." && exit 1
return 0
}
get_host_ip() {
os=$(uname -s)
if [ "${os}" == "Linux" ]; then
ipvar=$(hostname -I | awk '{ print $1}')
elif [ "${os}" == "Darwin" ]; then
ipvar=$(ifconfig en0 | awk '$1 == "inet" {print $2}')
fi
}
set_fleet_values() {
# Get the current Fleet settings
CURRENT_SETTINGS=$(curl -k -s -u "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -X GET "${LOCAL_KBN_URL}/api/fleet/agents/setup" -H "Content-Type: application/json")
# Check if Fleet is already set up
if echo "$CURRENT_SETTINGS" | grep -q '"isInitialized": true'; then
echo "Fleet settings are already configured."
return
fi
echo "Fleet is not initialized, setting up Fleet..."
fingerprint=$(${COMPOSE} exec -w /usr/share/elasticsearch/config/certs/ca elasticsearch cat ca.crt | openssl x509 -noout -fingerprint -sha256 | cut -d "=" -f 2 | tr -d :)
printf '{"host_urls": ["%s"], "name": "default", "is_default": true}' "https://${ipvar}:${FLEET_PORT}" | curl -k --silent --user "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -XPOST "${HEADERS[@]}" "${LOCAL_KBN_URL}/api/fleet/fleet_server_hosts" -d @- | jq
printf '{"hosts": ["%s"]}' "https://${ipvar}:9200" | curl -k --silent --user "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -XPUT "${HEADERS[@]}" "${LOCAL_KBN_URL}/api/fleet/outputs/fleet-default-output" -d @- | jq
printf '{"ca_trusted_fingerprint": "%s"}' "${fingerprint}" | curl -k --silent --user "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -XPUT "${HEADERS[@]}" "${LOCAL_KBN_URL}/api/fleet/outputs/fleet-default-output" -d @- | jq
printf '{"config_yaml": "%s"}' "ssl.verification_mode: certificate" | curl -k --silent --user "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -XPUT "${HEADERS[@]}" "${LOCAL_KBN_URL}/api/fleet/outputs/fleet-default-output" -d @- | jq
policy_id=$(printf '{"name": "%s", "description": "%s", "namespace": "%s", "monitoring_enabled": ["logs","metrics"], "inactivity_timeout": 1209600}' "Endpoint Policy" "" "default" | curl -k --silent --user "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -XPOST "${HEADERS[@]}" "${LOCAL_KBN_URL}/api/fleet/agent_policies?sys_monitoring=true" -d @- | jq -r '.item.id')
pkg_version=$(curl -k --user "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -XGET "${HEADERS[@]}" "${LOCAL_KBN_URL}/api/fleet/epm/packages/endpoint" -d : | jq -r '.item.version')
printf "{\"name\": \"%s\", \"description\": \"%s\", \"namespace\": \"%s\", \"policy_id\": \"%s\", \"enabled\": %s, \"inputs\": [{\"enabled\": true, \"streams\": [], \"type\": \"ENDPOINT_INTEGRATION_CONFIG\", \"config\": {\"_config\": {\"value\": {\"type\": \"endpoint\", \"endpointConfig\": {\"preset\": \"EDRComplete\"}}}}}], \"package\": {\"name\": \"endpoint\", \"title\": \"Elastic Defend\", \"version\": \"${pkg_version}\"}}" "Elastic Defend" "" "default" "${policy_id}" "true" | curl -k --silent --user "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -XPOST "${HEADERS[@]}" "${LOCAL_KBN_URL}/api/fleet/package_policies" -d @- | jq
}
clear_documents() {
if (($(curl -k --silent "${HEADERS[@]}" --user "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -X DELETE "https://${ipvar}:9200/_data_stream/logs-*" | grep -c "true") > 0)); then
printf "Successfully cleared logs data stream"
else
printf "Failed to clear logs data stream"
fi
echo
if (($(curl -k --silent "${HEADERS[@]}" --user "${ELASTIC_USERNAME}:${ELASTIC_PASSWORD}" -X DELETE "https://${ipvar}:9200/_data_stream/metrics-*" | grep -c "true") > 0)); then
printf "Successfully cleared metrics data stream"
else
printf "Failed to clear metrics data stream"
fi
echo
}
# Logic to enable the verbose output if needed
OPTIND=1 # Reset in case getopts has been used previously in the shell.
verbose=0
update_stack_version_flag=0
while getopts "vu" opt; do
case "$opt" in
v)
verbose=1
;;
u)
update_stack_version_flag=1
;;
*) ;;
esac
done
shift $((OPTIND - 1))
[ "${1:-}" = "--" ] && shift
if [ $verbose -eq 1 ]; then
exec 3<>/dev/stderr
else
exec 3<>/dev/null
fi
if [ "${update_stack_version_flag}" -eq 1 ]; then
refresh_stack_version
exit 0
fi
ACTION="${*:-help}"
if docker compose >/dev/null; then
COMPOSE="docker compose"
elif command -v docker-compose >/dev/null; then
COMPOSE="docker-compose"
else
case "${ACTION}" in
help | "update-version") ;;
*)
echo "elastic-container requires docker compose!"
exit 2
;;
esac
fi
case "${ACTION}" in
"stage")
# Collect the Elastic, Kibana, and Elastic-Agent Docker images
docker pull "docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}"
docker pull "docker.elastic.co/kibana/kibana:${STACK_VERSION}"
docker pull "docker.elastic.co/elastic-agent/elastic-agent:${STACK_VERSION}"
;;
"start")
passphrase_reset
check_required_apps
get_host_ip
echo "Starting Elastic Stack network and containers."
${COMPOSE} up -d --no-deps
configure_kbn 1>&2 2>&3
echo "Waiting 40 seconds for Fleet Server setup."
echo
sleep 40
echo "Populating Fleet Settings."
set_fleet_values > /dev/null 2>&1
echo
echo "READY SET GO!"
echo
echo "Browse to https://localhost:${KIBANA_PORT}"
if [ $verbose -eq 1 ]; then
echo "Username: ${ELASTIC_USERNAME}"
echo "Passphrase: ${ELASTIC_PASSWORD}"
fi
echo
;;
"stop")
echo "Stopping running containers."
${COMPOSE} stop
;;
"destroy")
echo "#####"
echo "Stopping and removing the containers, network, and volumes created."
echo "#####"
${COMPOSE} down -v
;;
"restart")
echo "#####"
echo "Restarting all Elastic Stack components."
echo "#####"
${COMPOSE} restart elasticsearch kibana fleet-server
;;
"status")
${COMPOSE} ps | grep -v setup
;;
"clear")
clear_documents
;;
"update-version")
refresh_stack_version
;;
"help")
usage
;;
*)
echo -e "Proper syntax not used. See the usage\n"
usage
;;
esac
# Close FD 3
exec 3>&-