diff --git a/.arg.template b/.arg.template index c72706a..32c8b9c 100644 --- a/.arg.template +++ b/.arg.template @@ -10,4 +10,15 @@ HTTPS_PROXY= HTTP_PROXY= PROXY_CERT_PATH= UPDATE_KERNEL=false -CLUSTERCONFIG=spc.tgz \ No newline at end of file +CLUSTERCONFIG=spc.tgz +CIS_HARDENING=true + +# If you have Ubuntu Pro, use the UBUNTU_PRO_KEY variable to activate it as part of the image build +# UBUNTU_PRO_KEY=your-key + +# For enabling Secure Boot with Full Disk Encryption +# IS_UKI=true +# MY_ORG="ACME Corporation" +# UKI_BRING_YOUR_OWN_KEYS=false # See sb-private-ca/howto.md for instructions on bringing your own certiticates +# INCLUDE_MS_SECUREBOOT_KEYS=true # Adds Microsoft Secure Boot certificates; if you export existing keys from a device, you typically won't need this +# AUTO_ENROLL_SECUREBOOT_KEYS=false # Set to true to automatically enroll certificates on devices in Setup Mode, useful for flashing devices without user interaction diff --git a/.earthlyignore b/.earthlyignore new file mode 100644 index 0000000..c6fb21c --- /dev/null +++ b/.earthlyignore @@ -0,0 +1,2 @@ +local/ +build/* \ No newline at end of file diff --git a/.gitignore b/.gitignore index 430bbf2..09a92a8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,11 +8,13 @@ config.yaml content-*/* *.arg .idea - .DS_Store - build/ local/ keys/ secure-boot/ -spectro-luet-auth.yaml \ No newline at end of file +sb-private-ca/*.pem +sb-private-ca/*.key +sb-private-ca/*.req +spectro-luet-auth.yaml +spc.tgz diff --git a/Dockerfile b/Dockerfile index 9e1655a..cc80b2a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,6 +23,19 @@ RUN if [ "${OS_DISTRIBUTION}" = "opensuse-leap" ] && [ "${PROXY_CERT_PATH}" != " ### To install the nginx package for Ubuntu ### +#TODO: Remove the following line. This is only for dev purpose. + +# RUN useradd -m kairos && echo "kairos:kairos" | chpasswd +# RUN adduser kairos sudo +# RUN echo '%sudo ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers + +# sbctl and mokutil are useful tools to check secure boot status, manage secure boot keys. +# RUN curl -Ls https://github.com/Foxboron/sbctl/releases/download/0.13/sbctl-0.13-linux-amd64.tar.gz | tar -xvzf - && mv sbctl/sbctl /usr/bin/sbctl +# RUN chmod +x /usr/bin/sbctl +# RUN apt-get update && apt-get install -y \ +# mokutil \ +# && apt-get clean + # RUN apt-get update && apt-get install nginx -y ### or diff --git a/Earthfile b/Earthfile index d18442b..89ecfd7 100644 --- a/Earthfile +++ b/Earthfile @@ -3,6 +3,7 @@ ARG TARGETOS ARG TARGETARCH ## Default Image Repos Used in the Builds. +ARG ALPINE_IMG=gcr.io/spectro-images-public/alpine:3.16.2 ARG SPECTRO_PUB_REPO=gcr.io/spectro-images-public ARG SPECTRO_LUET_REPO=gcr.io/spectro-dev-public ARG KAIROS_BASE_IMAGE_URL=gcr.io/spectro-images-public @@ -12,25 +13,30 @@ FROM $SPECTRO_PUB_REPO/canvos/alpine-cert:v1.0.0 ## Spectro Cloud and Kairos Tags ## ARG PE_VERSION=v4.3.2 ARG SPECTRO_LUET_VERSION=v1.2.7 -ARG KAIROS_VERSION=v2.4.5 +ARG KAIROS_VERSION=v3.0.9 ARG K3S_FLAVOR_TAG=k3s1 ARG RKE2_FLAVOR_TAG=rke2r1 -ARG OSBUILDER_VERSION=v0.7.11 -ARG OSBUILDER_IMAGE=$KAIROS_BASE_IMAGE_URL/osbuilder-tools:$OSBUILDER_VERSION -ARG K3S_PROVIDER_VERSION=v4.2.1 -ARG KUBEADM_PROVIDER_VERSION=v4.3.1 -ARG RKE2_PROVIDER_VERSION=v4.1.1 +ARG BASE_IMAGE_URL=quay.io/kairos +ARG OSBUILDER_VERSION=v0.200.11 +ARG OSBUILDER_IMAGE=quay.io/kairos/osbuilder-tools:$OSBUILDER_VERSION +ARG K3S_PROVIDER_VERSION=v4.4.0-alpha2 +ARG KUBEADM_PROVIDER_VERSION=v4.4.0-alpha1 +ARG RKE2_PROVIDER_VERSION=v4.4.0-alpha1 # Variables used in the builds. Update for ADVANCED use cases only Modify in .arg file or via CLI arguements ARG OS_DISTRIBUTION ARG OS_VERSION +ARG K8S_VERSION ARG IMAGE_REGISTRY ARG IMAGE_REPO=$OS_DISTRIBUTION +ARG ISO_NAME=installer ARG K8S_DISTRIBUTION ARG CUSTOM_TAG ARG CLUSTERCONFIG ARG ARCH ARG DISABLE_SELINUX=true +ARG CIS_HARDENING=true +ARG UBUNTU_PRO_KEY ARG FIPS_ENABLED=false ARG HTTP_PROXY @@ -43,27 +49,61 @@ ARG PROXY_CERT_PATH ARG UPDATE_KERNEL=false +# UKI Variables +ARG IS_UKI=false +ARG INCLUDE_MS_SECUREBOOT_KEYS=true +ARG AUTO_ENROLL_SECUREBOOT_KEYS=false +ARG UKI_BRING_YOUR_OWN_KEYS=false + +ARG CMDLINE="stylus.registration" +ARG BRANDING="Palette eXtended Kubernetes Edge" ARG ETCD_VERSION="v3.5.13" +# EFI size check +ARG EFI_MAX_SIZE=2048 +ARG EFI_IMG_SIZE=2200 + +# internal variables +ARG GOLANG_VERSION=1.22 + IF [ "$OS_DISTRIBUTION" = "ubuntu" ] && [ "$BASE_IMAGE" = "" ] IF [ "$OS_VERSION" == 22 ] || [ "$OS_VERSION" == 20 ] ARG BASE_IMAGE_TAG=kairos-$OS_DISTRIBUTION:$OS_VERSION.04-core-$ARCH-generic-$KAIROS_VERSION ELSE - ARG BASE_IMAGE_TAG=kairos-$OS_DISTRIBUTION:$OS_VERSION-core-$ARCH-generic-$KAIROS_VERSION + IF [ "$IS_UKI" = "true" ] + ARG BASE_IMAGE_TAG=kairos-$OS_DISTRIBUTION:$OS_VERSION-core-$ARCH-generic-$KAIROS_VERSION-uki + ELSE + ARG BASE_IMAGE_TAG=kairos-$OS_DISTRIBUTION:$OS_VERSION-core-$ARCH-generic-$KAIROS_VERSION + END END ARG BASE_IMAGE=$KAIROS_BASE_IMAGE_URL/$BASE_IMAGE_TAG ELSE IF [ "$OS_DISTRIBUTION" = "opensuse-leap" ] && [ "$BASE_IMAGE" = "" ] - ARG BASE_IMAGE_TAG=opensuse:leap-$OS_VERSION-core-$ARCH-generic-$KAIROS_VERSION + ARG BASE_IMAGE_TAG=kairos-opensuse:leap-$OS_VERSION-core-$ARCH-generic-$KAIROS_VERSION ARG BASE_IMAGE=$KAIROS_BASE_IMAGE_URL/$BASE_IMAGE_TAG ELSE IF [ "$OS_DISTRIBUTION" = "rhel" ] || [ "$OS_DISTRIBUTION" = "sles" ] # Check for default value for rhel ARG BASE_IMAGE END -IF [[ "$BASE_IMAGE" =~ "20.04-arm64-nvidia-jetson-agx-orin" ]] +IF [[ "$BASE_IMAGE" =~ "nvidia-jetson-agx-orin" ]] ARG IS_JETSON=true END +IF [ "$FIPS_ENABLED" = "true" ] + ARG STYLUS_BASE=gcr.io/spectro-images-public/stylus-framework-fips-linux-$ARCH:$PE_VERSION + ARG STYLUS_PACKAGE_BASE=gcr.io/spectro-images-public/stylus-fips-linux-$ARCH:$PE_VERSION +ELSE + ARG STYLUS_BASE=gcr.io/spectro-images-public/stylus-framework-linux-$ARCH:$PE_VERSION + ARG STYLUS_PACKAGE_BASE=gcr.io/spectro-images-public/stylus-linux-$ARCH:$PE_VERSION +END + +IF [ "$CUSTOM_TAG" != "" ] + ARG IMAGE_PATH=$IMAGE_REGISTRY/$IMAGE_REPO:$K8S_DISTRIBUTION-$K8S_VERSION-$PE_VERSION-$CUSTOM_TAG +ELSE + ARG IMAGE_PATH=$IMAGE_REGISTRY/$IMAGE_REPO:$K8S_DISTRIBUTION-$K8S_VERSION-$PE_VERSION +END +ARG CMDLINE="stylus.registration" + build-all-images: IF $FIPS_ENABLED BUILD +build-provider-images-fips @@ -71,65 +111,74 @@ build-all-images: BUILD +build-provider-images END IF [ "$ARCH" = "arm64" ] - BUILD --platform=linux/arm64 +iso-image - BUILD --platform=linux/arm64 +iso + BUILD --platform=linux/arm64 +iso-image + BUILD --platform=linux/arm64 +iso ELSE IF [ "$ARCH" = "amd64" ] - BUILD --platform=linux/amd64 +iso-image - BUILD --platform=linux/amd64 +iso + BUILD --platform=linux/amd64 +iso-image + BUILD --platform=linux/amd64 +iso END build-provider-images: - IF [ "$K8S_DISTRIBUTION" = "kubeadm" ] - BUILD +provider-image --K8S_VERSION=1.24.6 - BUILD +provider-image --K8S_VERSION=1.25.2 - BUILD +provider-image --K8S_VERSION=1.26.4 - BUILD +provider-image --K8S_VERSION=1.27.2 - BUILD +provider-image --K8S_VERSION=1.25.13 - BUILD +provider-image --K8S_VERSION=1.26.8 - BUILD +provider-image --K8S_VERSION=1.27.5 - BUILD +provider-image --K8S_VERSION=1.27.7 - BUILD +provider-image --K8S_VERSION=1.26.10 - BUILD +provider-image --K8S_VERSION=1.25.15 - BUILD +provider-image --K8S_VERSION=1.28.2 - BUILD +provider-image --K8S_VERSION=1.29.0 - BUILD +provider-image --K8S_VERSION=1.27.9 - BUILD +provider-image --K8S_VERSION=1.26.12 - BUILD +provider-image --K8S_VERSION=1.28.5 - ELSE IF [ "$K8S_DISTRIBUTION" = "rke2" ] - BUILD +provider-image --K8S_VERSION=1.26.14 - BUILD +provider-image --K8S_VERSION=1.27.11 - BUILD +provider-image --K8S_VERSION=1.28.7 - BUILD +provider-image --K8S_VERSION=1.29.3 - BUILD +provider-image --K8S_VERSION=1.24.6 - BUILD +provider-image --K8S_VERSION=1.25.2 - BUILD +provider-image --K8S_VERSION=1.26.4 - BUILD +provider-image --K8S_VERSION=1.27.2 - BUILD +provider-image --K8S_VERSION=1.25.13 - BUILD +provider-image --K8S_VERSION=1.26.8 - BUILD +provider-image --K8S_VERSION=1.27.5 - BUILD +provider-image --K8S_VERSION=1.27.7 - BUILD +provider-image --K8S_VERSION=1.26.10 - BUILD +provider-image --K8S_VERSION=1.25.15 - BUILD +provider-image --K8S_VERSION=1.28.2 - BUILD +provider-image --K8S_VERSION=1.27.9 - BUILD +provider-image --K8S_VERSION=1.26.12 - BUILD +provider-image --K8S_VERSION=1.28.5 - ELSE IF [ "$K8S_DISTRIBUTION" = "k3s" ] - BUILD +provider-image --K8S_VERSION=1.26.14 - BUILD +provider-image --K8S_VERSION=1.27.11 - BUILD +provider-image --K8S_VERSION=1.28.7 - BUILD +provider-image --K8S_VERSION=1.29.2 - BUILD +provider-image --K8S_VERSION=1.24.6 - BUILD +provider-image --K8S_VERSION=1.25.2 - BUILD +provider-image --K8S_VERSION=1.26.4 - BUILD +provider-image --K8S_VERSION=1.27.2 - BUILD +provider-image --K8S_VERSION=1.25.13 - BUILD +provider-image --K8S_VERSION=1.26.8 - BUILD +provider-image --K8S_VERSION=1.27.5 - BUILD +provider-image --K8S_VERSION=1.27.7 - BUILD +provider-image --K8S_VERSION=1.26.10 - BUILD +provider-image --K8S_VERSION=1.25.15 - BUILD +provider-image --K8S_VERSION=1.28.2 + IF [ "$IS_UKI" = "true" ] + ARG TARGET=uki-provider-image + ELSE + ARG TARGET=provider-image + END + IF [ "$K8S_VERSION" = "" ] + IF [ "$K8S_DISTRIBUTION" = "kubeadm" ] + BUILD +$TARGET --K8S_VERSION=1.24.6 + BUILD +$TARGET --K8S_VERSION=1.25.2 + BUILD +$TARGET --K8S_VERSION=1.26.4 + BUILD +$TARGET --K8S_VERSION=1.27.2 + BUILD +$TARGET --K8S_VERSION=1.25.13 + BUILD +$TARGET --K8S_VERSION=1.26.8 + BUILD +$TARGET --K8S_VERSION=1.27.5 + BUILD +$TARGET --K8S_VERSION=1.27.7 + BUILD +$TARGET --K8S_VERSION=1.26.10 + BUILD +$TARGET --K8S_VERSION=1.25.15 + BUILD +$TARGET --K8S_VERSION=1.28.2 + BUILD +$TARGET --K8S_VERSION=1.29.0 + BUILD +$TARGET --K8S_VERSION=1.27.9 + BUILD +$TARGET --K8S_VERSION=1.26.12 + BUILD +$TARGET --K8S_VERSION=1.28.5 + ELSE IF [ "$K8S_DISTRIBUTION" = "rke2" ] + BUILD +$TARGET --K8S_VERSION=1.26.14 + BUILD +$TARGET --K8S_VERSION=1.27.11 + BUILD +$TARGET --K8S_VERSION=1.28.7 + BUILD +$TARGET --K8S_VERSION=1.29.3 + BUILD +$TARGET --K8S_VERSION=1.24.6 + BUILD +$TARGET --K8S_VERSION=1.25.2 + BUILD +$TARGET --K8S_VERSION=1.26.4 + BUILD +$TARGET --K8S_VERSION=1.27.2 + BUILD +$TARGET --K8S_VERSION=1.25.13 + BUILD +$TARGET --K8S_VERSION=1.26.8 + BUILD +$TARGET --K8S_VERSION=1.27.5 + BUILD +$TARGET --K8S_VERSION=1.27.7 + BUILD +$TARGET --K8S_VERSION=1.26.10 + BUILD +$TARGET --K8S_VERSION=1.25.15 + BUILD +$TARGET --K8S_VERSION=1.28.2 + BUILD +$TARGET --K8S_VERSION=1.27.9 + BUILD +$TARGET --K8S_VERSION=1.26.12 + BUILD +$TARGET --K8S_VERSION=1.28.5 + ELSE IF [ "$K8S_DISTRIBUTION" = "k3s" ] + BUILD +$TARGET --K8S_VERSION=1.26.14 + BUILD +$TARGET --K8S_VERSION=1.27.11 + BUILD +$TARGET --K8S_VERSION=1.28.7 + BUILD +$TARGET --K8S_VERSION=1.29.2 + BUILD +$TARGET --K8S_VERSION=1.24.6 + BUILD +$TARGET --K8S_VERSION=1.25.2 + BUILD +$TARGET --K8S_VERSION=1.26.4 + BUILD +$TARGET --K8S_VERSION=1.27.2 + BUILD +$TARGET --K8S_VERSION=1.25.13 + BUILD +$TARGET --K8S_VERSION=1.26.8 + BUILD +$TARGET --K8S_VERSION=1.27.5 + BUILD +$TARGET --K8S_VERSION=1.27.7 + BUILD +$TARGET --K8S_VERSION=1.26.10 + BUILD +$TARGET --K8S_VERSION=1.25.15 + BUILD +$TARGET --K8S_VERSION=1.28.2 + END + ELSE + BUILD +$TARGET --K8S_VERSION="$K8S_VERSION" END build-provider-images-fips: @@ -189,26 +238,141 @@ iso-image-rootfs: FROM --platform=linux/${ARCH} +iso-image SAVE ARTIFACT --keep-own /. rootfs -iso: - ARG ISO_NAME=installer +uki-iso: WORKDIR /build - COPY --platform=linux/${ARCH} (+build-iso/ --ISO_NAME=$ISO_NAME) . + COPY --platform=linux/${ARCH} +build-uki-iso/ . SAVE ARTIFACT /build/* AS LOCAL ./build/ -build-iso: - ARG ISO_NAME +uki-provider-image: + FROM --platform=linux/${ARCH} +ubuntu-systemd + WORKDIR / + COPY +luet/luet /usr/bin/luet + COPY +kairos-agent/kairos-agent /usr/bin/kairos-agent + COPY --platform=linux/${ARCH} +trust-boot-unpack/ /trusted-boot + COPY --platform=linux/${ARCH} +install-k8s/ /k8s + SAVE IMAGE --push $IMAGE_PATH + +trust-boot-unpack: + COPY +luet/luet /usr/bin/luet + COPY --platform=linux/${ARCH} +build-provider-trustedboot-image/ /image + RUN FILE="file:/$(find /image -type f -name "*.tar" | head -n 1)" && \ + luet util unpack $FILE /trusted-boot + SAVE ARTIFACT /trusted-boot/* + +stylus-image-pack: + COPY +luet/luet /usr/bin/luet + COPY --platform=linux/${ARCH} +stylus-package-image/ /stylus + RUN cd stylus && tar -czf ../stylus.tar * + RUN luet util pack $STYLUS_BASE stylus.tar stylus-image.tar + SAVE ARTIFACT stylus-image.tar AS LOCAL ./build/ + +luet: + FROM --platform=linux/${ARCH} quay.io/luet/base:latest + SAVE ARTIFACT /usr/bin/luet /luet +kairos-agent: + FROM --platform=linux/${ARCH} $BASE_IMAGE + SAVE ARTIFACT /usr/bin/kairos-agent /kairos-agent + +install-k8s: + FROM --platform=linux/${ARCH} alpine:3.19 + COPY +luet/luet /usr/bin/luet + + IF [ "$K8S_DISTRIBUTION" = "kubeadm" ] || [ "$K8S_DISTRIBUTION" = "kubeadm-fips" ] + ARG BASE_K8S_VERSION=$K8S_VERSION + ELSE IF [ "$K8S_DISTRIBUTION" = "k3s" ] + ARG K8S_DISTRIBUTION_TAG=$K3S_FLAVOR_TAG + ARG BASE_K8S_VERSION=$K8S_VERSION-$K8S_DISTRIBUTION_TAG + ELSE IF [ "$K8S_DISTRIBUTION" = "rke2" ] + ARG K8S_DISTRIBUTION_TAG=$RKE2_FLAVOR_TAG + ARG BASE_K8S_VERSION=$K8S_VERSION-$K8S_DISTRIBUTION_TAG + END + + WORKDIR /output + RUN mkdir -p /etc/luet/repos.conf.d && \ + luet repo add spectro --type docker --url gcr.io/spectro-dev-public/luet-repo --priority 1 -y && \ + luet repo update + IF [ "$K8S_DISTRIBUTION" = "kubeadm" ] + RUN luet install -y container-runtime/containerd --system-target /output + END + + IF [ "$K8S_DISTRIBUTION" = "kubeadm-fips" ] + RUN luet install -y container-runtime/containerd-fips --system-target /output + END + RUN luet install -y k8s/$K8S_DISTRIBUTION@$BASE_K8S_VERSION --system-target /output && luet cleanup + RUN rm -rf /output/var/cache/* + SAVE ARTIFACT /output/* + +build-uki-iso: FROM --platform=linux/${ARCH} $OSBUILDER_IMAGE ENV ISO_NAME=${ISO_NAME} COPY overlay/files-iso/ /overlay/ - COPY --if-exists user-data /overlay/files-iso/config.yaml + COPY --if-exists user-data /overlay/config.yaml + COPY --platform=linux/${ARCH} +stylus-image-pack/stylus-image.tar /overlay/stylus-image.tar + COPY --platform=linux/${ARCH} +luet/luet /overlay/luet + COPY --if-exists content-*/*.zst /overlay/opt/spectrocloud/content/ + RUN if [ -n "$(ls /overlay/opt/spectrocloud/content/*.zst 2>/dev/null)" ]; then \ + for file in /overlay/opt/spectrocloud/content/*.zst; do \ + split --bytes=3GB --numeric-suffixes "$file" /overlay/opt/spectrocloud/content/$(basename "$file")_part; \ + done; \ + rm -f /overlay/opt/spectrocloud/content/*.zst; \ + fi + #check if clusterconfig is passed in IF [ "$CLUSTERCONFIG" != "" ] COPY --if-exists "$CLUSTERCONFIG" /overlay/opt/spectrocloud/clusterconfig/spc.tgz END + COPY --if-exists ui.tar /overlay/opt/spectrocloud/emc/ + RUN if [ -f /overlay/opt/spectrocloud/emc/ui.tar ]; then \ + tar -xf /overlay/opt/spectrocloud/emc/ui.tar -C /overlay/opt/spectrocloud/emc && \ + rm -f /overlay/opt/spectrocloud/emc/ui.tar; \ + fi + WORKDIR /build + COPY --platform=linux/${ARCH} --keep-own +iso-image-rootfs/rootfs /build/image + IF [ "$ARCH" = "arm64" ] + RUN /entrypoint.sh --name $ISO_NAME build-iso --date=false --overlay-iso /overlay dir:/build/image --debug --output /iso/ --arch $ARCH + ELSE IF [ "$ARCH" = "amd64" ] + COPY secure-boot/enrollment/ secure-boot/private-keys/ secure-boot/public-keys/ /keys + RUN ls -liah /keys + RUN mkdir /iso + IF [ "$AUTO_ENROLL_SECUREBOOT_KEYS" = "true" ] + RUN enki --config-dir /config build-uki dir:/build/image --extend-cmdline "$CMDLINE" --overlay-iso /overlay --secure-boot-enroll force -t iso -d /iso -k /keys --boot-branding "$BRANDING" + ELSE + RUN enki --config-dir /config build-uki dir:/build/image --extend-cmdline "$CMDLINE" --overlay-iso /overlay -t iso -d /iso -k /keys --boot-branding "$BRANDING" + END + END + WORKDIR /iso + RUN mv /iso/*.iso $ISO_NAME.iso + SAVE ARTIFACT /iso/* + +iso: + WORKDIR /build + IF [ "$IS_UKI" = "true" ] + COPY --platform=linux/${ARCH} +build-uki-iso/ . + ELSE + COPY --platform=linux/${ARCH} +build-iso/ . + END + SAVE ARTIFACT /build/* AS LOCAL ./build/ + +build-iso: + FROM --platform=linux/${ARCH} $OSBUILDER_IMAGE + ENV ISO_NAME=${ISO_NAME} + COPY overlay/files-iso/ /overlay/ + COPY --if-exists user-data /overlay/files-iso/config.yaml + COPY --if-exists content-*/*.zst /overlay/opt/spectrocloud/content/ + RUN if [ -n "$(ls /overlay/opt/spectrocloud/content/*.zst 2>/dev/null)" ]; then \ + for file in /overlay/opt/spectrocloud/content/*.zst; do \ + split --bytes=3GB --numeric-suffixes "$file" /overlay/opt/spectrocloud/content/$(basename "$file")_part; \ + done; \ + rm -f /overlay/opt/spectrocloud/content/*.zst; \ + fi + #check if clusterconfig is passed in + IF [ "$CLUSTERCONFIG" != "" ] + COPY --if-exists "$CLUSTERCONFIG" /overlay/opt/spectrocloud/clusterconfig/spc.tgz + END WORKDIR /build COPY --platform=linux/${ARCH} --keep-own +iso-image-rootfs/rootfs /build/image @@ -228,20 +392,122 @@ build-iso: RUN sha256sum $ISO_NAME.iso > $ISO_NAME.iso.sha256 SAVE ARTIFACT /iso/* +### UKI targets +## Generate UKI keys +# Default Expiry 15 years +## earthly +uki-genkey --MY_ORG="ACME Corp" --EXPIRATION_IN_DAYS=5475 +uki-genkey: + ARG MY_ORG="ACME Corp" + ARG EXPIRATION_IN_DAYS=5475 + FROM --platform=linux/${ARCH} $OSBUILDER_IMAGE + + IF [ "$UKI_BRING_YOUR_OWN_KEYS" = "false" ] + RUN --no-cache mkdir -p /custom-keys + COPY --if-exists secure-boot/exported-keys/ /custom-keys + IF [ "$INCLUDE_MS_SECUREBOOT_KEYS" = "false" ] + RUN --no-cache if [[ -f /custom-keys/KEK && -f /custom-keys/db ]]; then \ + echo "Generating Secure Boot keys, including exported UEFI keys..." && \ + /entrypoint.sh genkey "$MY_ORG" --custom-cert-dir /custom-keys --skip-microsoft-certs-I-KNOW-WHAT-IM-DOING --expiration-in-days $EXPIRATION_IN_DAYS -o /keys; else \ + echo "Generating Secure Boot keys..." && \ + /entrypoint.sh genkey "$MY_ORG" --skip-microsoft-certs-I-KNOW-WHAT-IM-DOING --expiration-in-days $EXPIRATION_IN_DAYS -o /keys; fi + ELSE + RUN --no-cache if [[ -f /custom-keys/KEK && -f /custom-keys/db ]]; then \ + echo "Generating Secure Boot keys, including exported UEFI keys and Microsoft keys..." && \ + /entrypoint.sh genkey "$MY_ORG" --custom-cert-dir /custom-keys --expiration-in-days $EXPIRATION_IN_DAYS -o /keys; else \ + echo "Generating Secure Boot keys, including Microsoft keys..." && \ + /entrypoint.sh genkey "$MY_ORG" --expiration-in-days $EXPIRATION_IN_DAYS -o /keys; fi + END + RUN --no-cache mkdir -p /private-keys + RUN --no-cache mkdir -p /public-keys + RUN --no-cache cd /keys; mv *.key tpm2-pcr-private.pem /private-keys + RUN --no-cache cd /keys; mv *.pem /public-keys + ELSE + COPY +uki-byok/ /keys + END + + SAVE ARTIFACT --if-exists /keys AS LOCAL ./secure-boot/enrollment + IF [ "$UKI_BRING_YOUR_OWN_KEYS" = "false" ] + SAVE ARTIFACT --if-exists /private-keys AS LOCAL ./secure-boot/private-keys + SAVE ARTIFACT --if-exists /public-keys AS LOCAL ./secure-boot/public-keys + END + +download-sbctl: + DO +BASE_ALPINE + RUN curl -Ls https://github.com/Foxboron/sbctl/releases/download/0.13/sbctl-0.13-linux-amd64.tar.gz | tar -xvzf - && mv sbctl/sbctl /usr/bin/sbctl + SAVE ARTIFACT /usr/bin/sbctl + +uki-byok: + FROM +ubuntu-systemd + + RUN apt-get update && apt-get install -y efitools curl + COPY +download-sbctl/sbctl /usr/bin/sbctl + COPY --if-exists secure-boot/exported-keys/ /exported-keys + COPY secure-boot/private-keys/ secure-boot/public-keys /keys + WORKDIR /keys + RUN sbctl import-keys \ + --pk-key /keys/PK.key \ + --pk-cert /keys/PK.pem \ + --kek-key /keys/KEK.key \ + --kek-cert /keys/KEK.pem \ + --db-key /keys/db.key \ + --db-cert /keys/db.pem + RUN sbctl create-keys + IF [ "$INCLUDE_MS_SECUREBOOT_KEYS" = "false" ] + RUN sbctl enroll-keys --export esl --yes-this-might-brick-my-machine + ELSE + RUN sbctl enroll-keys --export esl --yes-this-might-brick-my-machine --microsoft + END + RUN mkdir -p /output + RUN cp PK.esl /output/PK.esl 2>/dev/null + RUN cp KEK.esl /output/KEK.esl 2>/dev/null + RUN cp db.esl /output/db.esl 2>/dev/null + RUN if [ -f dbx.esl ]; then cp dbx.esl /output/dbx.esl; else touch /output/dbx.esl; fi + RUN [ -f /exported-keys/KEK ] && cat /exported-keys/KEK >> /output/KEK.esl || true + RUN [ -f /exported-keys/db ] && cat /exported-keys/db >> /output/db.esl || true + RUN [ -f /exported-keys/dbx ] && cat /exported-keys/dbx >> /output/dbx.esl || true + + WORKDIR /output + RUN sign-efi-sig-list -c /keys/PK.pem -k /keys/PK.key PK PK.esl PK.auth + RUN sign-efi-sig-list -c /keys/PK.pem -k /keys/PK.key KEK KEK.esl KEK.auth + RUN sign-efi-sig-list -c /keys/KEK.pem -k /keys/KEK.key db db.esl db.auth + RUN sign-efi-sig-list -c /keys/KEK.pem -k /keys/KEK.key dbx dbx.esl dbx.auth + + RUN sig-list-to-certs 'PK.esl' 'PK' + RUN sig-list-to-certs 'KEK.esl' 'KEK' + RUN sig-list-to-certs 'db.esl' 'db' + + RUN cp PK-0.der PK.der 2>/dev/null + RUN cp KEK-0.der KEK.der 2>/dev/null + RUN cp db-0.der db.der 2>/dev/null + + SAVE ARTIFACT /output/* + +secure-boot-dirs: + FROM --platform=linux/${ARCH} ubuntu:latest + RUN mkdir -p --mode=0644 /secure-boot/enrollment + RUN mkdir -p --mode=0600 /secure-boot/exported-keys + RUN mkdir -p --mode=0600 /secure-boot/private-keys + RUN mkdir -p --mode=0644 /secure-boot/public-keys + COPY --if-exists --keep-ts secure-boot/enrollment/ /secure-boot/enrollment + COPY --if-exists --keep-ts secure-boot/exported-keys/ /secure-boot/exported-keys + COPY --if-exists --keep-ts secure-boot/private-keys/ /secure-boot/private-keys + COPY --if-exists --keep-ts secure-boot/public-keys/ /secure-boot/public-keys + RUN chmod 0600 /secure-boot/exported-keys + RUN chmod 0600 /secure-boot/private-keys + RUN chmod 0644 /secure-boot/public-keys + SAVE ARTIFACT --keep-ts /secure-boot AS LOCAL ./secure-boot + # Used to create the provider images. The --K8S_VERSION will be passed in the earthly build -provider-image: +provider-image: FROM --platform=linux/${ARCH} +base-image # added PROVIDER_K8S_VERSION to fix missing image in ghcr.io/kairos-io/provider-* - ARG K8S_VERSION=1.26.4 ARG IMAGE_REPO - IF [ "$CUSTOM_TAG" != "" ] - ARG IMAGE_PATH=$IMAGE_REGISTRY/$IMAGE_REPO:$K8S_DISTRIBUTION-$K8S_VERSION-$PE_VERSION-$CUSTOM_TAG - ELSE - ARG IMAGE_PATH=$IMAGE_REGISTRY/$IMAGE_REPO:$K8S_DISTRIBUTION-$K8S_VERSION-$PE_VERSION - END IF [ "$K8S_DISTRIBUTION" = "kubeadm" ] || [ "$K8S_DISTRIBUTION" = "kubeadm-fips" ] ARG BASE_K8S_VERSION=$K8S_VERSION + IF [ "$OS_DISTRIBUTION" = "ubuntu" ] && [ "$ARCH" = "amd64" ] + RUN kernel=$(ls /lib/modules | tail -n1) && if ! ls /usr/src | grep linux-headers-$kernel; then apt-get update && apt-get install -y "linux-headers-${kernel}"; fi + END ELSE IF [ "$K8S_DISTRIBUTION" = "k3s" ] ARG K8S_DISTRIBUTION_TAG=$K3S_FLAVOR_TAG ARG BASE_K8S_VERSION=$K8S_VERSION-$K8S_DISTRIBUTION_TAG @@ -254,15 +520,18 @@ provider-image: COPY +stylus-image/etc/kairos/branding /etc/kairos/branding COPY +stylus-image/oem/stylus_config.yaml /etc/kairos/branding/stylus_config.yaml COPY +stylus-image/etc/elemental/config.yaml /etc/elemental/config.yaml - IF [ "$K8S_DISTRIBUTION" = "kubeadm" ] - RUN luet install -y container-runtime/containerd - END - IF [ "$K8S_DISTRIBUTION" = "kubeadm-fips" ] - RUN luet install -y container-runtime/containerd-fips + IF [ "$IS_UKI" = "true" ] + COPY +internal-slink/slink /usr/bin/slink + COPY +install-k8s/ /k8s + RUN slink --source /k8s/ --target /opt/k8s + RUN rm -f /usr/bin/slink + RUN rm -rf /k8s + RUN ln -sf /opt/spectrocloud/bin/agent-provider-stylus /usr/local/bin/agent-provider-stylus + ELSE + COPY +install-k8s/ / END - RUN luet install -y k8s/$K8S_DISTRIBUTION@$BASE_K8S_VERSION && luet cleanup RUN rm -f /etc/ssh/ssh_host_* /etc/ssh/moduli COPY (+download-etcdctl/etcdctl) /usr/bin/ @@ -272,17 +541,28 @@ provider-image: SAVE IMAGE --push $IMAGE_PATH + +provider-image-rootfs: + FROM --platform=linux/${ARCH} +provider-image + SAVE ARTIFACT --keep-own /. rootfs + +build-provider-trustedboot-image: + FROM --platform=linux/${ARCH} $OSBUILDER_IMAGE + COPY --platform=linux/${ARCH} --keep-own +provider-image-rootfs/rootfs /build/image + COPY secure-boot/enrollment/ secure-boot/private-keys/ secure-boot/public-keys/ /keys + RUN /entrypoint.sh build-uki dir:/build/image -t container -d /output -k /keys --boot-branding "Palette eXtended Kubernetes Edge" + SAVE ARTIFACT /output/* AS LOCAL ./trusted-boot/ + stylus-image: - IF [ "$FIPS_ENABLED" = "true" ] - ARG STYLUS_BASE=$SPECTRO_PUB_REPO/stylus-framework-fips-linux-$ARCH:$PE_VERSION - ELSE - ARG STYLUS_BASE=$SPECTRO_PUB_REPO/stylus-framework-linux-$ARCH:$PE_VERSION - END - FROM $STYLUS_BASE - SAVE ARTIFACT ./* - SAVE ARTIFACT /etc/kairos/branding - SAVE ARTIFACT /etc/elemental/config.yaml - SAVE ARTIFACT /oem/stylus_config.yaml + FROM --platform=linux/${ARCH} $STYLUS_BASE + SAVE ARTIFACT --keep-own ./* + # SAVE ARTIFACT /etc/kairos/branding + # SAVE ARTIFACT /etc/elemental/config.yaml + # SAVE ARTIFACT /oem/stylus_config.yaml + +stylus-package-image: + FROM --platform=linux/${ARCH} $STYLUS_PACKAGE_BASE + SAVE ARTIFACT --keep-own ./* kairos-provider-image: IF [ "$K8S_DISTRIBUTION" = "kubeadm" ] @@ -302,45 +582,88 @@ kairos-provider-image: # base build image used to create the base image for all other image types base-image: FROM DOCKERFILE --build-arg BASE=$BASE_IMAGE --build-arg PROXY_CERT_PATH=$PROXY_CERT_PATH \ - --build-arg OS_DISTRIBUTION=$OS_DISTRIBUTION --build-arg HTTP_PROXY=$HTTP_PROXY --build-arg HTTPS_PROXY=$HTTPS_PROXY \ + --build-arg OS_DISTRIBUTION=$OS_DISTRIBUTION --build-arg OS_VERSION=$OS_VERSION \ + --build-arg HTTP_PROXY=$HTTP_PROXY --build-arg HTTPS_PROXY=$HTTPS_PROXY \ --build-arg NO_PROXY=$NO_PROXY . IF [ "$IS_JETSON" = "true" ] - COPY mount.yaml /system/oem/mount.yaml + COPY cloudconfigs/mount.yaml /system/oem/mount.yaml + END + + IF [ "$IS_UKI" = "true" ] + COPY cloudconfigs/80_stylus_uki.yaml /system/oem/80_stylus_uki.yaml + END + + IF [ "$ARCH" = "arm64" ] + RUN mkdir -p /etc/luet/repos.conf.d && \ + SPECTRO_LUET_VERSION=$SPECTRO_LUET_VERSION luet repo add spectro --type docker --url gcr.io/spectro-dev-public/luet-repo-arm --priority 1 -y && \ + luet repo update + ELSE IF [ "$ARCH" = "amd64" ] + RUN mkdir -p /etc/luet/repos.conf.d && \ + SPECTRO_LUET_VERSION=$SPECTRO_LUET_VERSION luet repo add spectro --type docker --url gcr.io/spectro-dev-public/luet-repo --priority 1 -y && \ + luet repo update + END + + IF [ "$K8S_DISTRIBUTION" = "kubeadm" ] || [ "$K8S_DISTRIBUTION" = "kubeadm-fips" ] + ARG BASE_K8S_VERSION=$K8S_VERSION + ELSE IF [ "$K8S_DISTRIBUTION" = "k3s" ] + ARG K8S_DISTRIBUTION_TAG=$K3S_FLAVOR_TAG + ARG BASE_K8S_VERSION=$K8S_VERSION-$K8S_DISTRIBUTION_TAG + ELSE IF [ "$K8S_DISTRIBUTION" = "rke2" ] + ARG K8S_DISTRIBUTION_TAG=$RKE2_FLAVOR_TAG + ARG BASE_K8S_VERSION=$K8S_VERSION-$K8S_DISTRIBUTION_TAG END IF [ "$OS_DISTRIBUTION" = "ubuntu" ] && [ "$ARCH" = "amd64" ] + IF [ ! -z "$UBUNTU_PRO_KEY" ] + RUN sed -i '/^[[:space:]]*$/d' /etc/os-release && \ + apt update && apt-get install -y snapd && \ + pro attach $UBUNTU_PRO_KEY + END + # Add proxy certificate if present IF [ ! -z $PROXY_CERT_PATH ] COPY sc.crt /etc/ssl/certs RUN update-ca-certificates END - RUN apt update && \ - apt install --no-install-recommends zstd vim iputils-ping bridge-utils curl tcpdump ethtool -y + RUN apt-get update && \ + apt-get install --no-install-recommends kbd zstd vim iputils-ping bridge-utils curl tcpdump ethtool -y + IF [ "$UPDATE_KERNEL" = "false" ] - RUN if dpkg -l linux-image-generic-hwe-20.04 > /dev/null; then apt-mark hold linux-image-generic-hwe-20.04; fi && \ - if dpkg -l linux-image-generic-hwe-22.04 > /dev/null; then apt-mark hold linux-image-generic-hwe-22.04; fi && \ + RUN if dpkg -l "linux-image-generic-hwe-$OS_VERSION" > /dev/null; then apt-mark hold "linux-image-generic-hwe-$OS_VERSION" "linux-headers-generic-hwe-$OS_VERSION" "linux-generic-hwe-$OS_VERSION" ; fi && \ if dpkg -l linux-image-generic > /dev/null; then apt-mark hold linux-image-generic linux-headers-generic linux-generic; fi END - RUN apt update && \ - apt upgrade --no-install-recommends -y - RUN kernel=$(ls /boot/vmlinuz-* | tail -n1) && \ - ln -sf "${kernel#/boot/}" /boot/vmlinuz - RUN kernel=$(ls /lib/modules | tail -n1) && \ - dracut -f "/boot/initrd-${kernel}" "${kernel}" && \ - ln -sf "initrd-${kernel}" /boot/initrd - RUN kernel=$(ls /lib/modules | tail -n1) && \ - depmod -a "${kernel}" - - RUN if [ ! -f /usr/bin/grub2-editenv ]; then \ - ln -s /usr/sbin/grub-editenv /usr/bin/grub2-editenv; \ - fi - - RUN rm -rf /var/cache/* && \ - apt clean - - # IF OS Type is Opensuse + + IF [ "$IS_UKI" = "false" ] + RUN apt-get update && \ + apt-get upgrade -y + RUN kernel=$(ls /boot/vmlinuz-* | tail -n1) && \ + ln -sf "${kernel#/boot/}" /boot/vmlinuz + RUN kernel=$(ls /lib/modules | tail -n1) && \ + dracut -f "/boot/initrd-${kernel}" "${kernel}" && \ + ln -sf "initrd-${kernel}" /boot/initrd + RUN kernel=$(ls /lib/modules | tail -n1) && \ + depmod -a "${kernel}" + + RUN if [ ! -f /usr/bin/grub2-editenv ]; then \ + ln -s /usr/sbin/grub-editenv /usr/bin/grub2-editenv; \ + fi + + RUN rm -rf /var/cache/* && \ + apt-get clean + END + + IF [ "$CIS_HARDENING" = "true" ] + COPY cis-harden/harden.sh /tmp/harden.sh + RUN /tmp/harden.sh && rm /tmp/harden.sh + END + + IF [ ! -z "$UBUNTU_PRO_KEY" ] + RUN pro detach --assume-yes + END + + # IF OS Type is Opensuse ELSE IF [ "$OS_DISTRIBUTION" = "opensuse-leap" ] && [ "$ARCH" = "amd64" ] # Add proxy certificate if present IF [ ! -z $PROXY_CERT_PATH ] @@ -409,7 +732,13 @@ base-image: # Used to build the installer image. The installer ISO will be created from this. iso-image: FROM --platform=linux/${ARCH} +base-image - COPY --platform=linux/${ARCH} +stylus-image/ / + IF [ "$IS_UKI" = "false" ] + COPY --platform=linux/${ARCH} +stylus-image/ / + ELSE + COPY --platform=linux/${ARCH} +stylus-image/ / + RUN find /opt/spectrocloud/bin/. ! -name 'agent-provider-stylus' -type f -exec rm -f {} + + RUN rm -f /usr/bin/luet + END COPY overlay/files/ / RUN rm -f /etc/ssh/ssh_host_* /etc/ssh/moduli @@ -421,6 +750,97 @@ iso-image: SAVE IMAGE palette-installer-image:$PE_VERSION END +iso-disk-image: + FROM scratch + + COPY +iso/*.iso /disk/ + IF [ "$CUSTOM_TAG" != "" ] + SAVE IMAGE --push $IMAGE_REGISTRY/$IMAGE_REPO/$ISO_NAME:$PE_VERSION-$CUSTOM_TAG + ELSE + SAVE IMAGE --push $IMAGE_REGISTRY/$IMAGE_REPO/$ISO_NAME:$PE_VERSION + END + +go-deps: + FROM $SPECTRO_PUB_REPO/golang:${GOLANG_VERSION}-alpine + RUN apk add libc-dev binutils-gold clang + + +BUILD_GOLANG: + COMMAND + ARG WORKDIR=/build + WORKDIR $WORKDIR + + ARG BIN + ARG SRC + ARG GOOS + ARG GOARCH + ARG VERSION=dev + + ENV GOOS=$GOOS + ENV GOARCH=$GOARCH + ENV GO_LDFLAGS=" -X github.com/spectrocloud/stylus/pkg/version.Version=${VERSION} -w -s" + + ENV CC=clang + RUN go mod download + RUN go-build-static.sh -a -o ${BIN} ./${SRC} + + SAVE ARTIFACT ${BIN} ${BIN} AS LOCAL build/${BIN} + +internal-slink: + FROM +go-deps + + WORKDIR /build + COPY internal internal + + ARG BUILD_DIR=/build/internal + WORKDIR $BUILD_DIR + + DO +BUILD_GOLANG --BIN=slink --SRC=cmd/slink/slink.go --WORKDIR=$BUILD_DIR + + SAVE ARTIFACT slink + +rust-deps: + FROM rust:1.78-bookworm + RUN apt-get update -qq + RUN apt-get install --no-install-recommends -qq autoconf autotools-dev libtool-bin clang cmake bsdmainutils + +build-efi-size-check: + FROM +rust-deps + + WORKDIR /build + COPY --keep-ts efi-size-check efi-size-check + + WORKDIR /build/efi-size-check + RUN cargo build --target x86_64-unknown-uefi + + SAVE ARTIFACT target/x86_64-unknown-uefi/debug/efi-size-check.efi + +iso-efi-size-check: + FROM +ubuntu-systemd + + RUN apt-get update + RUN apt-get install -y mtools xorriso + + WORKDIR /build + + COPY +build-efi-size-check/efi-size-check.efi /build/efi-size-check.efi + RUN mkdir -p esp + RUN dd if=/dev/urandom of=esp/ABC bs=1M count=$EFI_MAX_SIZE + RUN dd if=/dev/zero of=fat.img bs=1M count=$EFI_IMG_SIZE + RUN mformat -i fat.img -F :: + RUN mmd -i fat.img ::/EFI + RUN mmd -i fat.img ::/EFI/BOOT + RUN mcopy -i fat.img efi-size-check.efi ::/EFI/BOOT/BOOTX64.EFI + RUN mcopy -i fat.img esp/ABC :: + RUN mkdir -p iso + RUN cp fat.img iso + RUN xorriso -as mkisofs -e fat.img -no-emul-boot -o efi-size-check.iso iso + + SAVE ARTIFACT efi-size-check.iso AS LOCAL ./build/ + +ubuntu-systemd: + FROM $SPECTRO_PUB_REPO/ubuntu-systemd:22.04 + OS_RELEASE: COMMAND ARG OS_ID=${OS_DISTRIBUTION} @@ -433,7 +853,8 @@ OS_RELEASE: ARG OS_REPO=spectrocloud/CanvOS ARG OS_NAME=kairos-core-${OS_DISTRIBUTION} ARG ARTIFACT=kairos-core-${OS_DISTRIBUTION}-$OS_VERSION + ARG KAIROS_RELEASE=${OS_VERSION} # update OS-release file - RUN sed -i -n '/KAIROS_/!p' /etc/os-release + # RUN sed -i -n '/KAIROS_/!p' /etc/os-release RUN envsubst >>/etc/os-release > ${config_file} + check_error $? "Failed appending config value $append_str" 1 + + return 0 +} + + +########################################################################## +# Determine the Operating system +########################################################################## +get_os() { + if [ -f /etc/os-release ]; then + . /etc/os-release + OS=$NAME + VER=$VERSION_ID + elif type lsb_release >/dev/null 2>&1; then + OS=$(lsb_release -si) + VER=$(lsb_release -sr) + elif [ -f /etc/lsb-release ]; then + . /etc/lsb-release + OS=$DISTRIB_ID + VER=$DISTRIB_RELEASE + elif [ -f /etc/debian_version ]; then + OS=Debian + VER=$(cat /etc/debian_version) + elif [ -f /etc/SuSe-release ]; then + OS=Suse + elif [ -f /etc/centos-release ]; then + OS='CentOS Linux' + VER=$(cat /etc/centos-release | sed 's/.*\( [0-9][^ ]\+\) .*/\1/') + elif [ -f /etc/rocky-release ]; then + OS='Rocky Linux' + VER=$(cat /etc/rocky-release | sed 's/.*\( [0-9][^ ]\+\) .*/\1/') + elif [ -f /etc/redhat-release ]; then + OS='Red Hat Enterprise Linux' + VER=$(cat /etc/redhat-release | sed 's/.*\( [0-9][^ ]\+\) .*/\1/') + else + OS=$(uname -s) + VER=$(uname -r) + fi + + if [[ $OS =~ 'Red Hat' ]]; then + OS_FLAVOUR="rhel" + elif [[ $OS =~ 'CentOS' ]]; then + OS_FLAVOUR="centos" + elif [[ $OS =~ 'Rocky' ]]; then + OS_FLAVOUR="centos" + elif [[ $OS =~ 'Ubuntu' ]]; then + OS_FLAVOUR="ubuntu" + else + OS_FLAVOUR="linux" + fi + + return 0 +} + + +########################################################################## +# Upgrade OS packages +########################################################################## +upgrade_packages() { + if [[ ${OS_FLAVOUR} == "ubuntu" ]]; then + apt-get update + apt-get -y upgrade + check_error $? "Failed upgrading packages" 1 + apt-get install -y auditd apparmor-utils libpam-pwquality + if [[ $? -ne 0 ]]; then + echo 'deb http://archive.ubuntu.com/ubuntu focal main restricted' > /etc/apt/sources.list.d/repotmp.list + apt-get update + apt-get install -y auditd apparmor-utils libpam-pwquality + check_error $? "Failed installing audit packages" 1 + rm -f /etc/apt/sources.list.d/repotmp.list + apt-get update + fi + fi + + if [[ ${OS_FLAVOUR} == "centos" ]]; then + yum -y update + yum install -y audit libpwquality + check_error $? "Failed upgrading packages" 1 + yum clean all + fi + + if [[ ${OS_FLAVOUR} == "rhel" ]]; then + yum -y update + yum install -y audit libpwquality + check_error $? "Failed upgrading packages" 1 + yum clean all + fi + + # Placeholder for supporting other linux OS + if [[ ${OS_FLAVOUR} == "linux" ]]; then + test 1 -eq 2 + check_error $? "OS not supported" 1 + fi + + return 0 +} + + +########################################################################## +# Harden Sysctl based parameters +########################################################################## +harden_sysctl() { + config_file='/etc/sysctl.conf' + + echo "Harden sysctl parameters" + echo "" >> ${config_file} + #Disabling IP forward related hardening as it is needed for k8s + # update_config_files 'net.ipv4.ip_forward' 'net.ipv4.ip_forward=0' ${config_file} + # update_config_files 'net.ipv4.conf.all.forwarding' 'net.ipv4.conf.all.forwarding=0' ${config_file} + # update_config_files 'net.ipv4.conf.all.mc_forwarding' 'net.ipv4.conf.all.mc_forwarding=0' ${config_file} + + update_config_files 'net.ipv4.conf.all.send_redirects' 'net.ipv4.conf.all.send_redirects=0' ${config_file} + update_config_files 'net.ipv4.conf.default.send_redirects' 'net.ipv4.conf.default.send_redirects=0' ${config_file} + + update_config_files 'net.ipv4.conf.all.accept_source_route' 'net.ipv4.conf.all.accept_source_route=0' ${config_file} + update_config_files 'net.ipv4.conf.default.accept_source_route' 'net.ipv4.conf.default.accept_source_route=0' ${config_file} + + update_config_files 'net.ipv4.conf.all.accept_redirects' 'net.ipv4.conf.all.accept_redirects=0' ${config_file} + update_config_files 'net.ipv4.conf.default.accept_redirects' 'net.ipv4.conf.default.accept_redirects=0' ${config_file} + + update_config_files 'net.ipv4.conf.all.secure_redirects' 'net.ipv4.conf.all.secure_redirects=0' ${config_file} + update_config_files 'net.ipv4.conf.default.secure_redirects' 'net.ipv4.conf.default.secure_redirects=0' ${config_file} + + + update_config_files 'net.ipv4.conf.all.log_martians' 'net.ipv4.conf.all.log_martians=1' ${config_file} + update_config_files 'net.ipv4.conf.default.log_martians' 'net.ipv4.conf.default.log_martians=1' ${config_file} + + update_config_files 'net.ipv4.icmp_echo_ignore_broadcasts' 'net.ipv4.icmp_echo_ignore_broadcasts=1' ${config_file} + update_config_files 'net.ipv4.icmp_ignore_bogus_error_responses' 'net.ipv4.icmp_ignore_bogus_error_responses=1' ${config_file} + update_config_files 'net.ipv4.conf.all.rp_filter' 'net.ipv4.conf.all.rp_filter=1' ${config_file} + update_config_files 'net.ipv4.conf.default.rp_filter' 'net.ipv4.conf.default.rp_filter=1' ${config_file} + update_config_files 'net.ipv4.tcp_syncookies' 'net.ipv4.tcp_syncookies=1' ${config_file} + update_config_files 'kernel.randomize_va_space' 'kernel.randomize_va_space=2' ${config_file} + update_config_files 'fs.suid_dumpable' 'fs.suid_dumpable=0' ${config_file} + + + update_config_files 'net.ipv6.conf.all.accept_redirects' 'net.ipv6.conf.all.accept_redirects=0' ${config_file} + update_config_files 'net.ipv6.conf.default.accept_redirects' 'net.ipv6.conf.default.accept_redirects=0' ${config_file} + update_config_files 'net.ipv6.conf.all.accept_source_route' 'net.ipv6.conf.all.accept_source_route=0' ${config_file} + update_config_files 'net.ipv6.conf.default.accept_source_route' 'net.ipv6.conf.default.accept_source_route=0' ${config_file} + update_config_files 'net.ipv6.conf.all.accept_ra' 'net.ipv6.conf.all.accept_ra=0' ${config_file} + update_config_files 'net.ipv6.conf.default.accept_ra' 'net.ipv6.conf.default.accept_ra=0' ${config_file} + + # To restrict core dumps + config_file='/etc/security/limits.conf' + echo "" >> ${config_file} + update_config_files '* hard core' '* hard core 0' ${config_file} + + return 0 +} + + +##function################################################################ +# ssh related hardening +########################################################################## +harden_ssh() { + config_file='/etc/ssh/sshd_config' + + echo "Harden ssh parameters" + # Set permissions on ssh config file + chown root:root ${config_file} + chmod og-rwx ${config_file} + + echo "" >> ${config_file} + update_config_files 'Protocol ' 'Protocol 2' ${config_file} + update_config_files 'LogLevel ' 'LogLevel INFO' ${config_file} + update_config_files 'PermitEmptyPasswords ' 'PermitEmptyPasswords no' ${config_file} + update_config_files 'X11Forwarding ' 'X11Forwarding no' ${config_file} + update_config_files 'IgnoreRhosts ' 'IgnoreRhosts yes' ${config_file} + update_config_files 'MaxAuthTries' 'MaxAuthTries 4' ${config_file} + update_config_files 'PermitRootLogin' 'PermitRootLogin no' ${config_file} + update_config_files 'ClientAliveInterval' 'ClientAliveInterval 300' ${config_file} + update_config_files 'ClientAliveCountMax' 'ClientAliveCountMax 3' ${config_file} + update_config_files 'LoginGraceTime' 'LoginGraceTime 60' ${config_file} + update_config_files 'Banner' 'Banner /etc/issue.net' ${config_file} + update_config_files 'MaxStartups' 'MaxStartups 10:30:60' ${config_file} + update_config_files 'MaxSessions' 'MaxSessions 10' ${config_file} + update_config_files 'PermitUserEnvironment' 'PermitUserEnvironment no' ${config_file} + update_config_files 'HostbasedAuthentication' 'HostbasedAuthentication no' ${config_file} + update_config_files 'Ciphers' 'Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr' ${config_file} + update_config_files 'MACs' 'MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256' ${config_file} + update_config_files 'KexAlgorithms' 'KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256' ${config_file} + + #############Shell timeout policy################## + + # Configuration lines to add to /etc/profile.d/timeout.sh + config_lines="TMOUT=900" + + # Add configuration lines to the top of the file + echo -e "$config_lines" > /etc/profile.d/timeout.sh + + echo "Configuration added to /etc/profile.d/timeout.sh" + + ############sudo command use pty################## + config_file='/etc/sudoers' + echo "" >> ${config_file} + + update_config_files 'Defaults use_pty' 'Defaults use_pty' ${config_file} + echo "Updated config file to sudo command use pty" + + return 0 +} + + +##function################################################################ +# audit related hardening +########################################################################## +harden_audit() { + + local file_path_base="/etc/audit/rules.d/audit.rules" + local file_path_timechange="/etc/audit/rules.d/50-time-change.rules" + local file_path_identityrules="/etc/audit/rules.d/50-identity.rules" + local file_path_accessrules="/etc/audit/rules.d/50-access.rules" + local file_path_deleterules="/etc/audit/rules.d/50-delete.rules" + local file_path_mountrules="/etc/audit/rules.d/50-mounts.rules" + local file_path_scoperules="/etc/audit/rules.d/50-scope.rules" + local file_path_actionsrules="/etc/audit/rules.d/50-actions.rules" + local file_path_modulesrules="/etc/audit/rules.d/50-modules.rules" + local file_path_immutablerules="/etc/audit/rules.d/99-finalize.rules" + local file_path_networkrules="/etc/audit/rules.d/50-system-locale.rules" + local file_path_MACrules="/etc/audit/rules.d/50-MAC-policy.rules" + local file_path_logineventsrules="/etc/audit/rules.d/50-logins.rules" + local file_path_DACrules="/etc/audit/rules.d/50-perm_mod.rules" + + local content_base=( + "-D" + "-b 8192" + "--backlog_wait_time 60000" + "-f 1" + ) + + local content_timechange=( + "-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time-change" + "-a always,exit -F arch=b32 -S adjtimex -S settimeofday -S stime -k time-change" + "-a always,exit -F arch=b64 -S clock_settime -k time-change" + "-a always,exit -F arch=b32 -S clock_settime -k time-change" + "-w /etc/localtime -p wa -k time-change" + ) + + local content_identityrules=( + "-w /etc/group -p wa -k identity" + "-w /etc/passwd -p wa -k identity" + "-w /etc/gshadow -p wa -k identity" + "-w /etc/shadow -p wa -k identity" + "-w /etc/security/opasswd -p wa -k identity" + ) + + local content_accessrules=( + "-a always,exit -F arch=b64 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=4294967295 -k access" + "-a always,exit -F arch=b32 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=4294967295 -k access" + "-a always,exit -F arch=b64 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=4294967295 -k access" + "-a always,exit -F arch=b32 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=4294967295 -k access" + ) + + local content_deleterules=( + "-a always,exit -F arch=b64 -S unlink -S unlinkat -S rename -S renameat -F auid>=1000 -F auid!=4294967295 -k delete" + "-a always,exit -F arch=b32 -S unlink -S unlinkat -S rename -S renameat -F auid>=1000 -F auid!=4294967295 -k delete" + ) + + local content_mountrules=( + "-a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=4294967295 -k mounts" + "-a always,exit -F arch=b32 -S mount -F auid>=1000 -F auid!=4294967295 -k mounts" + ) + + local content_scoperules=( + "-w /etc/sudoers -p wa -k scope" + "-w /etc/sudoers.d/ -p wa -k scope" + ) + + local content_actionsules=( + "-a always,exit -F arch=b64 -C euid!=uid -F euid=0 -Fauid>=1000 -F auid!=4294967295 -S execve -k actions" + "-a always,exit -F arch=b32 -C euid!=uid -F euid=0 -Fauid>=1000 -F auid!=4294967295 -S execve -k actions" + ) + + local content_modulesrules=( + "-w /sbin/insmod -p x -k modules" + "-w /sbin/rmmod -p x -k modules" + "-w /sbin/modprobe -p x -k modules" + "-a always,exit -F arch=b64 -S init_module -S delete_module -k modules" + ) + + local content_immutablerules=( + "-e 2" + ) + + local content_networkrules=( + "-a always,exit -F arch=b64 -S sethostname -S setdomainname -k system-locale" + "-a always,exit -F arch=b32 -S sethostname -S setdomainname -k system-locale" + "-w /etc/issue -p wa -k system-locale" + "-w /etc/issue.net -p wa -k system-local" + "-w /etc/hosts -p wa -k system-locale" + "-w /etc/network -p wa -k system-locale" + ) + + local content_MACrules=( + "-w /etc/apparmor/ -p wa -k MAC-policy" + ) + + local content_logineventsrules=( + "-w /var/log/faillog -p wa -k logins" + "-w /var/log/lastlog -p wa -k logins" + "-w /var/log/tallylog -p wa -k logins" + ) + + local content_DACrules=( + "-a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat -F auid>=1000 -F auid!=4294967295 -k perm_mod" + "-a always,exit -F arch=b32 -S chmod -S fchmod -S fchmodat -F auid>=1000 -F auid!=4294967295 -k perm_mod" + "-a always,exit -F arch=b64 -S chown -S fchown -S fchownat -S lchown -F auid>=1000 -F auid!=4294967295 -k perm_mod" + "-a always,exit -F arch=b32 -S chown -S fchown -S fchownat -S lchown -F auid>=1000 -F auid!=4294967295 -k perm_mod" + "-a always,exit -F arch=b64 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=1000 -F auid!=4294967295 -k perm_mod" + "-a always,exit -F arch=b32 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=1000 -F auid!=4294967295 -k perm_mod" + ) + + + # Create or append to the time change rules + echo "" > "$file_path_base" + for line in "${content_base[@]}"; do + echo "$line" | sudo tee -a "$file_path_base" >/dev/null + done + + # Create or append to the time change rules + for line in "${content_timechange[@]}"; do + echo "$line" | sudo tee -a "$file_path_timechange" >/dev/null + done + + # Create or append to the identity rules + for line in "${content_identityrules[@]}"; do + echo "$line" | sudo tee -a "$file_path_identityrules" >/dev/null + done + + # Create or append to the access rules + for line in "${content_accessrules[@]}"; do + echo "$line" | sudo tee -a "$file_path_accessrules" >/dev/null + done + + # Create or append to the delete rules + for line in "${content_deleterules[@]}"; do + echo "$line" | sudo tee -a "$file_path_deleterules" >/dev/null + done + + # Create or append to the mount rules + for line in "${content_mountrules[@]}"; do + echo "$line" | sudo tee -a "$file_path_mountrules" >/dev/null + done + + # Create or append to the scope rules + for line in "${content_scoperules[@]}"; do + echo "$line" | sudo tee -a "$file_path_scoperules" >/dev/null + done + + # Create or append to the actions rules + for line in "${content_actionsules[@]}"; do + echo "$line" | sudo tee -a "$file_path_actionsrules" >/dev/null + done + + # Create or append to the modules rules + for line in "${content_modulesrules[@]}"; do + echo "$line" | sudo tee -a "$file_path_modulesrules" >/dev/null + done + + # Create or append to the immutables rules + for line in "${content_immutablerules[@]}"; do + echo "$line" | sudo tee -a "$file_path_immutablerules" >/dev/null + done + + # Create or append to the network rules + for line in "${content_networkrules[@]}"; do + echo "$line" | sudo tee -a "$file_path_networkrules" >/dev/null + done + + # Create or append to the MAC rules + for line in "${content_MACrules[@]}"; do + echo "$line" | sudo tee -a "$file_path_MACrules" >/dev/null + done + + # Create or append to the login/logout events rules + for line in "${content_logineventsrules[@]}"; do + echo "$line" | sudo tee -a "$file_path_logineventsrules" >/dev/null + done + + # Create or append to the DAC rules + for line in "${content_DACrules[@]}"; do + echo "$line" | sudo tee -a "$file_path_DACrules" >/dev/null + done + + + # Verify if the files were created or appended successfully + if [ -f "$file_path_timechange" ] && [ -f "$file_path_identityrules" ] && [ -f "$file_path_accessrules" ] && [ -f "$file_path_deleterules" ] && [ -f "$file_path_mountrules" ] && [ -f "$file_path_scoperules" ] && [ -f "$file_path_actionsrules" ] && [ -f "$file_path_modulesrules" ] && [ -f "$file_path_immutablerules" ] && [ -f "$file_path_networkrules" ] && [ -f "$file_path_MACrules" ] && [ -f "$file_path_logineventsrules" ] && [ -f "$file_path_DACrules" ]; then + echo "Files '$file_path_timechange', '$file_path_identityrules', '$file_path_accessrules', '$file_path_deleterules', '$file_path_mountrules', '$file_path_scoperules', '$file_path_actionsrules', '$file_path_modulesrules', '$file_path_immutablerules', '$file_path_networkrules', '$file_path_MACrules', '$file_path_logineventsrules'& '$file_path_DACrules' created/appended successfully." + else + echo "Failed to create/append to files '$file_path_timechange' and/or '$file_path_identityrules' and or '$file_path_accessrules' and or '$file_path_deleterules' and or '$file_path_mountrules' and or '$file_path_scoperules' and or '$file_path_actionsrules' and or '$file_path_modulesrules' and or '$file_path_immutablerules' and or '$file_path_networkrules' and or '$file_path_MACrules' and or '$file_path_logineventsrules' and or '$file_path_DACrules'." + fi + + # Define the desired value for max_log_file + max_log_file_value=100 + + # Set the max_log_file parameter in auditd.conf + sed -i "s/^max_log_file = 8/max_log_file = ${max_log_file_value}/" /etc/audit/auditd.conf + + echo "The max_log_file parameter has been set to ${max_log_file_value}." + + # Enable auditd service + systemctl enable auditd + + return 0 +} + +##function################################################################ +# boot up related hardening +########################################################################## +harden_boot() { + echo "Disable Ctrl + Alt + Del key" + systemctl mask ctrl-alt-del.target + + grub_conf='/etc/cos/grub.cfg' + + if [[ -f ${grub_conf} ]]; then + chown root:root ${grub_conf} + chmod u-wx,go-rwx ${grub_conf} + + sed -i 's/set baseExtraArgs=""/set baseExtraArgs="audit=1"/g' /etc/cos/bootargs.cfg + + echo "Grub configuration updated successfully." + fi + + return 0 +} + +##function################################################################ +# password related hardening +########################################################################## +harden_password_files() { + + chmod 644 /etc/passwd + chown root:root /etc/passwd + chmod 644 /etc/passwd- + chown root:root /etc/passwd- + chmod 640 /etc/shadow + chown root:root /etc/shadow + chmod 000 /etc/shadow- + chown root:root /etc/shadow- + chmod 000 /etc/gshadow + chown root:root /etc/gshadow + chmod 000 /etc/gshadow- + chown root:root /etc/gshadow- + chmod 644 /etc/group + chown root:root /etc/group + chmod 644 /etc/group- + chown root:root /etc/group- + + return 0 +} + + +##function################################################################ +# os related hardening +########################################################################## +harden_system() { + + echo "Check if root user has 0 as guid , if not set it" + root_gid=$(grep '^root:' /etc/passwd | cut -d : -f 4) + if [[ ${root_gid} -ne 0 ]]; then + usermod -g 0 root + check_error $? "Failed changing root guid to 0" 1 + fi + + echo "Error out if there are users with empty password" + cat /etc/shadow |awk -F : '($2 == "" ){ exit 1}' + if [[ $? -ne 0 ]]; then + echo "Users present with empty password. Remove the user or set pasword for the users" + exit 1 + fi + + echo "Check if any user other than root has uid of 0" + root_uid_count=$(cat /etc/passwd | awk -F ":" '($3 == 0){print $3}' | wc -l) + if [[ ${root_uid_count} -ne 1 ]]; then + echo "Non root users have UID of 0.Correct the error and retry" + exit 1 + fi + + echo "Fix permission of all cron files" + for each in `echo /etc/cron.daily /etc/cron.hourly /etc/cron.d /etc/cron.monthly /etc/cron.weekly /etc/crontab` + do + if [[ -e ${each} ]]; then + stat -L -c "%a %u %g" ${each} | egrep ".00 0 0" + if [[ $? -ne 0 ]]; then + chown root:root ${each} + chmod og-rwx ${each} + fi + fi + done + + echo "Remove cron and at deny files anf have allow files in place" + rm -f /etc/cron.deny + rm -f /etc/at.deny + touch /etc/cron.allow + touch /etc/at.allow + chmod g-wx,o-rwx /etc/cron.allow + chmod g-wx,o-rwx /etc/at.allow + chown root:root /etc/cron.allow + chown root:root /etc/at.allow + + if [[ ! -f /etc/issue ]]; then + echo "### Authorized users only. All activity may be monitored and reported ###" > /etc/issue + fi + chmod 644 /etc/issue + chown root:root /etc/issue + + if [[ ! -f /etc/issue.net ]]; then + echo "### Authorized users only. All activity may be monitored and reported ###" > /etc/issue.net + fi + chmod 644 /etc/issue.net + chown root:root /etc/issue.net + + if [[ -f /etc/rsyslog.conf ]]; then + chmod 0640 /etc/rsyslog.conf + fi + + ##################users' home directories permissions are 750 or more restrictive###### + awk -F: '($1 !~ /^(halt|sync|shutdown|nfsnobody)$/ && $7 !~ /^(\/usr)?\/sbin\/nologin(\/)?$/ && $7 !~ /^(\/usr)?\/bin\/false(\/)?$/) {print $6}' /etc/passwd | while read -r dir; do + if [ -d "$dir" ]; then + dirperm=$(stat -L -c '%a' "$dir") + if [ "$(echo "$dirperm" | cut -c6)" != "-" ] || [ "$(echo "$dirperm" | cut -c8)" != "-" ] || [ "$(echo "$dirperm" | cut -c9)" != "-" ] || [ "$(echo "$dirperm" | cut -c10)" != "-" ]; then + chmod g-w,o-rwx "$dir" + fi + fi + done + + return 0 +} + +########################################################################## +# Remove unnecessary packages +########################################################################## +remove_services() { + + if [[ ${OS_FLAVOUR} == "ubuntu" ]]; then + echo "Disable setrouble shoot service if enabled" + systemctl disable setroubleshoot + + echo "Removing legacy networking services" + systemctl disable xinetd + apt-get remove -y openbsd-inetd rsh-client rsh-redone-client nis talk telnet ldap-utils gdm3 + apt-get purge -y telnet vim vim-common vim-runtime vim-tiny + + echo "Removing X packages" + apt-get remove -y xserver-xorg* + fi + + if [[ ${OS_FLAVOUR} == "centos" ]] || [[ ${OS_FLAVOUR} == "rhel" ]]; then + echo "Disable setrouble shoot service if enabled" + chkconfig setroubleshoot off + + echo "Removing legacy networking services" + yum erase -y inetd xinetd ypserv tftp-server telnet-server rsh-server gdm3 telnet vim vim-common vim-runtime vim-tiny + + echo "Removing X packages" + yum groupremove -y "X Window System" + yum remove -y xorg-x11* + fi + + # Placeholder for supporting other linux OS + if [[ ${OS_FLAVOUR} == "linux" ]]; then + test 1 -eq 2 + check_error $? "OS not supported" 1 + fi + + return 0 +} + +########################################################################## +# Block unnecessary modules +########################################################################## +disable_modules() { + + if [[ -d /etc/modprobe.d ]]; then + echo "Disabling unnecessary modules" + + echo "install dccp /bin/true" > /etc/modprobe.d/dccp.conf + echo "install sctp /bin/true" >> /etc/modprobe.d/sctp.conf + echo "install rds /bin/true" >> /etc/modprobe.d/rds.conf + echo "install tipc /bin/true" >> /etc/modprobe.d/tipc.conf + + echo "install cramfs /bin/false" > /etc/modprobe.d/cramfs.conf + echo "install freevxfs /bin/true" >> /etc/modprobe.d/freevxfs.conf + echo "install jffs2 /bin/true" >> /etc/modprobe.d/jffs2.conf + echo "install hfs /bin/true" >> /etc/modprobe.d/hfs.conf + echo "install hfsplus /bin/true" >> /etc/modprobe.d/hfsplus.conf + + # Needed for Kairos + #echo "install squashfs /bin/true" >> /etc/modprobe.d/squashfs.conf + #echo "install udf /bin/true" >> /etc/modprobe.d/udf.conf + #echo "install usb-storage /bin/false" >> /etc/modprobe.d/usb_storage.conf + fi + + return 0 +} + +########################################################################## +# Login Banner +########################################################################## + +harden_banner() { + + local file_path_locallogin="/etc/issue" + local file_path_remotelogin="/etc/issue.net" + + local content_locallogin=( + "Authorized uses only. All activity may be monitored and reported." + ) + + local content_remotelogin=( + "Authorized uses only. All activity may be monitored and reported." + ) + # Create or append to the local login banner + for line in "${content_locallogin[@]}"; do + echo "$line" | sudo tee -a "$file_path_locallogin" >/dev/null + done + + # Create or append to the remote login banner + for line in "${content_remotelogin[@]}"; do + echo "$line" | sudo tee -a "$file_path_remotelogin" >/dev/null + done + + # Verify if the files were created or appended successfully + if [ -f "$file_path_locallogin" ] && [ -f "$file_path_remotelogin" ]; then + echo "Files $file_path_locallogin', '$file_path_remotelogin' created/appended successfully." + else + echo "Failed to create/append to files '$file_path_locallogin' and or '$file_path_remotelogin'." + fi + + # Delete motd file + if [[ -f /etc/motd ]]; then rm /etc/motd; fi + + return 0 +} + + +############################################################# +# Log files permission +############################################################# +harden_log() { + + # Ensure permissions on all logfiles are configured + + # Find and set permissions on log files + find /var/log -type f -exec chmod g-wx,o-rwx '{}' + -o -type d -exec chmod g-w,o-rwx '{}' + + + echo "750 permission set on all log files & directories inside /var/log" + + # Ensure logrotate assigns appropriate permissions + + # Define restrictive permissiom + utmp="create 0640 root utmp" + # Modify the logrotate configuration + + if [ -e "/etc/logrotate.conf" ]; then + # Check if the logrotation restrictive permission exists + if grep -q "^create 0640" /etc/logrotate.conf; then + # Modify the existing line + sed -i "s/^create 0640.*/$utmp/" /etc/logrotate.conf + echo "Modified restrictive permission in /etc/logrotate.conf" + else + # Add the new restrictive permission at the end of the file + echo "$utmp" >> /etc/logrotate.conf + echo "Added restrictive permission to /etc/logrotate.conf" + fi + fi + + # Ensure sudo log file exists + # Define sudo log file + logfile="Defaults logfile=/var/log/sudo.log" + + if [ -e "/etc/sudoers" ]; then + # Check if the sudo log file path exists + if grep -q "$logfile" /etc/sudoers; then + echo "sudo log file path already exist in /etc/sudoers" + else + # Add the log file path at the end of the file + echo "$logfile" >> /etc/sudoers + echo "Added log file path to /etc/sudoers" + fi + fi + + return 0 +} + + +########################################################################## +# Authentication/Login Hardening +########################################################################## +harden_auth() { + + # Define the new values for minlen and minclass + new_minlen="minlen = 14" + new_minclass="minclass = 4" + new_difok="difok = 2" + new_dictcheck="dictcheck = 0" + new_maxrepeat="maxrepeat = 3" + + # Check if the file exists + if [ -e "/etc/security/pwquality.conf" ]; then + # Check if the minlen line already exists + if grep -q "^minlen" /etc/security/pwquality.conf; then + # Modify the existing minlen line + sed -i "s/^minlen.*/$new_minlen/" /etc/security/pwquality.conf + echo "Modified minlen in /etc/security/pwquality.conf" + else + # Add the new minlen line at the end of the file + echo "$new_minlen" >> /etc/security/pwquality.conf + echo "Added minlen to /etc/security/pwquality.conf" + fi + + # Check if the minclass line already exists + if grep -q "^minclass" /etc/security/pwquality.conf; then + # Modify the existing minclass line + sed -i "s/^minclass.*/$new_minclass/" /etc/security/pwquality.conf + echo "Modified minclass in /etc/security/pwquality.conf" + else + # Add the new minclass line at the end of the file + echo "$new_minclass" >> /etc/security/pwquality.conf + echo "Added minclass to /etc/security/pwquality.conf" + fi + + # Check if the difok line already exists + if grep -q "^difok" /etc/security/pwquality.conf; then + # Modify the existing difok line + sed -i "s/^difok.*/$new_difok/" /etc/security/pwquality.conf + echo "Modified difok in /etc/security/pwquality.conf" + else + # Add the new difok line at the end of the file + echo "$new_difok" >> /etc/security/pwquality.conf + echo "Added difok to /etc/security/pwquality.conf" + fi + + # Check if the dictcheck line already exists + if grep -q "^dictcheck" /etc/security/pwquality.conf; then + # Modify the existing dictcheck line + sed -i "s/^dictcheck.*/$new_dictcheck/" /etc/security/pwquality.conf + echo "Modified dictcheck in /etc/security/pwquality.conf" + else + # Add the new dictcheck line at the end of the file + echo "$new_dictcheck" >> /etc/security/pwquality.conf + echo "Added dictcheck to /etc/security/pwquality.conf" + fi + + # Check if the maxrepeat line already exists + if grep -q "^maxrepeat" /etc/security/pwquality.conf; then + # Modify the existing maxrepeat line + sed -i "s/^maxrepeat.*/$new_maxrepeat/" /etc/security/pwquality.conf + echo "Modified maxrepeat in /etc/security/pwquality.conf" + else + # Add the new maxrepeat line at the end of the file + echo "$new_maxrepeat" >> /etc/security/pwquality.conf + echo "Added maxrepeat to /etc/security/pwquality.conf" + fi + else + echo "File /etc/security/pwquality.conf not found." + fi + + # Configuration lines to add to /etc/pam.d/su + config_lines="auth required pam_wheel.so use_uid group=admin" + + # Add configuration lines to the top of the file + echo -e "$config_lines\n$(cat /etc/pam.d/su)" > /etc/pam.d/su + + echo "Configuration to ensure access to the su command is restricted have been made" + + ##############Password lockout policies################## + + # Backup the original file + cp /etc/pam.d/common-auth /etc/pam.d/common-auth.bak + + echo "auth required pam_faillock.so preauth audit silent deny=4 fail_interval=900 unlock_time=600" > /etc/pam.d/common-auth + echo "auth [success=1 default=ignore] pam_unix.so nullok" >> /etc/pam.d/common-auth + echo "auth [default=die] pam_faillock.so authfail audit deny=4 fail_interval=900 unlock_time=600" >> /etc/pam.d/common-auth + echo "auth sufficient pam_faillock.so authsucc audit deny=4 fail_interval=900 unlock_time=600" >> /etc/pam.d/common-auth + echo "auth requisite pam_deny.so" >> /etc/pam.d/common-auth + echo "auth required pam_permit.so" >> /etc/pam.d/common-auth + + # Backup the original file + cp /etc/pam.d/common-account /etc/pam.d/common-account.bak + + echo "account required pam_faillock.so" >> /etc/pam.d/common-account + + ##############Password reuse policy################## + + # Backup the original file + cp /etc/pam.d/common-password /etc/pam.d/common-password.bak + + echo "password requisite pam_pwquality.so retry=3" > /etc/pam.d/common-password + echo "password [success=1 default=ignore] pam_unix.so obscure use_authtok try_first_pass remember=5" >> /etc/pam.d/common-password + echo "password requisite pam_deny.so" >> /etc/pam.d/common-password + echo "password required pam_permit.so" >> /etc/pam.d/common-password + + #####################Password expiry policy################# + + #Define the destination file + config_file='/etc/login.defs' + + echo "" >> ${config_file} + + update_config_files 'PASS_MIN_DAYS' 'PASS_MIN_DAYS 1' ${config_file} + update_config_files 'PASS_MAX_DAYS' 'PASS_MAX_DAYS 365' ${config_file} + update_config_files 'PASS_WARN_AGE' 'PASS_WARN_AGE 7' ${config_file} + + echo "Password expiry policy updated to PASS_MIN_DAYS 1 & PASS_MAX_DAYS 365 & PASS_WARN_AGE 7" + + #####################Password encryption standards########## + + config_file='/etc/login.defs' + + update_config_files 'ENCRYPT_METHOD' 'ENCRYPT_METHOD yescrypt' ${config_file} + + echo "Password encryption method set to yescrypt" + + ####################Inactive password lock################ + + #Define the destination file + config_file='/etc/default/useradd' + + echo "" >> ${config_file} + + update_config_files 'INACTIVE' 'INACTIVE=30' ${config_file} + echo "Inactive password lock policy updated to 30 days" + + #################Session expiry policy##################### + # Configuration lines to add to /etc/profile + config_lines="readonly TMOUT=900 ; export TMOUT" + + # Add configuration lines to the top of the file + echo "$config_lines" >> /etc/profile + + echo "Configuration added to /etc/profile for shell timeout policy" + return 0 +} + +########################################################################## +# Cleanup Package Manager Cache +########################################################################## +cleanup_cache() { + if [[ ${OS_FLAVOUR} == "ubuntu" ]]; then + apt-get clean + rm -rf /var/lib/apt/lists/* + fi + + if [[ ${OS_FLAVOUR} == "centos" ]]; then + yum clean all + rm -rf /var/cache/yum/* + fi + + if [[ ${OS_FLAVOUR} == "rhel" ]]; then + yum clean all + rm -rf /var/cache/yum/* + fi + + # Placeholder for supporting other linux OS + if [[ ${OS_FLAVOUR} == "linux" ]]; then + test 1 -eq 2 + check_error $? "OS not supported" 1 + fi + + return 0 +} + +cp /etc/os-release /etc/os-release.bak + +OS_FLAVOUR="linux" +get_os +upgrade_packages +harden_sysctl +harden_ssh +harden_boot +harden_password_files +harden_system +remove_services +disable_modules +harden_audit +harden_banner +harden_log +harden_auth +cleanup_cache + +mv /etc/os-release.bak /etc/os-release + +exit 0 \ No newline at end of file diff --git a/cloudconfigs/80_stylus_uki.yaml b/cloudconfigs/80_stylus_uki.yaml new file mode 100644 index 0000000..e62c4e1 --- /dev/null +++ b/cloudconfigs/80_stylus_uki.yaml @@ -0,0 +1,29 @@ +#cloud-config +stages: + initramfs: + - if: '[ -e "/run/cos/uki_boot_mode" ] && [ ! -f "/run/cos/live_mode" ]' + name: link agent provider stylus + commands: + - echo "Linking agent provider" + - mkdir -p /usr/local/bin/ + - ln -sf /opt/spectrocloud/bin/agent-provider-stylus /usr/local/bin/agent-provider-stylus + - if: '[ -e "/run/cos/uki_boot_mode" ] && [ ! -f "/run/cos/live_mode" ] && [ ! -f "/opt/spectrocloud/bin/stylus-agent" ]' + name: unpack stylus package + commands: + - echo "Unpacking stylus package" + - ln -sf /oem/opt.bind/luet /usr/local/bin/luet + - /oem/opt.bind/luet util unpack file://oem/opt.bind/stylus-image.tar / + after-install: + - name: uki stylus package persist + if: '[ -e "/run/cos/uki_install_mode" ]' + commands: + - echo "Copying files to persistent path" + - if mount | grep /oem >/dev/null; then umount /oem; fi + - for d in /dev/mapper/*; do if [ ! "$d" = "/dev/mapper/control" ]; then cryptsetup close $d; fi; done + - /usr/lib/systemd/systemd-cryptsetup attach oem $(findfs PARTLABEL=oem) - tpm2-device=auto + - mount /dev/mapper/oem /oem + - mkdir -p /oem/opt.bind + - cp -rfv /run/initramfs/live/luet /oem/opt.bind/ + - cp -rfv /run/initramfs/live/stylus-image.tar /oem/opt.bind/ + - umount /dev/mapper/oem + - if [ -e /dev/mapper/oem ]; then cryptsetup close /dev/mapper/oem; fi diff --git a/mount.yaml b/cloudconfigs/mount.yaml similarity index 100% rename from mount.yaml rename to cloudconfigs/mount.yaml diff --git a/earthly.sh b/earthly.sh index dd1e992..6bca2a6 100755 --- a/earthly.sh +++ b/earthly.sh @@ -26,13 +26,10 @@ function build_without_proxy() { global_config="{disable_analytics: true}" PE_VERSION=$(git describe --abbrev=0 --tags) +SPECTRO_PUB_REPO=gcr.io/spectro-images-public EARTHLY_VERSION=v0.8.5 source .arg -if [ -z "SPECTRO_PUB_REPO" ]; then - SPECTRO_PUB_REPO=gcr.io/spectro-images-public -fi - ### Verify Depencies # Check if Docker is installed if command -v docker >/dev/null 2>&1; then @@ -64,32 +61,36 @@ fi docker rmi $SPECTRO_PUB_REPO/earthly/buildkitd:$EARTHLY_VERSION docker rmi alpine:latest -# Print the output for use in Palette Profile. -echo -e '##########################################################################################################' -echo -e '\nPASTE THE CONTENT BELOW INTO YOUR CLUSTER PROFILE IN PALETTE REPLACING ALL THE CONTENTS IN THE PROFILE\n' -echo -e '##########################################################################################################' -echo -e '\n' -echo -e 'pack:' -echo -e ' content:' -echo -e ' images:' -echo -e ' - image: "{{.spectro.pack.edge-native-byoi.options.system.uri}}"' -echo -e ' # Below config is default value, please uncomment if you want to modify default values' -echo -e ' #drain:' -echo -e ' #cordon: true' -echo -e ' #timeout: 60 # The length of time to wait before giving up, zero means infinite' -echo -e ' #gracePeriod: 60 # Period of time in seconds given to each pod to terminate gracefully. If negative, the default value specified in the pod will be used' -echo -e ' #ignoreDaemonSets: true' -echo -e ' #deleteLocalData: true # Continue even if there are pods using emptyDir (local data that will be deleted when the node is drained)' -echo -e ' #force: true # Continue even if there are pods that do not declare a controller' -echo -e ' #disableEviction: false # Force drain to use delete, even if eviction is supported. This will bypass checking PodDisruptionBudgets, use with caution' -echo -e ' #skipWaitForDeleteTimeout: 60 # If pod DeletionTimestamp older than N seconds, skip waiting for the pod. Seconds must be greater than 0 to skip.' -echo -e 'options:' -echo -e ' system.uri: "{{ .spectro.pack.edge-native-byoi.options.system.registry }}/{{ .spectro.pack.edge-native-byoi.options.system.repo }}:{{ .spectro.pack.edge-native-byoi.options.system.k8sDistribution }}-{{ .spectro.system.kubernetes.version }}-{{ .spectro.pack.edge-native-byoi.options.system.peVersion }}-{{ .spectro.pack.edge-native-byoi.options.system.customTag }}"' -echo -e '\n' -echo -e " system.registry: $IMAGE_REGISTRY" -echo -e " system.repo: $IMAGE_REPO" -echo -e " system.k8sDistribution: $K8S_DISTRIBUTION" -echo -e " system.osName: $OS_DISTRIBUTION" -echo -e " system.peVersion: $PE_VERSION" -echo -e " system.customTag: $CUSTOM_TAG" -echo -e " system.osVersion: $OS_VERSION" +if [[ "$1" == "+uki-genkey" ]]; then + ./keys.sh secure-boot/ +else + # Print the output for use in Palette Profile. + echo -e '##########################################################################################################' + echo -e '\nPASTE THE CONTENT BELOW INTO YOUR CLUSTER PROFILE IN PALETTE REPLACING ALL THE CONTENTS IN THE PROFILE\n' + echo -e '##########################################################################################################' + echo -e '\n' + echo -e 'pack:' + echo -e ' content:' + echo -e ' images:' + echo -e ' - image: "{{.spectro.pack.edge-native-byoi.options.system.uri}}"' + echo -e ' # Below config is default value, please uncomment if you want to modify default values' + echo -e ' #drain:' + echo -e ' #cordon: true' + echo -e ' #timeout: 60 # The length of time to wait before giving up, zero means infinite' + echo -e ' #gracePeriod: 60 # Period of time in seconds given to each pod to terminate gracefully. If negative, the default value specified in the pod will be used' + echo -e ' #ignoreDaemonSets: true' + echo -e ' #deleteLocalData: true # Continue even if there are pods using emptyDir (local data that will be deleted when the node is drained)' + echo -e ' #force: true # Continue even if there are pods that do not declare a controller' + echo -e ' #disableEviction: false # Force drain to use delete, even if eviction is supported. This will bypass checking PodDisruptionBudgets, use with caution' + echo -e ' #skipWaitForDeleteTimeout: 60 # If pod DeletionTimestamp older than N seconds, skip waiting for the pod. Seconds must be greater than 0 to skip.' + echo -e 'options:' + echo -e ' system.uri: "{{ .spectro.pack.edge-native-byoi.options.system.registry }}/{{ .spectro.pack.edge-native-byoi.options.system.repo }}:{{ .spectro.pack.edge-native-byoi.options.system.k8sDistribution }}-{{ .spectro.system.kubernetes.version }}-{{ .spectro.pack.edge-native-byoi.options.system.peVersion }}-{{ .spectro.pack.edge-native-byoi.options.system.customTag }}"' + echo -e '\n' + echo -e " system.registry: $IMAGE_REGISTRY" + echo -e " system.repo: $IMAGE_REPO" + echo -e " system.k8sDistribution: $K8S_DISTRIBUTION" + echo -e " system.osName: $OS_DISTRIBUTION" + echo -e " system.peVersion: $PE_VERSION" + echo -e " system.customTag: $CUSTOM_TAG" + echo -e " system.osVersion: $OS_VERSION" +fi diff --git a/efi-size-check/Cargo.lock b/efi-size-check/Cargo.lock new file mode 100644 index 0000000..f96ac5b --- /dev/null +++ b/efi-size-check/Cargo.lock @@ -0,0 +1,163 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "efi-size-check" +version = "0.1.0" +dependencies = [ + "log", + "uefi", + "uefi-services", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptr_meta" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcada80daa06c42ed5f48c9a043865edea5dc44cbf9ac009fda3b89526e28607" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca9224df2e20e7c5548aeb5f110a0f3b77ef05f8585139b7148b59056168ed2" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "ucs2" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad643914094137d475641b6bab89462505316ec2ce70907ad20102d28a79ab8" +dependencies = [ + "bit_field", +] + +[[package]] +name = "uefi" +version = "0.27.0" +source = "git+https://github.com/nianyush/uefi-rs.git?branch=unchunked#270480564d78a92a1c8f5a05b9f2612d2dc8b7fd" +dependencies = [ + "bitflags", + "log", + "ptr_meta", + "ucs2", + "uefi-macros", + "uefi-raw", + "uguid", +] + +[[package]] +name = "uefi-macros" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a7b1c2c808c3db854a54d5215e3f7e7aaf5dcfbce095598cba6af29895695d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "uefi-raw" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cc02890e65b2b8e390d0e081907c0d5ac3931bc6935f452095d68199e3698c5" +dependencies = [ + "bitflags", + "ptr_meta", + "uguid", +] + +[[package]] +name = "uefi-services" +version = "0.24.0" +source = "git+https://github.com/nianyush/uefi-rs.git?branch=unchunked#270480564d78a92a1c8f5a05b9f2612d2dc8b7fd" +dependencies = [ + "cfg-if", + "log", + "uefi", +] + +[[package]] +name = "uguid" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab14ea9660d240e7865ce9d54ecdbd1cd9fa5802ae6f4512f093c7907e921533" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" \ No newline at end of file diff --git a/efi-size-check/Cargo.toml b/efi-size-check/Cargo.toml new file mode 100644 index 0000000..c5d06be --- /dev/null +++ b/efi-size-check/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "efi-size-check" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +log = "0.4.21" +uefi = { git = "https://github.com/nianyush/uefi-rs.git", branch = "unchunked" } +uefi-services = { git = "https://github.com/nianyush/uefi-rs.git", branch = "unchunked" } diff --git a/efi-size-check/rust-toolchain.toml b/efi-size-check/rust-toolchain.toml new file mode 100644 index 0000000..b3a6ddc --- /dev/null +++ b/efi-size-check/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +targets = ["aarch64-unknown-uefi", "i686-unknown-uefi", "x86_64-unknown-uefi"] \ No newline at end of file diff --git a/efi-size-check/src/main.rs b/efi-size-check/src/main.rs new file mode 100644 index 0000000..5532db4 --- /dev/null +++ b/efi-size-check/src/main.rs @@ -0,0 +1,128 @@ +#![no_main] +#![no_std] + +extern crate alloc; +extern crate uefi; +extern crate uefi_services; + +use alloc::format; +use alloc::string::String; +use alloc::vec::Vec; +use log::info; +use uefi::prelude::*; +use uefi::proto::media::file::{File, FileAttribute, FileMode}; +use uefi::table::boot::{AllocateType, MemoryType}; +use uefi::CStr16; + +const KB: usize = 1024; +const MB: usize = KB * 1024; +const GB: usize = MB * 1024; + +const MAX_SIZE: usize = 2 * GB; +const INCREMENT: usize = 100 * MB; + +#[entry] +fn main(image: Handle, mut st: SystemTable) -> Status { + match uefi_services::init(&mut st) { + Ok(_) => {} + Err(err) => { + panic!("Failed to initialize uefi services: {:?}", err); + } + } + info!("Starting EFI size checker..."); + + let bs = st.boot_services(); + + let mut sfs = bs.get_image_file_system(image).unwrap(); + let mut root = sfs.open_volume().unwrap(); + + let mut buf = [0; 4]; + let file_name: &CStr16 = CStr16::from_str_with_buf("ABC", &mut buf).unwrap(); // Ensure this file exists on your ESP with a large size for testing + let mut buffer_size = MB * 100; // Start with 100 MiB + info!("start to do file read check..."); + + loop { + if buffer_size > MAX_SIZE { + info!("Max buffer size reached: {}", format_bytes(MAX_SIZE)); + break; + } + let mut file = match root.open(file_name, FileMode::Read, FileAttribute::empty()) { + Ok(f) => f.into_regular_file().unwrap(), + Err(_) => { + panic!("Failed to open file: {:?}", file_name); + } + }; + + info!("Reading {} bytes into buffer", format_bytes(buffer_size)); + + let mut buffer = Vec::with_capacity(buffer_size); + unsafe { + buffer.set_len(buffer_size); + } // Unsafe due to uninitialized memory + + match file.read_unchunked(&mut buffer) { + Ok(_) => { + info!( + "Successfully read {} into buffer", + format_bytes(buffer_size) + ); + buffer_size += INCREMENT; + } + Err(e) => { + panic!("Failed to read into a {} byte buffer: {:?}", buffer_size, e); + } + } + } + + mem_test(bs); + + bs.stall(100_000_000); + Status::SUCCESS +} + +fn mem_test(bs: &BootServices) { + let mut buffer_size = MB * 100; // Start with 100 MiB + info!("start to do memory allocation check..."); + + loop { + if buffer_size > MAX_SIZE { + info!("Max buffer size reached: {}", format_bytes(MAX_SIZE)); + break; + } + match bs.allocate_pages( + AllocateType::AnyPages, + MemoryType::ACPI_RECLAIM, + buffer_size, + ) { + Ok(addr) => { + info!("Successfully allocated {} mem", buffer_size); + unsafe { + match bs.free_pages(addr, buffer_size) { + Ok(_) => { + info!("Successfully freed {} mem", buffer_size) + } + Err(err) => { + panic!("Failed to free {} mem: {:?}", buffer_size, err); + } + } + } + } + Err(err) => { + panic!("Failed to allocate {} mem: {:?}", buffer_size, err); + } + } + buffer_size += INCREMENT; + } +} + +fn format_bytes(size: usize) -> String { + if size >= GB { + format!("{:.2} GB", size as f64 / GB as f64) + } else if size >= MB { + format!("{:.2} MB", size as f64 / MB as f64) + } else if size >= KB { + format!("{:.2} KB", size as f64 / KB as f64) + } else { + format!("{} bytes", size) + } +} \ No newline at end of file diff --git a/internal/cmd/slink/slink.go b/internal/cmd/slink/slink.go new file mode 100644 index 0000000..822ec12 --- /dev/null +++ b/internal/cmd/slink/slink.go @@ -0,0 +1,62 @@ +package main + +import ( + "os" + "path/filepath" + + "github.com/sirupsen/logrus" + "github.com/spectrocloud/CanvOS/internal/pkg/utils" + "github.com/spf13/cobra" + "github.com/twpayne/go-vfs/v5" +) + +func slink(cmd *cobra.Command, args []string) { + source := cmd.Flag("source").Value.String() + target := cmd.Flag("target").Value.String() + logrus.Infof("Source: %s, Target: %s", source, target) + if source == "" || target == "" { + logrus.Fatal("Source and target must be provided") + } + + sourceFS := vfs.NewPathFS(vfs.OSFS, source) + + if err := vfs.Walk(sourceFS, "/", func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + + // Create symlink + linkname := path + targetname := filepath.Join(target, path) + logrus.Infof("Creating link %s to %s", linkname, targetname) + + if err := utils.CopyDir(filepath.Dir(linkname), sourceFS, vfs.OSFS); err != nil { + logrus.Errorf("Error creating directory: %s", err) + return err + } + + if err := os.Symlink(targetname, linkname); err != nil { + logrus.Errorf("Error creating symlink: %s", err) + return err + } + + return nil + }); err != nil { + logrus.Fatalf("Error walking source directory: %s", err) + } +} + +func main() { + cmd := &cobra.Command{ + Use: "slink", + Run: slink, + } + cmd.Flags().StringP("source", "s", "", "source directory") + cmd.Flags().StringP("target", "t", "", "target prefix") + if err := cmd.Execute(); err != nil { + logrus.Fatal(err) + } +} diff --git a/internal/go.mod b/internal/go.mod new file mode 100644 index 0000000..7b9f0d7 --- /dev/null +++ b/internal/go.mod @@ -0,0 +1,16 @@ +module github.com/spectrocloud/CanvOS/internal + +go 1.22.0 + +require ( + github.com/sirupsen/logrus v1.9.3 + github.com/spf13/cobra v1.8.0 + github.com/twpayne/go-vfs/v5 v5.0.4 +) + +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/testify v1.9.0 // indirect + golang.org/x/sys v0.20.0 // indirect +) diff --git a/internal/go.sum b/internal/go.sum new file mode 100644 index 0000000..bd1bf5a --- /dev/null +++ b/internal/go.sum @@ -0,0 +1,28 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/twpayne/go-vfs/v5 v5.0.4 h1:/ne3h+rW7f5YOyOFguz+3ztfUwzOLR0Vts3y0mMAitg= +github.com/twpayne/go-vfs/v5 v5.0.4/go.mod h1:zTPFJUbgsEMFNSWnWQlLq9wh4AN83edZzx3VXbxrS1w= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/pkg/utils/fs.go b/internal/pkg/utils/fs.go new file mode 100644 index 0000000..388226e --- /dev/null +++ b/internal/pkg/utils/fs.go @@ -0,0 +1,43 @@ +package utils + +import ( + "os" + "path/filepath" + + "github.com/twpayne/go-vfs/v5" +) + +func CopyDir(path string, srcFS, dstFS vfs.FS) error { + if exists, err := Exists(dstFS, path); err != nil { + return err + } else if exists { + return nil + } + + // Check if parent of path exists + if err := CopyDir(filepath.Dir(path), srcFS, dstFS); err != nil { + return err + } + + // Get permission of source directory + srcInfo, err := srcFS.Stat(path) + if err != nil { + return err + } + // Create directory with same permissions as source + if err := vfs.MkdirAll(dstFS, path, srcInfo.Mode()); err != nil { + return err + } + return nil +} + +func Exists(fs vfs.FS, path string) (bool, error) { + _, err := fs.Stat(path) + if err == nil { + return true, nil + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} \ No newline at end of file diff --git a/keys.sh b/keys.sh new file mode 100755 index 0000000..91cef84 --- /dev/null +++ b/keys.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# Define the directory to be listed; default to secure-boot if none provided +directory="${1:-secure-boot}" + +# Print the root directory +echo "$(basename "$(realpath "$directory")")/" + +# Function to generate tree view and add comments to specific files +generate_tree() { + find "$directory" -mindepth 1 -print | sort | sed 's|[^/]*/| |g' | awk ' + { + # Replace leading spaces with a combination of pipes and dashes to simulate tree branches + gsub(/ /, "| ", $0); + sub(/\| $/, "`---", $0); + print; + }' +} + +# Function to add comments to specific files +function add_comment { + while read -r line; do + # Determine filename from the indented line + filename="${line##* }" # Extract the last part after space, which should be the file name + case "$filename" in + "PK.auth"*) + echo "$line <-- Platform Key" + ;; + "KEK.auth"*) + echo "$line <-- Key Exchange Key" + ;; + "db.auth"*) + echo "$line <-- Signature Database" + ;; + "dbx.auth"*) + echo "$line <-- Forbidden Signatures Database" + ;; + "PK.key"*) + echo "$line <-- Remove me from this directory and keep me safe" + ;; + "KEK.key"*) + echo "$line <-- Remove me from this directory and keep me safe" + ;; + "tpm2-pcr-private.pem"*) + echo "$line <-- Don't lose me! Without me you lose access to your encrypted disks" + ;; + *) + echo "$line" + ;; + esac + done +} + +# Generate the tree and pipe it to add comments +generate_tree | add_comment diff --git a/overlay/files-iso/boot/grub2/grub.cfg b/overlay/files-iso/boot/grub2/grub.cfg index f4a73da..9ed290b 100644 --- a/overlay/files-iso/boot/grub2/grub.cfg +++ b/overlay/files-iso/boot/grub2/grub.cfg @@ -4,14 +4,6 @@ set timeout=5 set timeout_style=menu set linux=linux set initrd=initrd -if [ "${grub_cpu}" = "x86_64" -o "${grub_cpu}" = "i386" -o "${grub_cpu}" = "arm64" ];then - if [ "${grub_platform}" = "efi" ]; then - if [ "${grub_cpu}" != "arm64" ]; then - set linux=linuxefi - set initrd=initrdefi - fi - fi -fi if [ "${grub_platform}" = "efi" ]; then echo "Please press 't' to show the boot menu on this console" fi diff --git a/rhel-core-images/Dockerfile.rhel8 b/rhel-core-images/Dockerfile.rhel8 index b0e1ad9..93e7279 100644 --- a/rhel-core-images/Dockerfile.rhel8 +++ b/rhel-core-images/Dockerfile.rhel8 @@ -56,7 +56,13 @@ RUN uuidgen > /etc/machine-id && dnf install -y \ kernel kernel-modules kernel-modules-extra \ rsync jq && dnf clean all -COPY --from=quay.io/kairos/framework:v2.4.5 / / + +COPY --from=quay.io/kairos/framework:v2.7.31 / / + +RUN sed -i 's/\bsource\b/./g' /system/oem/00_rootfs.yaml +RUN sed -i 's/\bsource\b/./g' /system/oem/09_openrc_services.yaml +RUN sed -i 's/\bsource\b/./g' /system/oem/50_recovery.yaml + RUN mkdir -p /run/lock RUN touch /usr/libexec/.keep diff --git a/rhel-fips/Dockerfile b/rhel-fips/Dockerfile index 55289f7..999dadd 100644 --- a/rhel-fips/Dockerfile +++ b/rhel-fips/Dockerfile @@ -1,11 +1,12 @@ ARG BASE_IMAGE=registry.access.redhat.com/ubi8/ubi-init:8.7-10 -ARG USERNAME -ARG PASSWORD FROM $BASE_IMAGE as base +ARG USERNAME +ARG PASSWORD + # Generate os-release file -FROM quay.io/kairos/osbuilder-tools:v0.7.11 as osbuilder +FROM quay.io/kairos/osbuilder-tools:v0.200.11 as osbuilder RUN zypper install -y gettext && zypper clean RUN mkdir /workspace COPY --from=base /etc/os-release /workspace/os-release @@ -83,7 +84,11 @@ RUN mkdir -p /run/lock && \ # Copy the os-release file to identify the OS COPY --from=osbuilder /workspace/os-release /etc/os-release -COPY --from=quay.io/kairos/framework:v2.4.5-fips / / +COPY --from=quay.io/kairos/framework:v2.7.31-fips / / + +RUN sed -i 's/\bsource\b/./g' /system/oem/00_rootfs.yaml +RUN sed -i 's/\bsource\b/./g' /system/oem/09_openrc_services.yaml +RUN sed -i 's/\bsource\b/./g' /system/oem/50_recovery.yaml COPY overlay/rhel8 / diff --git a/rhel-fips/build.sh b/rhel-fips/build.sh index 1ddcb76..f24fae4 100644 --- a/rhel-fips/build.sh +++ b/rhel-fips/build.sh @@ -1,14 +1,17 @@ #!/bin/bash -set -ex + +USERNAME=$1 +PASSWORD=$2 +BASE_IMAGE="${3:-rhel-byoi-fips}" # Build the container image -docker build -t rhel-byoi-fips . +docker build --build-arg USERNAME=$USERNAME --build-arg PASSWORD=$PASSWORD -t $BASE_IMAGE . docker run -v "$PWD"/build:/tmp/auroraboot \ -v /var/run/docker.sock:/var/run/docker.sock \ - --rm -ti quay.io/kairos/auroraboot \ - --set container_image=docker://rhel-byoi-fips \ + --rm quay.io/kairos/auroraboot \ + --set container_image=docker://$BASE_IMAGE \ --set "disable_http_server=true" \ --set "disable_netboot=true" \ --set "state_dir=/tmp/auroraboot" diff --git a/sb-private-ca/KEK_request.conf b/sb-private-ca/KEK_request.conf new file mode 100644 index 0000000..e3653d6 --- /dev/null +++ b/sb-private-ca/KEK_request.conf @@ -0,0 +1,18 @@ +[ req ] +prompt = no +string_mask = default +default_bits = 2048 +encrypt_key = no +distinguished_name = req_dn +req_extensions = req_ext + +[ req_dn ] +C = US +L = San Jose +OU = IT +CN = Spectro Cloud Example - KEK + +[ req_ext ] +basicConstraints = critical, CA:true +keyUsage = critical, digitalSignature, keyCertSign, cRLSign +subjectKeyIdentifier = hash \ No newline at end of file diff --git a/sb-private-ca/PK_request.conf b/sb-private-ca/PK_request.conf new file mode 100644 index 0000000..4611b28 --- /dev/null +++ b/sb-private-ca/PK_request.conf @@ -0,0 +1,20 @@ +[ req ] +prompt = no +string_mask = default +default_bits = 2048 +encrypt_key = no +distinguished_name = req_dn +req_extensions = req_ext +oid_section = OIDs + +[ req_dn ] +C = US +L = San Jose +OU = IT +CN = Spectro Cloud Example - PK + +[ req_ext ] +basicConstraints = critical, CA:false +keyUsage = critical, digitalSignature +extendedKeyUsage = codeSigning +subjectKeyIdentifier = hash \ No newline at end of file diff --git a/sb-private-ca/db_request.conf b/sb-private-ca/db_request.conf new file mode 100644 index 0000000..2665592 --- /dev/null +++ b/sb-private-ca/db_request.conf @@ -0,0 +1,18 @@ +[ req ] +prompt = no +string_mask = default +default_bits = 2048 +encrypt_key = no +distinguished_name = req_dn +req_extensions = req_ext + +[ req_dn ] +C = US +L = San Jose +OU = IT +CN = Spectro Cloud Example - db + +[ req_ext ] +basicConstraints = critical, CA:true +keyUsage = critical, digitalSignature, keyCertSign, cRLSign +subjectKeyIdentifier = hash \ No newline at end of file diff --git a/sb-private-ca/howto.md b/sb-private-ca/howto.md new file mode 100644 index 0000000..d943494 --- /dev/null +++ b/sb-private-ca/howto.md @@ -0,0 +1,71 @@ +INSTRUCTIONS FOR USING YOUR OWN CA FOR SECURE BOOT KEYS +------------------------------------------------------- + +1. First, create certificate requests for the PK, KEK and db certificates. Review the company information in the *.conf files, adjust `req_dn` section of each file to your liking, and then run: +``` +openssl req -new -config PK_request.conf -keyout PK.key -out CSR_PK.req +openssl req -new -config KEK_request.conf -keyout KEK.key -out CSR_KEK.req +openssl req -new -config db_request.conf -keyout db.key -out CSR_db.req +``` + +2. This results in 3 .req files. Use these to request the certificates from the corporate CA. +3. Retrieve the issued certificates from the corporate CA in base64-encoded form (PEM format). + +4. Save the retrieved certificate files as (note the case sensitivity): +PK.pem +KEK.pem +db.pem + +5. Run `./earthly.sh +secure-boot-dirs` to create the secure-boot directory structure in CanvOS. +6. Place the files in the following directory structure: +``` +CanvOS/ + secure-boot/ + private-keys/ + PK.key + KEK.key + db.key + public-keys/ + PK.pem + KEK.pem + db.pem +``` + +7. Create the Full Disk Encryption key for the TPM: +``` +openssl genrsa -out tpm2-pcr-private.pem 2048 +``` + +8. Place the resulting `tpm2-pcr-private.pem` file in `secure-boot/private-keys` + +9. Export the factory UEFI keys from your device by installing a regular Linux or Windows OS on the device. Then run the following commands to export the factory keys: + * Linux: + ``` + apt update && apt install -y efitools + efi-readvar -v KEK -o KEK + efi-readvar -v db -o db + efi-readvar -v dbx -o dbx + ``` + * Windows: + ``` + Get-SecureBootUEFI –Name KEK –OutputFilePath KEK + Get-SecureBootUEFI –Name db –OutputFilePath db + Get-SecureBootUEFI –Name dbx –OutputFilePath dbx + ``` + +10. Place the exported `KEK`, `db` and `dbx` files in the following directory structure: +``` +CanvOS/ + secure-boot/ + exported-keys/ + KEK + db + dbx +``` + +11. Ensure your `.arg` file contains `UKI_BRING_YOUR_OWN_KEYS=true` + +12. Run the "uki-genkey" function in CanvOS to generate the Secure Boot enrollment payload: +``` +./earthly.sh +uki-genkey +``` \ No newline at end of file diff --git a/slem/Dockerfile b/slem/Dockerfile index c19a09f..0de8df4 100644 --- a/slem/Dockerfile +++ b/slem/Dockerfile @@ -28,7 +28,12 @@ RUN mkdir -p /run/lock RUN mkdir -p /usr/libexec RUN touch /usr/libexec/.keep -COPY --from=quay.io/kairos/framework:v2.4.5 / / +COPY --from=quay.io/kairos/framework:v2.7.31 / / + +RUN sed -i 's/\bsource\b/./g' /system/oem/00_rootfs.yaml +RUN sed -i 's/\bsource\b/./g' /system/oem/09_openrc_services.yaml +RUN sed -i 's/\bsource\b/./g' /system/oem/50_recovery.yaml + # Remove file below to allow dracut to build initrd without dhcp-client RUN rm -rf /usr/lib/dracut/modules.d/35network-legacy diff --git a/ubuntu-fips/Dockerfile b/ubuntu-fips/Dockerfile index 67dae7e..b9feb0e 100644 --- a/ubuntu-fips/Dockerfile +++ b/ubuntu-fips/Dockerfile @@ -1,12 +1,12 @@ # Kairos framework packages for ubuntu fips -FROM quay.io/kairos/framework:v2.4.5-fips as kairos-fips +FROM quay.io/kairos/framework:v2.7.31-fips as kairos-fips # Base ubuntu image (focal) FROM ubuntu:focal as base # Generate os-release file -FROM quay.io/kairos/osbuilder-tools:v0.7.11 as osbuilder +FROM quay.io/kairos/osbuilder-tools:v0.200.11 as osbuilder RUN zypper install -y gettext && zypper clean RUN mkdir /workspace COPY --from=base /etc/os-release /workspace/os-release @@ -99,7 +99,6 @@ RUN apt-get install -y --no-install-recommends \ qemu-guest-agent \ rsync \ shared-mime-info \ - snapd \ snmpd \ squashfs-tools \ sudo \ @@ -119,6 +118,11 @@ RUN apt-get install -y --no-install-recommends \ # Copy the Kairos framework files. We use master builds here for fedora. See https://quay.io/repository/kairos/framework?tab=tags for a list COPY --from=kairos-fips / / + +RUN sed -i 's/\bsource\b/./g' /system/oem/00_rootfs.yaml +RUN sed -i 's/\bsource\b/./g' /system/oem/09_openrc_services.yaml +RUN sed -i 's/\bsource\b/./g' /system/oem/50_recovery.yaml + # Copy the os-release file to identify the OS COPY --from=osbuilder /workspace/os-release /etc/os-release diff --git a/ubuntu-fips/build.sh b/ubuntu-fips/build.sh index 47051fd..a7a48c0 100644 --- a/ubuntu-fips/build.sh +++ b/ubuntu-fips/build.sh @@ -1,2 +1,4 @@ -DOCKER_BUILDKIT=1 docker build . --secret id=pro-attach-config,src=pro-attach-config.yaml -t ubuntu-focal-fips -docker run -v "$PWD"/build:/tmp/auroraboot -v /var/run/docker.sock:/var/run/docker.sock --rm -ti quay.io/kairos/auroraboot --set container_image=docker://ubuntu-focal-fips --set "disable_http_server=true" --set "disable_netboot=true" --set "state_dir=/tmp/auroraboot" +BASE_IMAGE="${1:-ubuntu-focal-fips}" + +DOCKER_BUILDKIT=1 docker build . --secret id=pro-attach-config,src=pro-attach-config.yaml -t $BASE_IMAGE +docker run -v "$PWD"/build:/tmp/auroraboot -v /var/run/docker.sock:/var/run/docker.sock --rm quay.io/kairos/auroraboot --set container_image=docker://$BASE_IMAGE --set "disable_http_server=true" --set "disable_netboot=true" --set "state_dir=/tmp/auroraboot"