Created
August 8, 2018 21:45
-
-
Save donaldguy/415f94ea7a553f2c4e7db58327566450 to your computer and use it in GitHub Desktop.
Ensure an Ubuntu AKS worker pool is patched against CVE-2018-5390 aka SegmentSmack/FragmentSmack
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# This script is intended to ensure an AKS worker pool is patched against CVE-2018-5390 aka SegmentSmack/FragmentSmack | |
# by ensuring kernel is patched to version 4.15.0-1019 | |
set -eu | |
case ${1:-} in | |
-h|--help|help|/?) | |
echo "Usages:" | |
echo " To determine unpatched machines and upgrade all (after prompt):" | |
echo " $0 - will prompt for args" | |
echo " $0 [subscription_id] [aks_cluster_name]" | |
echo " $0 [subscription_id] [resource_group]/[aks_cluster_name] (if name ambiguous)" | |
echo " Attempt to upgrade a single machine (even if detection would think it done):" | |
echo " $0 [subscription_id] [aks_cluster_name] [worker node name]" | |
;; | |
*) | |
;; | |
esac | |
subscription=${1:-} | |
aks_cluster=${2:-} | |
if echo $aks_cluster | grep -q '/'; then | |
aks_cluster_resource_group=$(echo $aks_cluster | cut -d'/' -f 1) | |
aks_cluster=$(echo $aks_cluster | cut -d'/' -f 2) | |
fi | |
node_name=${3:-} | |
printf "Verifying az installed: " | |
if ! which az; then | |
echo "not found!" | |
exec >&2 | |
echo "Could not find azure-cli! Please install it per https://docs.microsoft.com/en-us/cli/azure/install-azure-cli" | |
exit 1 | |
fi | |
printf "Verifying az acs/aks module version is 2.X: " | |
if ! az --version | grep acs | grep '(2.'; then | |
echo "pre-2.x" | |
exec >&2 | |
echo "Please upgrade your azure-cli, https://docs.microsoft.com/en-us/cli/azure/install-azure-cli" | |
fi | |
if [[ -z "$subscription" ]]; then | |
subscriptions=() | |
while IFS="\n" read -r sub; do | |
subscriptions+=( "$sub" ) | |
done < <(az account list -o tsv | awk -F '\t' '{print $4}') | |
PS3="Which subscription do you want to use? " | |
select sub_name in "${subscriptions[@]}"; do | |
az account set -s "$sub_name" | |
subscription=$(az account show --query id | sed 's/"//g') | |
echo "Using subscription: $subscription" | |
echo | |
break | |
done | |
fi | |
if [[ ! -z "${aks_cluster_resource_group:-}" ]]; then | |
constrained_resource_group_args="--resource-group $aks_cluster_resource_group" | |
else | |
constrained_resource_group_args="" | |
fi | |
if [[ -z "$aks_cluster" ]]; then | |
while IFS="\n" read -r cluster; do | |
clusters+=( "$cluster" ) | |
done < <(az aks list $constrained_resource_group_args --query "[:].name | join('|', @)" | sed 's/"//g' | tr '|' "\n") | |
PS3="Which cluster do you want to use? " | |
select cluster in "${clusters[@]}"; do | |
aks_cluster=$cluster | |
echo | |
break | |
done | |
fi | |
echo "Determining resource groups: " | |
if [[ -z "${aks_cluster_resource_group:-}" ]]; then | |
aks_cluster_resource_group=$(az aks list --query "[?name == '$aks_cluster'].resourceGroup | @[0] " | sed 's/"//g') | |
fi | |
echo " managedCluster in: $aks_cluster_resource_group" | |
aks_cluster_node_resource_group=$(az aks show --resource-group $aks_cluster_resource_group --name $aks_cluster --query nodeResourceGroup | sed 's/"//g') | |
echo " worker pool in: $aks_cluster_node_resource_group" | |
kubeconfig_path="$(mktemp $TMPDIR/aks_kubeconfig_XXXXX)" | |
printf "Fetching kubeconfig: " | |
az aks get-credentials --resource-group $aks_cluster_resource_group --name $aks_cluster --file $kubeconfig_path | |
export KUBECONFIG="$kubeconfig_path" | |
printf "Checking for kubectl: " | |
if ! which kubectl; then | |
echo "Not found." | |
printf "Determining k8s version: " | |
k8s_version=$(az aks show --resource-group $aks_cluster_resource_group --name $aks_cluster --query kubernetesVersion | sed 's/"//g') | |
echo "$k8s_version" | |
echo "Installing kubectl: " | |
az aks install-cli --client-version $k8s_version | |
fi | |
function kubectl_get_kernel_versions() { | |
kubectl get node -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.nodeInfo.kernelVersion}{"\n"}{end}' | |
} | |
declare -a nodes_to_patch=() | |
if [ -z "$node_name" ]; then | |
echo | |
printf "Examining kernel versions (target is >= 4.15.0-1019): " | |
node_kernels="$(kubectl_get_kernel_versions)" | |
echo | |
( | |
printf "NAME\tKERNEL\n" | |
echo "$node_kernels" | |
) | column -t -s $'\t' | |
echo | |
echo "Determining nodes to update:" | |
while IFS="\n" read -r line; do | |
node=$(echo $line | awk '{print $1}') | |
version=$(echo $line | awk '{print $2}') | |
major=$(echo $version | awk -F '.' '{print $1}' ) | |
minor=$(echo $version | awk -F '.' '{print $2}' ) | |
patch_str=$(echo $version | awk -F '.' '{print $3}' ) | |
patch=$(echo $patch_str | awk -F '-' '{print $1}' ) | |
iteration=$(echo $patch_str | awk -F '-' '{print $2}' ) | |
if [[ $major < 5 && $minor < 16 && $patch < 1 && $iteration < 1019 ]]; then | |
nodes_to_patch+=( "$node" ) | |
fi | |
done < <(echo "$node_kernels") | |
if [[ ${#nodes_to_patch[@]} == 0 ]]; then | |
echo "Everything is up to date!" | |
exit | |
fi | |
for node in ${nodes_to_patch[@]}; do | |
echo " - $node" | |
done | |
read -p "Do you want to patch these nodes now? " -n 1 -r | |
if [[ $REPLY =~ ^[Yy]$ ]]; then | |
echo | |
else | |
echo | |
echo "Okay! exiting. You can patch each node individually by running: " | |
for node in ${nodes_to_patch[@]}; do | |
echo " $0 $subscription $aks_cluster_resource_group/$aks_cluster $node" | |
done | |
exit 0 | |
fi | |
else | |
nodes_to_patch=( "$node_name" ) | |
fi | |
function do_remote_command() { | |
RESOURCE_GROUP=$1 | |
VM_NAME=$2 | |
SCRIPT="$3" | |
az vm run-command invoke --command-id RunShellScript \ | |
--resource-group $RESOURCE_GROUP \ | |
--name $VM_NAME \ | |
--scripts "$SCRIPT" > /dev/null | |
} | |
for node in ${nodes_to_patch[@]}; do | |
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' - | |
echo "!!! Going to patch and restart $node" | |
echo "= Draining:" | |
kubectl drain --ignore-daemonsets $node | |
echo "= Patching:" | |
commands=( | |
"apt-get update" | |
"apt-get install -y linux-azure" | |
) | |
for cmd in "${commands[@]}"; do | |
echo "- $cmd:" | |
do_remote_command $aks_cluster_node_resource_group $node "$cmd" | |
echo "done" | |
done | |
echo "= Restarting: " | |
az vm restart --resource-group $aks_cluster_node_resource_group --name $node | |
printf "= Waiting for node to go NotReady: " | |
while ! kubectl get node $node --no-headers | awk '{print $2}' | grep -q "^NotReady"; do | |
printf "." | |
sleep 2 | |
done | |
echo "NotReady" | |
printf "= Waiting for node to go Ready: " | |
while ! kubectl get node $node --no-headers | awk '{print $2}' | grep -q "^Ready"; do | |
printf "." | |
sleep 2 | |
done | |
echo "Ready" | |
printf "= Removing cordon: " | |
kubectl uncordon $node | |
done | |
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' - | |
echo "Final State" | |
( | |
printf "NAME\tKERNEL\n" | |
kubectl_get_kernel_versions | |
) | column -t -s $'\t' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
which linux distro are you checking against?