-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcwatch.sh
executable file
·345 lines (326 loc) · 11.8 KB
/
cwatch.sh
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
#!/bin/bash
# CWATCH (Docker Image Checker) V5
# (C) 2020 Peter Truman
# All Rights Reserved
#
# Use of this scripts is at executors own risk.
# See the Licence at https://github.com/ptruman/cwatch/blob/master/LICENSE or in the local LICENSE file as appropriate
CWATCHVer=5.3
##### PROCESS DEFAULT ENVIRONMENT VARIABLES #####
# Set DEBUG to 1 if you want full sdtdout...
if [ ! $DEBUG ]; then
DEBUG=0
fi
# Check for Registry ENVs
if [ ! $DOCKER_REGISTRY ]; then
DOCKER_REGISTRY="registry.hub.docker.com"
fi
if [ ! $DOCKER_REGISTRY_SERVICE ]; then
DOCKER_REGISTRY_SERVICE="registry.docker.io"
fi
if [ ! $DOCKER_AUTH_SERVICE ]; then
DOCKER_AUTH_SERVICE="auth.docker.io"
fi
# Handle Image processing rules
if [ ! $BEHAVIOUR ]; then
BEHAVIOUR=ALL
DEFBEHAV=1
else
BEHAVIOUR=${BEHAVIOUR^^}
DEFBEHAV=0
fi
#####
# Check if we are in a container...
IsContainer=0
FirstPID=`ps -aef | grep -v PID | sort | head -1| awk '{print $1}'`
FirstProc=`ps -aef | grep -v PID | sort | head -1| awk '{print $4}'`
if [ $FirstPID = 1 ]; then
if [ $FirstProc = "crond" ]; then
IsContainer=1
fi
fi
# Instantiate arrays
UpdateOk=()
UpdateReq=()
UpdateQuery=()
OUTPUT=()
# Define Output function
SendOutput()
{
OutputType="${@:1:1}"
RemOutput="${@:2}"
if [ $DEBUG = 1 ]; then
if [ $IsContainer = 1 ]; then
echo "$RemOutput" >> /proc/1/fd/1
echo "$RemOutput"
else
echo "$RemOutput"
fi
OUTPUT+=("$RemOutput")
else
if [ $OutputType = "S" ]; then
OUTPUT+=("$RemOutput")
fi
if [ $1 ]; then
# We have a parameter, thus likely running on CLI (local)
if [ $OutputType = "S" ]; then
echo $RemOutput
fi
fi
fi
}
TMPFile="/tmp/$RANDOM"
# Start processing
StartTime=`date +%s`
if [ $1 ]; then
SendOutput D "CWATCH >> CLI argument found - forcing STDOUT"
fi
SendOutput S "CWATCH >> Starting up...please wait...($CWATCHVer)"
# Check where we are...
if [ $IsContainer = 1 ]; then
SendOutput D "CWATCH >> Running in a container"
else
SendOutput D "CWATCH >> Running locally"
fi
# Check Behaviour settings
if [ $BEHAVIOUR = "ALL" ]; then
if [ $DEFBEHAV = 1 ]; then
SendOutput D "CWATCH >> Behaviour : INCLUDE:ALL (Defaulted)"
else
SendOutput D "CWATCH >> Behaviour : INCLUDE:ALL (SetByEnv)"
fi
fi
if [ $BEHAVIOUR = "INCLUDE" ]; then
SendOutput D "CWATCH >> Behaviour : INCLUDE:Explicit (SetByEnv)"
fi
if [ $BEHAVIOUR = "EXCLUDE" ]; then
SendOutput D "CWATCH >> Behaviour : EXCLUDE:Explcit (SetByEnv)"
fi
# Check Email status/config
if [ ! -f /etc/msmtprc ]; then
# No email config found - is email enabled?
if [ $CWATCH_ENABLE_EMAIL ]; then
if [ $CWATCH_ENABLE_EMAIL = 1 ]; then
if [ -f $CWATCH_EMAIL_PORT ]; then
CWATCH_EMAIL_PORT=25
fi
if [ -f $CWATCH_EMAIL_DOMAIN ]; then
CWATCH_EMAIL_DOMAIN=local
fi
if [ $CWATCH_EMAIL_FROM ]; then
CWATCH_EMAIL_FROM=`echo $CWATCH_EMAIL_FROM | sed s/\"//g;`
else
SendOutput D "CWATCH >> Email enabled - no CWATCH_EMAIL_FROM set - email will fail!"
fi
if [ $CWATCH_EMAIL_TYPE = "SMTP" ]; then
SendOutput D "CWATCH >> Creating email template."
# Check for TLS settings
if [ ! $CWATCH_EMAIL_TLS ]; then
CWATCH_EMAIL_TLS=off
else
CWATCH_EMAIL_TLS=${CWATCH_EMAIL_TLS,,}
fi
if [ ! $CWATCH_EMAIL_STARTTLS ]; then
CWATCH_EMAIL_STARTTLS=off
else
CWATCH_EMAIL_STARTTLS=${CWATCH_EMAIL_STARTTLS,,}
fi
cat << EOF > /etc/msmtprc
### Automatically generated on container start. See documentation on how to set!
account default
host $CWATCH_EMAIL_HOST
port $CWATCH_EMAIL_PORT
domain $CWATCH_EMAIL_DOMAIN
from $CWATCH_EMAIL_FROM
maildomain local
tls $CWATCH_EMAIL_TLS
tls_starttls $CWATCH_EMAIL_STARTTLS
tls_certcheck off
EOF
fi
if [ $CWATCH_EMAIL_TYPE = "GMAIL" ]; then
cat << EOF > /etc/msmtprc
### Automatically generated on container start. See documentation on how to set!
account default
host smtp.gmail.com
port 587
from $CWATCH_EMAIL_FROM
user $CWATCH_EMAIL_GMAILUSER
auth on
tls on
tls_starttls on
password $CWATCH_EMAIL_GMAILPASSWORD
EOF
fi
fi
fi
fi
# Check we have a docker.sock file accessible - we cannot proceed without Docker!
if [ -S /var/run/docker.sock ]; then
if [ $1 ]; then
SendOutput S "CWATCH >> CLI argument received - checking for a valid library/image:tag argument."
if [ `echo "$1" | awk -F: '{print $2}'` ]; then
RepoImg=`echo "$1" | awk -F: '{print $1}'`
if [[ "$RepoImg" =~ "/" ]]; then
Repo=`echo "$1" | awk -F\/ '{print $1}'`
Img=`echo "$1" | awk -F\/ '{print $2}' | awk -F: '{print $1}'`
else
Repo="library"
Img=`echo "$1" | awk -F\/ '{print $1}'`
fi
Tag=`echo "$1" | awk -F: '{print $2}'`
Images+=( "$Repo/$Img" )
Tags+=( "$Tag" )
SendOutput S "CWATCH >> CLI argument VALID - restricting scope to single image $Repo/$Img:$Tag"
else
SendOutput S "CWATCH >> CLI argument INVALID - no library/image:tag combo found in the supplied argument."
Images=()
fi
IncImgCount=1
ExcImgCount=0
else
# List and count images/tags
Images=(`docker image ls | grep -v REPOSITORY | awk '{split($0,ImgArr," "); print ImgArr[1]}'`)
Tags=(`docker image ls | grep -v REPOSITORY | awk '{split($0,ImgArr," "); print ImgArr[2]}'`)
# Check for CWATCH labels
Included=(`docker ps -f "label=CWATCH.INCLUDE=TRUE" | grep -v CREATED| awk '{split($0,ImgArr," "); print ImgArr[2]}'`)
Excluded=(`docker ps -f "label=CWATCH.EXCLUDE=TRUE" | grep -v CREATED| awk '{split($0,ImgArr," "); print ImgArr[2]}'`)
IncImgCount=${#Included[@]}
ExcImgCount=${#Excluded[@]}
fi
ImgCount=${#Images[@]}
SendOutput D "CWATCH >> Found a total of $ImgCount images"
if [ $ImgCount -gt 0 ]; then
SendOutput D "CWATCH >> $IncImgCount force included - $ExcImgCount force excluded"
else
ImgCount=0
fi
# Process each Image
CheckCount=0
for (( i=0; i<${#Images[@]}; i++))
do
OkToProcess=0
# Handle errors with CLI arguments
if [ ! ${Images[$i]} ]; then
exit for
fi
CurrentImg=${Images[$i]}
# Extract image name
Image=${Images[$i]}
# Check if image has a tag
Tag=${Tags[$i]}
if [ $Tag = "<none>" ]; then
SendOutput D "CWATCH >> $Image shows no tag - assuming 'latest'"
Tag="latest"
fi
# Check we have a repository AND an image (script does not handle images sans repositories yet...
if [[ ! $Image =~ "/" ]]; then
Image="library/$Image"
fi
CombinedImgTag="$Image:$Tag"
# Disposition Image based on Behaviour
if [ $BEHAVIOUR = "ALL" ]; then
OkToProcess=1
SendOutput D "CWATCH >> Including $CombinedImgTag (INCLUDE:ALL)"
fi
if [ $BEHAVIOUR = "INCLUDE" ]; then
if [[ " ${Included[@]} " =~ " ${CombinedImgTag} " ]]; then
OkToProcess=1
SendOutput D "CWATCH >> Including $CombinedImgTag (INCLUDE:Explicit)"
else
OkToProcess=0
SendOutput D "CWATCH >> Excluding $CombinedImgTag (INCLUDE:Explicit)"
fi
fi
if [ $BEHAVIOUR = "EXCLUDE" ]; then
if [[ " ${Excluded[@]} " =~ " ${CombinedImgTag} " ]]; then
OkToProcess=0
SendOutput D "CWATCH >> Excluding $CombinedImgTag (EXCLUDE:Explicit)"
else
OkToProcess=1
SendOutput D "CWATCH >> Including $CombinedImgTag (EXCLUDE:Explicit)"
fi
fi
if [ $OkToProcess = 1 ]; then
CheckCount=`expr $CheckCount + 1`
# Nice debug output
SendOutput D "CWATCH >> Now checking $CombinedImgTag"
# Grab existing (running) image SHA256 digest
RunningRepoDigestRaw=`docker image inspect $Image:$Tag | jq -r '.[0].Id'`
RunningRepoDigest=`echo $RunningRepoDigestRaw | awk '{split($0,RepoDigestArr,":"); print RepoDigestArr[2]}'`
SendOutput D "CWATCH >> Ext Digest : $RunningRepoDigest"
# Setup OAUTH request to query Docker Hub
AUTH_SCOPE="repository:$Image:pull"
AUTH_TOKEN=$(curl -fsSL "https://$DOCKER_AUTH_SERVICE/token?service=$DOCKER_REGISTRY_SERVICE&scope=$AUTH_SCOPE" | jq --raw-output '.token')
# Pull most receent image/tag digest
LiveDigestRaw=`curl -fsSL -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -H "Authorization: Bearer $AUTH_TOKEN" "$DOCKER_REGISTRY/v2/$Image/manifests/$Tag" | jq --raw-output '.config.digest'`
LiveDigest=`echo $LiveDigestRaw | awk '{split($0,LiveDigestArr,":"); print LiveDigestArr[2]}'`
# Munge any weird HTTP header chars back out...
LiveDigestEd=`echo "$LiveDigest" | sed "s/[^[:alnum:]-]//g"`
SendOutput D "CWATCH >> Live Digest: $LiveDigestEd"
# Check if SHA256 digests match
if [[ $RunningRepoDigest = $LiveDigestEd ]]; then
# If match - nothing required
SendOutput D "CWATCH >> Disposition for $Image:$Tag - NO UPDATE NEEDED "
UpdateOk+=("$CombingImgTag")
else
# If mismatch, flag for update
SendOutput D "CWATCH >> Disposition for $Image:$Tag - UPDATE REQUIRED"
UpdateReq+=("$CombinedImgTag")
fi
fi
done
UnChecked=`expr $ImgCount - $CheckCount`
# Dump findings
SendOutput S "CWATCH >> Checked $CheckCount of $ImgCount images."
SendOutput S "CWATCH >> Found ${#UpdateReq[@]} of $CheckCount images needing an update. ${#UpdateOk[@]} up to date. $UnChecked not checked."
# Updates required
NumUpdates=${#UpdateReq[@]}
if [ "$NumUpdates" -gt 0 ]; then
SendOutput S "CWATCH >> Images needing an update :"
for i in "${UpdateReq[@]}"
do
SendOutput S " >> $i"
done
fi
else
SendOutput D "CWATCH >> /var/run/docker.sock not found - please ensure file is available/mounted for the CWATCH container."
SendOutput D "CWATCH >> Terminating."
fi
# Provide output
MDate=`date`
if [ "$CWATCH_ENABLE_EMAIL" = 1 ]; then
echo "From: $CWATCH_EMAIL_FROM" >> $TMPFile
echo "To: $CWATCH_EMAIL_FROM" >> $TMPFile
echo "Subject: CWATCH Output ($MDate) - $NumUpdates image(s) to update" >> $TMPFile
fi
for (( i=0; i<${#OUTPUT[@]}; i++))
do
# Log to stdout (for CLI)
# Log to Docker log if within a container
if [ $IsContainer = 1 ]; then
# Only write to Docker log if DEBUG has not already been written
if [ $DEBUG = 0 ]; then
echo "${OUTPUT[$i]}" >> /proc/1/fd/1
fi
fi
# Log to /var/log/cwatch either way
echo "${OUTPUT[$i]}" >> /var/log/cwatch
# Should we be emailing?
if [ $CWATCH_ENABLE_EMAIL ]; then
if [ $CWATCH_ENABLE_EMAIL = 1 ]; then
# SendOutput D "CWATCH >> Spooling email"
echo "${OUTPUT[$i]}" >> $TMPFile
fi
fi
done
EndTime=`date +%s`
TotalTime=`expr $EndTime - $StartTime`
OutDate=`date`
SendOutput S "CWATCH >> Finished. Took $TotalTime seconds. ($OutDate)"
echo "CWATCH >> Finished. Took $TotalTime seconds. ($OutDate)" >> $TMPFile
# Send email
if [ "$CWATCH_ENABLE_EMAIL" = 1 ]; then
SendOutput D "CWATCH >> Attempting to send email to $CWATCH_EMAIL_FROM"
cat $TMPFile | msmtp $CWATCH_EMAIL_FROM
fi