Skip to content

Instantly share code, notes, and snippets.

@agup006
Created December 8, 2022 17:43
Show Gist options
  • Save agup006/0b29f6d923859760ce35f1e0e8ce66b8 to your computer and use it in GitHub Desktop.
Save agup006/0b29f6d923859760ce35f1e0e8ce66b8 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
set -eu
# Configuration variables, all of these should use the INSTALL_CALYPTIA_ prefix to make it simple and clear.
# Each handles a specific option that may also then be overridden via a command line argument too.
# Optionally install the Kubeshark tool, it is disabled by default, by setting to 'no'.
# See https://github.com/kubeshark/kubeshark for more detail.
DISABLE_KUBESHARK=${INSTALL_CALYPTIA_DISABLE_KUBESHARK:-yes}
# Optionally install the Kubernetes dashboard for k3s. Disabled by default so enable by setting to 'no'.
DISABLE_KUBEDASHBOARD=${INSTALL_CALYPTIA_DISABLE_KUBEDASHBOARD:-yes}
# Optionally install the `jq` tool. Enabled by default so disable by setting to 'yes'.
DISABLE_JQ=${INSTALL_CALYPTIA_DISABLE_JQ:-no}
# The user to install Calyptia Core as, it must pre-exist.
PROVISIONED_USER=${INSTALL_CALYPTIA_PROVISIONED_USER:-$USER}
# The group to install Calyptia Core as, it must pre-exist.
PROVISIONED_GROUP=${INSTALL_CALYPTIA_PROVISIONED_GROUP:-$(id -gn)}
# The version of Calyptia Core to install.
RELEASE_VERSION=${INSTALL_CALYPTIA_RELEASE_VERSION:-0.4.6}
# Optionally just run the checks and do not install by setting to 'yes'.
DRY_RUN=${INSTALL_CALYPTIA_DRY_RUN:-no}
# The architecture to install.
ARCH=${ARCH:-$(uname -m)}
# Provide a local package to use in preference by setting this, otherwise the package will be downloaded for RELEASE_VERSION.
LOCAL_PACKAGE=${LOCAL_PACKAGE:-}
# Internal variables
IGNORE_ERRORS=no
CURL_PARAMETERS=""
# TODO: make this relocatable
CALYPTIA_CORE_DIR="/opt/calyptia"
# Output variables - set to empty if disabled
Color_Off='\033[0m'
Red='\033[0;31m'
Green='\033[0;32m'
Yellow='\033[0;33m'
function info()
{
echo -e "$Green"'[INFO] ' "$@" "$Color_Off"
}
function warn()
{
echo -e "$Yellow"'[WARN] ' "$@" "$Color_Off" >&2
}
function fatal()
{
echo -e "$Red"'[ERROR] ' "$@" "$Color_Off" >&2
exit 1
}
function error_ignorable() {
if [[ "$IGNORE_ERRORS" == "yes" ]]; then
warn "$@"
else
fatal "$@"
fi
}
function verify_system() {
if command -v curl &> /dev/null; then
info "Found curl"
else
fatal 'No curl command present'
fi
if [ -x /bin/systemctl ] || type systemctl &> /dev/null; then
info "Found systemctl"
else
fatal 'Can not find systemctl'
fi
if id "$PROVISIONED_USER" &> /dev/null; then
info "$PROVISIONED_USER user found"
else
error_ignorable "$PROVISIONED_USER user not found"
fi
if getent group "$PROVISIONED_GROUP" &> /dev/null; then
info "$PROVISIONED_GROUP group found"
else
error_ignorable "$PROVISIONED_GROUP group not found"
fi
if [[ -d "$CALYPTIA_CORE_DIR" ]]; then
error_ignorable "Found existing directory: $CALYPTIA_CORE_DIR"
fi
info "Basic system checks complete"
}
function verify_selinux() {
if command -v getenforce &> /dev/null ; then
if getenforce | grep -qi "Disabled"; then
info "SELinux disabled"
elif getenforce | grep -qi "Enforcing"; then
error_ignorable "SELinux enabled in enforcing mode"
else
warn "SELinux enabled but not in enforcing mode"
fi
else
if grep '^\s*SELINUX=enforcing' /etc/selinux/config &>/dev/null ; then
error_ignorable "SELinux enabled in enforcing mode"
else
info "SELinux disabled"
fi
fi
}
function verify_crypto() {
if command -v update-crypto-policies &> /dev/null ; then
local current_policy
current_policy=$(update-crypto-policies --show)
case $current_policy in
DEFAULT*)
info "Crypto policy set to $current_policy"
;;
LEGACY*)
info "Crypto policy set to $current_policy"
;;
*)
error_ignorable "Crypto policy set to $current_policy, may fail to download components"
;;
esac
fi
}
function verify_fips() {
if [[ ! -f /proc/sys/crypto/fips_enabled ]]; then
info "FIPS mode not enabled"
elif grep -q "1" /proc/sys/crypto/fips_enabled; then
error_ignorable "FIPS mode enabled"
else
info "FIPS mode not enabled"
fi
}
function verify_firewall() {
if command -v ufw &> /dev/null ; then
if "$SUDO" ufw status | grep -qi "inactive"; then
info "Firewall disabled"
else
error_ignorable "Firewall is enabled, this may prevent traffic without the correct rules"
fi
elif systemctl is-enabled firewalld &> /dev/null || systemctl is-enabled netfilter-persistent &> /dev/null; then
error_ignorable "Firewall is enabled, this may prevent traffic without the correct rules"
else
info "Firewall not detected"
fi
}
function verify_k3s_reqs() {
if [[ -r /etc/redhat-release ]] || [[ -r /etc/centos-release ]] || [[ -r /etc/oracle-release ]]; then
# https://docs.k3s.io/advanced#additional-preparation-for-red-hatcentos-enterprise-linux
if systemctl is-enabled nm-cloud-setup.service nm-cloud-setup.timer &> /dev/null ; then
error_ignorable "nm-cloud-setup enabled: # https://docs.k3s.io/advanced#additional-preparation-for-red-hatcentos-enterprise-linux"
else
info "RHEL-compatible OS checks complete"
fi
elif [[ -r /etc/os-release ]]; then
# https://docs.k3s.io/advanced#additional-preparation-for-debian-buster-based-distributions
if grep -q 'ID=debian' /etc/os-release && grep -q 'VERSION_CODENAME=buster' /etc/os-release; then
if [[ -x /usr/sbin/iptables ]]; then
local iptables_version
# extract the version number from e.g. 'iptables v1.8.7 (nf_tables)'
iptables_version=$(/usr/sbin/iptables --version 2>&1 | sed -n 's/^.*v\(.*\) .*/\1/p')
if dpkg --compare-versions "$iptables_version" "lt" "1.8.4" &> /dev/null ; then
error_ignorable "iptables version is too low: https://docs.k3s.io/advanced#additional-preparation-for-debian-buster-based-distributions"
else
info "iptables version acceptable: $iptables_version"
fi
fi
fi
fi
}
# The bucket set up for the aggregator is strange so requires a specific URL that exists
declare -a ALLOWED_URLS=("https://cloud-api.calyptia.com"
"https://core-packages.calyptia.com"
"https://ghcr.io/calyptia/core"
"https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64"
)
function verify_urls_reachable() {
for i in "${ALLOWED_URLS[@]}"
do
# shellcheck disable=SC2086
if curl -o /dev/null -sSfl $CURL_PARAMETERS "$i" &> /dev/null ; then
info "$i - OK"
else
error_ignorable "$i - Failed"
fi
done
}
function check_prerequisites() {
verify_system
verify_selinux
verify_crypto
verify_fips
verify_firewall
verify_k3s_reqs
verify_urls_reachable
}
function setup() {
# Handle command line arguments
# We use equals-separated arguments, i.e. --key=value, and not space separated, i.e. --key value
for i in "$@"; do
case $i in
-f|--force)
IGNORE_ERRORS=yes
shift
;;
-k|--disable-tls-verify)
CURL_PARAMETERS="$CURL_PARAMETERS --insecure"
shift
;;
--enable-kubeshark)
DISABLE_KUBESHARK=no
shift
;;
--enable-kubedashboard)
DISABLE_KUBEDASHBOARD=no
shift
;;
--disable-jq)
DISABLE_JQ=yes
shift
;;
-u=*|--user=*)
PROVISIONED_USER="${i#*=}"
shift
;;
-g=*|--group=*)
PROVISIONED_GROUP="${i#*=}"
shift
;;
--core-version=*)
RELEASE_VERSION="${i#*=}"
shift
;;
--dry-run)
DRY_RUN=yes
shift
;;
--disable-colour|--disable-color)
Color_Off=''
Red=''
Green=''
Yellow=''
shift
;;
*)
warn "Ignoring unknown option '$i', ensure to use equal-separated key-value pairs: --key=value"
shift
;;
esac
done
# use sudo if we are not already root
SUDO=sudo
if [[ $(id -u) -eq 0 ]]; then
SUDO=''
fi
case $ARCH in
amd64)
ARCH=amd64
;;
x86_64)
ARCH=amd64
;;
arm64)
ARCH=arm64
;;
aarch64)
ARCH=arm64
;;
*)
fatal "Unsupported architecture $ARCH"
esac
}
setup "$@"
info "==================================="
info " Calyptia Core Installation Script "
info "==================================="
info "This script requires superuser access to install packages."
info "You will be prompted for your password by sudo."
info "==================================="
info "Installing for: $ARCH"
info "Installing Calyptia ${RELEASE_VERSION} to: $CALYPTIA_CORE_DIR"
info "Installing as ${PROVISIONED_USER}:${PROVISIONED_GROUP}"
if [[ "$IGNORE_ERRORS" == "yes" ]]; then
warn "Ignoring any errors during preflight checks"
fi
check_prerequisites
if [[ "$DRY_RUN" == "yes" ]]; then
info "Dry run only"
info "==================================="
exit 0
fi
# Do any OS-specific stuff first
# TODO: handle upgrade
if command -v dpkg &> /dev/null ; then
if [[ -f "$LOCAL_PACKAGE" ]]; then
info "Using local package: $LOCAL_PACKAGE"
else
URL="https://storage.googleapis.com/calyptia_aggregator_bucket/packages/calyptia-core_${RELEASE_VERSION}_${ARCH}.deb"
info "Downloading $URL"
# shellcheck disable=SC2086
curl -o "/tmp/calyptia-core_${RELEASE_VERSION}_${ARCH}.deb" -sSfL $CURL_PARAMETERS "$URL"
LOCAL_PACKAGE="/tmp/calyptia-core_${RELEASE_VERSION}_${ARCH}.deb"
fi
info "Installing Debian-derived OS dependencies"
"$SUDO" dpkg --install "${LOCAL_PACKAGE}"
elif command -v rpm &> /dev/null ; then
if [[ -f "$LOCAL_PACKAGE" ]]; then
info "Using local package: $LOCAL_PACKAGE"
else
# RPMs use the other defaults
case $ARCH in
amd64)
PACKAGE_ARCH=x86_64
;;
arm64)
PACKAGE_ARCH=aarch64
;;
*)
fatal "Unknown architecture: $ARCH"
esac
URL="https://storage.googleapis.com/calyptia_aggregator_bucket/packages/calyptia-core-${RELEASE_VERSION}.${PACKAGE_ARCH}.rpm"
info "Downloading $URL"
# shellcheck disable=SC2086
curl -o "/tmp/calyptia-core-${RELEASE_VERSION}.${PACKAGE_ARCH}.rpm" -sSfL $CURL_PARAMETERS "$URL"
LOCAL_PACKAGE="/tmp/calyptia-core-${RELEASE_VERSION}.${PACKAGE_ARCH}.rpm"
fi
info "Installing RHEL-derived OS dependencies"
"$SUDO" rpm -ivvh "${LOCAL_PACKAGE}"
elif command -v apk &> /dev/null ; then
if [[ -f "$LOCAL_PACKAGE" ]]; then
info "Using local package: $LOCAL_PACKAGE"
else
URL="https://storage.googleapis.com/calyptia_aggregator_bucket/packages/calyptia-core_${RELEASE_VERSION}_${ARCH}.apk"
info "Downloading $URL"
# shellcheck disable=SC2086
curl -o "/tmp/calyptia-core_${RELEASE_VERSION}_${ARCH}.apk" -sSfL $CURL_PARAMETERS "$URL"
LOCAL_PACKAGE="/tmp/calyptia-core_${RELEASE_VERSION}_${ARCH}.apk"
fi
info "Installing APK-derived OS dependencies"
"$SUDO" apk add --allow-untrusted "${LOCAL_PACKAGE}"
else
fatal "Unsupported platform"
fi
# Ensure our various directories are correctly set up to allow users to access everything
export KUBECONFIG="$CALYPTIA_CORE_DIR"/kubeconfig
"$SUDO" mkdir -p "/home/${PROVISIONED_USER}/.kube"
"$SUDO" cp -fv "$KUBECONFIG" "/home/${PROVISIONED_USER}/.kube/config"
"$SUDO" chown -R "${PROVISIONED_USER}:${PROVISIONED_GROUP}" "/home/${PROVISIONED_USER}/.kube" "$CALYPTIA_CORE_DIR"/
"$SUDO" chmod -R a+r "$CALYPTIA_CORE_DIR"/
info "Calyptia Core installation completed: $("$CALYPTIA_CORE_DIR"/calyptia-core -v)"
info "Calyptia CLI installation completed: $(calyptia --version)"
info "K3S cluster info: $(kubectl cluster-info)"
# Optional extras now
if [[ "$DISABLE_JQ" = "no" ]]; then
# Use curl to allow us to run with ssl verify disabled
if ! command -v jq &> /dev/null; then
info "Installing jq"
# shellcheck disable=SC2086
"$SUDO" curl -o /usr/local/bin/jq -sSfL $CURL_PARAMETERS https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64
"$SUDO" chmod 755 /usr/local/bin/jq
fi
fi
if [[ "$DISABLE_KUBESHARK" = "no" ]]; then
info "Installing Kubeshark, see docs for more details: https://kubeshark.co/"
"$SUDO" sh -e <<SCRIPT
curl -sSfL $CURL_PARAMETERS -o /usr/local/bin/kubeshark https://github.com/kubeshark/kubeshark/releases/latest/download/kubeshark_linux_${ARCH}
chmod 755 /usr/local/bin/kubeshark
SCRIPT
fi
if [[ "$DISABLE_KUBEDASHBOARD" = "no" ]]; then
if ! "$SUDO" k3s kubectl cluster-info &> /dev/null; then
warn "Unable to install kube-dashboard as k3s is not running, follow docs to manually install: https://docs.k3s.io/installation"
else
info "Installing Kubedashboard, see docs for more details: https://docs.k3s.io/installation/kube-dashboard"
GITHUB_URL=https://github.com/kubernetes/dashboard/releases
# shellcheck disable=SC2086
VERSION_KUBE_DASHBOARD=$(curl -w '%{url_effective}' -I -L -s -S $CURL_PARAMETERS "${GITHUB_URL}"/latest -o /dev/null | sed -e 's|.*/||')
"$SUDO" k3s kubectl create -f https://raw.githubusercontent.com/kubernetes/dashboard/"${VERSION_KUBE_DASHBOARD}"/aio/deploy/recommended.yaml
cat << K8S_DASH_EOF | "$SUDO" k3s kubectl apply -f dashboard -
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
K8S_DASH_EOF
fi
fi
info "==================================="
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment